엔코더를 어떻게 단위 테스트합니까?


9

나는 이와 같은 것을 가지고있다 :

public byte[] EncodeMyObject(MyObject obj)

나는 다음과 같이 단위 테스트를 해왔다.

byte[] expectedResults = new byte[3]{ 0x01, 0x02, 0xFF };
Assert.IsEqual(expectedResults, EncodeMyObject(myObject));

편집 : 내가 제안한 두 가지 방법은 다음과 같습니다.

1) 위의 예와 같이 하드 코딩 된 예상 값 사용.

2) 디코더를 사용하여 인코딩 된 바이트 배열을 디코딩하고 입력 / 출력 객체를 비교합니다.

방법 1에서 볼 수있는 문제는 그것이 취성이며 많은 하드 코딩 된 값이 필요하다는 것입니다.

방법 2의 문제점은 인코더 테스트가 올바르게 작동하는 디코더에 달려 있다는 것입니다. 인코더 / 디코더가 같은 위치에서 동일하게 고장난 경우 테스트에서 오 탐지가 발생할 수 있습니다.

이러한 유형의 방법을 테스트하는 유일한 방법 일 수 있습니다. 그 경우라면 괜찮습니다. 이러한 유형의 테스트를위한 더 나은 전략 이 있는지 질문합니다 . 작업중인 특정 인코더의 내부를 공개 할 수 없습니다. 나는 일반적으로 이러한 유형의 문제를 해결하는 방법을 묻고 있으며 내부가 중요하다고 생각하지 않습니다. 주어진 입력 객체가 항상 동일한 출력 바이트 배열을 생성한다고 가정합니다.


4
어떻게 myObject에서 이동 myObject{ 0x01, 0x02, 0xFF }? 해당 알고리즘을 분석하고 테스트 할 수 있습니까? 내가 묻는 이유는 현재 하나의 마법이 다른 마법을 생성한다는 것을 입증하는 테스트를받은 것처럼 보입니다. 하나의 입력이 하나의 출력을 생성한다는 유일한 확신입니다. 알고리즘을 세분화 할 수 있으면 알고리즘에 대한 신뢰도를 높이고 마법의 입력 및 출력에 덜 의존 할 수 있습니다.
Anthony Pegram

3
@Codism 인코더와 디코더가 같은 장소에서 고장난 경우 어떻게해야합니까?
ConditionRacer

2
테스트는 정의에 따라 무언가를하고 예상 결과를 얻었는지 확인하는 것입니다. 물론 모든 코드를 연습하고 엣지 케이스와 기타 이상한 점을 다루기 위해 충분한 테스트를 수행해야합니다.
Blrfl

1
@ Justin984, 이제 우리는 더 깊이 가고 있습니다. 나는 그 개인 내부를 Encoder API의 멤버로 노출시키지 않을 것입니다. 인코더에서 완전히 제거했습니다. 또는 인코더는 다른 곳으로 종속성 을 위임합니다 . 테스트 할 수없는 괴물 방법이나 많은 도우미 클래스 사이의 싸움이라면 매번 도우미 클래스를 선택합니다. 그러나 다시, 나는이 시점에서 코드에 대한 정보를 알 수없는 추론을하고 있습니다. 그러나 테스트에 대한 자신감을 얻으려면 더 적은 방법으로 더 적은 수의 작업을 수행하는 것이 좋습니다.
Anthony Pegram

1
@ Justin984 사양이 변경되면 테스트에서 예상 출력을 변경하면 실패합니다. 그런 다음 인코더 논리를 전달하도록 변경합니다. TDD가 작동하는 방식을 정확하게 보았을 때 TDD가 실패해야합니다. 이것이 어떻게 부서지기 쉬운 지 모르겠습니다.
Daniel Kaplan

답변:


1

당신은 거기에 약간의 불쾌한 상황에 처해 있습니다. 인코딩하는 정적 형식이 있다면 첫 번째 방법이 될 것입니다. 그것이 자신의 형식 일 뿐이고 두 번째 방법보다 해독 할 사람이 없다면 갈 길입니다. 그러나 당신은 그 두 가지 범주 중 하나에 실제로 적합하지 않습니다.

내가 한 것은 추상화 수준으로 일을 세분화하는 것입니다.

그래서 나는 비트 레벨에서 무언가로 시작할 것입니다.

bitWriter = new BitWriter();
bitWriter.writeInt(42, bits = 7);
assertEqual( bitWriter.data(), {0x42} )

따라서 비트 라이터는 int와 같이 가장 원시적 인 유형의 필드를 작성하는 방법을 알고 있습니다.

더 복잡한 유형은 다음을 사용하여 구현하고 테스트합니다.

bitWriter = new BitWriter();
writeDate(bitWriter, new Datetime(2001, 10, 4));

bitWriter2 = new BitWriter();
bitWriter2.writeInt(2001, 12)
bitWriter2.writeInt(10, 4)
bitWriter2.writeInt(4, 6)

assertEquals(bitWriter.data(), bitWriter2.data() )

이렇게하면 실제 비트가 어떻게 패킹되는지에 대한 지식이 없어집니다. 그것은 이전 테스트에서 테스트 되었으며이 테스트에서는 작동한다고 가정합니다.

다음 단계의 추상화에서 우리는

bitWriter = new BitWriter();
encodeObject(bitWriter, myObject);


bitWriter2 = new BitWriter();
bitWriter2.writeInt(42, 32)
writeDate(bitWriter2, new Datetime(2001, 10, 4));
writeVarString(bitWriter2, "alphanumeric");

assertEquals(bitWriter.data(), bitWriter2.data() )

따라서 우리는 varstring 또는 날짜 또는 숫자가 실제로 어떻게 인코딩되는지에 대한 지식을 포함시키지 않습니다. 이 테스트에서는 encodeObject에 의해 생성 된 인코딩에만 관심이 있습니다.

최종 결과는 날짜 형식이 변경되면 실제로 날짜와 관련된 테스트를 수정해야하지만 다른 모든 코드 및 테스트는 날짜가 실제로 인코딩되는 방식과 관련이 없으며 코드를 업데이트 한 후에는 코드를 업데이트하는 것입니다 그 모든 시험은 잘 통과 할 것입니다.


나는 이것을 좋아한다. 나는 이것이 다른 논평가들이 그것을 작은 조각으로 나누는 것에 대해 말한 것 같아요. 사양이 변경 될 때 문제를 완전히 피할 수는 없지만 더 좋아집니다.
ConditionRacer

6

다릅니다. 모든 구현이 정확히 동일한 출력을 생성 해야하는 인코딩이 완전히 고정 된 경우 예제 입력이 정확히 예상 출력에 매핑되는지 확인하는 것 이외의 다른 것을 확인하는 것은 의미가 없습니다. 그것은 가장 명백한 시험이며 아마도 가장 쉬운 시험일 것입니다.

MPEG 표준에서와 같이 대체 출력이있는 빈 공간이있는 경우 (예 : 입력에 적용 할 수있는 특정 연산자가 있지만 인코딩 노력과 출력 품질 또는 저장 공간을 자유롭게 교환 할 수 있음), 출력에 대한 디코딩 전략을 정의하고 입력과 동일한 지 또는 인코딩이 손실 된 경우 원래 입력에 합리적으로 가까운 지 확인하십시오. 프로그래밍하기는 어렵지만 향후 인코더의 개선 사항을 방지 할 수 있습니다.


2
디코더를 사용하고 값을 비교한다고 가정하십시오. 인코더와 디코더가 모두 같은 장소에서 고장난 경우 어떻게해야합니까? 엔코더가 잘못 인코딩되고 디코더가 잘못 디코딩되지만 프로세스가 두 번 잘못 수행 되었기 때문에 입 / 출력 객체는 정확합니다.
ConditionRacer

Justin984 다음 소위 "테스트 벡터 ', 알고 입력 / 출력 쌍을 사용 @ 당신은 인코더 및 디코더 테스트를 정확하게 사용할 수있는
래칫 괴물

@ratchet freak 그것은 예상 값으로 테스트를 다시 시작합니다. 어느 것이 좋으며, 현재 내가하고있는 일이지만 약간 부서지기 때문에 더 좋은 방법이 있는지 찾고 싶었습니다.
ConditionRacer

1
표준을주의 깊게 읽고 모든 규칙에 대해 테스트 사례를 작성하는 것 외에도 인코더와 디코더에 동일한 버그가 포함되는 것을 피할 수있는 방법은 거의 없습니다. 예를 들어, "ABC"가 "xyz"로 변환되어야하지만 인코더가이를 알지 못하고 디코더가 "xyz"를 이해하지 못하는 경우를 가정 해 봅시다. 프로그래머가 해당 규칙을 알지 못했기 때문에 수작업으로 만들어진 테스트 케이스에는 "ABC"시퀀스가 포함되어 있지 않으며, 임의 문자열 인코딩 / 디코딩 테스트는 인코더와 디코더가 모두 문제를 무시하기 때문에 잘못 전달됩니다.
user281377

1
지식이 없어서 직접 작성한 디코더와 인코더 모두에 영향을 미치는 버그를 포착하려면 다른 공급 업체로부터 인코더 출력을 얻으려고 노력하십시오. 또한 타사 디코더에서 인코더의 출력을 테스트하십시오. 그 주위에 대안이 없습니다.
rwong

3

테스트가 encode(decode(coded_value)) == coded_valuedecode(encode(value)) == value. 원하는 경우 테스트에 무작위로 입력 할 수 있습니다.

인코더와 디코더가 모두 무료로 손상 될 가능성이 있지만 인코딩 표준에 대한 개념적인 오해가없는 한 그럴 것 같지 않습니다. 인코더와 디코더에 대해 하드 코딩 된 테스트를 수행하면 (이미 수행중인 것처럼)이를 방지해야합니다.

작동하는 것으로 알려진 다른 구현에 액세스 할 수 있다면 단위 테스트에서 사용하는 것이 불가능할지라도 구현이 훌륭하다는 확신을 얻기 위해 적어도 그것을 사용할 수 있습니다.


보완적인 엔코더 / 디코더 오류가 일반적 일 가능성은 낮습니다. 필자의 경우 엔코더 / 디코더 클래스의 코드는 데이터베이스의 규칙에 따라 다른 도구로 생성됩니다. 따라서 보완 오류가 가끔 발생합니다.
ConditionRacer

'무료 오류'는 어떻게 발생합니까? 이는 인코딩 된 형식에 대한 외부 사양이 있으므로 외부 디코더가 있음을 의미합니다.
kevin cline

외부 단어의 사용을 이해하지 못합니다. 그러나 데이터 인코딩 방법과 디코더에 대한 사양이 있습니다. 무료 오류는 인코더와 디코더가 상호 보완적인 방식으로 작동하지만 사양에서 벗어난 방식입니다. 원래 질문의 의견에 예가 있습니다.
ConditionRacer

인코더가 ROT13을 구현하려고했지만 실수로 ROT14를 수행했지만 디코더도 그렇게 한 경우, decode (encode ( 'a')) == 'a'이지만 인코더는 여전히 손상되었습니다. 그것보다 훨씬 더 복잡한 것들에 대해서는 아마도 그런 종류의 일이 일어날 가능성은 적지 만 이론적으로는 일어날 수 있습니다.
Michael Shaw

@MichaelShaw 한 가지 퀴즈로 ROT13의 인코더와 디코더는 동일합니다. ROT13은 자체 역수입니다. 실수로 ROT14를 구현 한 경우 decode(encode(char))같지 않습니다 char(같음 char+2).
Tom Marthenal

2

요구 사항을 테스트합니다 .

요구 사항이 '디코딩 될 때 동등한 객체를 생성하는 바이트 스트림으로 인코딩'인 경우 디코딩하여 인코더를 테스트하십시오. 인코더와 디코더를 모두 쓰고 있다면 함께 테스트하십시오. "일치하는 오류"를 가질 수 없습니다. 그들이 함께 작동하면 테스트에 합격합니다.

데이터 스트림에 대한 다른 요구 사항이 있으면 인코딩 된 데이터를 검사하여 테스트해야합니다.

인코딩 된 형식이 사전 정의 된 경우 예상 한 결과와 비교하여 인코딩 된 데이터를 확인하거나 확인을 위해 신뢰할 수있는 참조 디코더를 확보해야합니다. 참조 디코더를 사용하면 형식 사양을 잘못 해석했을 가능성이 없습니다.


1

사용중인 테스트 프레임 워크 및 패러다임에 따라 여전히 말한 것처럼 Arrange Act Assert 패턴을 사용할 수 있습니다.

[TestMethod]
public void EncodeMyObject_ForValidInputs_Encodes()
{
    //Arrange object under test
    MyEncoder encoderUnderTest = new MyEncoder();
    MyObject validObject = new MyOjbect();
    //arrange object for condition under test

    //Act
    byte[] actual = encoderUnderTest.EncodeMyObject(myObject);

    //Assert
    byte[] expected = new byte[3]{ 0x01, 0x02, 0xFF };
    Assert.IsEqual(expected, actual);
}

EncodeMyObject()이 패턴에 대한 요구 사항을 알고 이를 사용 expected하여 디코더와 마찬가지로 각각의 기준을 정렬하고에 대한 예상 결과를 하드 코딩하여 유효 및 유효하지 않은 기준을 테스트 할 수 있습니다 .

예상되는 코드는 하드 코딩되어 있기 때문에 대규모로 변경하면 깨지기 쉽습니다.

매개 변수 구동 ( Pex 살펴보기 ) 으로 자동화 하거나 DDD 또는 BDD를 수행하는 경우 gerkin / cucumber 살펴보십시오 .


1

중요한 것이 무엇인지 결정하게됩니다.

왕복 여행에서 객체가 생존하고 정확한 와이어 형식이 실제로 중요하지 않은 것이 중요합니까? 아니면 정확한 와이어 형식이 인코더 및 디코더 기능의 중요한 부분입니까?

전자의 경우, 왕복에서 개체가 생존하는지 확인하십시오. 인코더와 디코더가 모두 정확히 보완적인 방식으로 고장난 경우 실제로 신경 쓰지 않아도됩니다.

후자의 경우 와이어 입력이 주어진 입력에 대해 예상 한 것과 같은지 테스트해야합니다. 이는 형식을 직접 테스트하거나 참조 구현을 사용하는 것을 의미합니다. 그러나 기본 사항을 테스트 한 후에는 추가 왕복 테스트를 통해 가치를 얻을 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.