2009-12-10 5 views
3

일부 가정을 확인하고 해피 경로를 따르거나 불행한 경로를 따라 종료하는 방법이 있습니다. 나는 그것을 잘못 설계했거나 흐름 제어를 테스트하는 방법을 놓치고있다.유닛 테스팅 흐름 제어 패턴은 무엇입니까

if (this.officeInfo.OfficeClosed) 
{ 
    this.phoneCall.InformCallerThatOfficeIsClosedAndHangUp(); 
    return; 
} 
if (!this.operators.GetAllOperators().Any()) 
{ 
    this.phoneCall.InformCallerThatNoOneIsAvailableAndSendToVoicemail(); 
    return; 
} 
Call call=null; 
forach(var operator in this.operators.GetAllOperators()) 
{ 
    call = operator.Call(); 
    if(call!=null) {break;} 
} 

등등. 나는 내 의존성을 주입 시켰어. 나는 나의 mock moq'd를 얻었다. 나는 이것이 또는 그것이 불려지는지를 확신 할 수 있지만, "반환"이 어떻게되는지 테스트하는 방법을 모른다. 만약 TDD가 없으면 실패 할 때까지 줄을 쓰지 않는다는 의미입니다.

어떻게 테스트하나요? 아니면 그것을 더 테스트 할 수 있도록 작성하는 방법이 있습니까?

업데이트 : 몇 가지 대답은 결과 호출이 아닌 흐름 제어를 테스트해야한다고 말한 것입니다. 이 접근법에서 내가 가진 문제는 모든 테스트가 다른 테스트의 상태와 결과를 설정하고 테스트해야한다는 것입니다. 이것은 정말로 다루기 힘들고 부서지기 쉬운 것처럼 보입니다. 나는 첫 번째 if 절만을 테스트해서 두 번째 테스트를 단독으로 수행 할 수 없어야합니까? Method_WithParameter_DoesntInvokeMethod8IfMethod7IsTrueandMethod6IsTrueAndMethod5IsTrueAndMethod4IsTrueAndMethod3IsFalseAndMethod2IsTrueAndMethod1isAaaaccck()와 같이 시작되는 대수적으로 확장되는 테스트 집합이 정말로 필요합니까?

+0

아마도 메서드가 너무 길어질 수 있습니다. –

+0

"모든 테스트가 다른 테스트의 상태와 결과를 설정하고 테스트해야합니다"라는 것을 이해하지 못합니다. 각 테스트의 Te 길이는 코드 길이와 일치 할 수 있습니다. 'operator.Call'이 호출되었는지 확인하기 위해'officeInfo.OfficeClosed'가 false를 반환하고'operators.GetAllOperators(). Any()'가 true를 반환하는지 확인해야합니다. – ChrisW

+0

무슨 뜻입니까? 후속 테스트는 초기 코드 행이 실행되었거나 실행되지 않았 음을 설정 및 확인해야합니다. 이것은 루틴이 끝나는 지점을 테스트하는 방법을 찾을 수 없기 때문에 필요합니다. Assert.DidReturnAtLine (3)이있는 경우 (물론 끔찍한 테스트가 될 것입니다), 입력 상태가 그 지점을 넘어서는 문장에 흐름을 전달하지 않았 음을 확신 할 수 있습니다. – JoshRivers

답변

1

당신은 탄도와 전략 패턴을 사용할 수 있습니다. 단일 무효 메서드 DoTheRightThing() 및 인터페이스를 구현하는 3 개의 클래스 HandleOfficeIsClosed, HandleEveryoneIsBusy, HandleGiveFirstOperatorAvailable을 사용하여 IHandleCall 인터페이스가있는 선을 따라 가면서 뭔가가 발생합니다.

IHandleCall handleCall; 
if (this.officeInfo.OfficeClosed) 
{ 
    handleCall = new HandleOfficeIsClosed(); 
} 
else if other condition 
{ 
handleCall = new OtherImplementation(); 
} 
handleCall.DoTheRightThing(); 
return; 

당신이 당신의 방법에 여러 반환 지점을 제거 할 수 있습니다 그 방법 : 그리고 다음과 같은 코드가 있습니다. 이것은 매우 더러운 윤곽입니다. 그러나 기본적으로이 시점에서 if/else를 일부 팩토리에 추출해야합니다. 테스트해야하는 것은 클래스가 팩토리를 호출하고 handleCall.DoTheRightThing()이 다음과 같습니다. - (그리고 물론 공장은 올바른 전략을 반환합니다). 당신이 좋은 일 TDD를 사용하여 부착하는 경우

var operator = this.operators.FindFirst(); 
call = operator.Call(); 
3

정말 맞는 접근 방식이 맞는지 잘 모르겠습니다. 메서드가 예상 된 결과를 생성했는지 여부를 신경 써야합니다. 특정 메서드를 통해 컨트롤이 "흐른"방법 일 필요는 없습니다. 예를 들어 phoneCall.InformCallerThatOfficeIsClosedAndHangUp이 호출되면 일부 결과가 어딘가에 기록된다고 가정합니다. 따라서 단위 테스트에서 결과가 실제로 기록되었다고 주장 할 것입니다 (데이터베이스 레코드, 파일 등을 검사하여).

그렇다면 단위 테스트가 실제로 코드를 다루는 지 확인하는 것이 중요합니다. 이를 위해 NCover와 같은 도구를 사용하여 모든 코드를 평가할 수 있습니다. 그것은 당신의 단위 테스트에 의해 실행 된 라인을 정확히 알려주는 커버리지 리포트를 생성 할 것이고, 더 중요한 것은 아닌 것입니다.

4

프로그램의 출력을 테스트하고 싶습니다. 예를 들어, this.officeInfo.OfficeClosed 일 때 this.phoneCall.InformCallerThatOfficeIsClosedAndHangUp()이 호출되고 this.operators.GetAllOperators()과 같은 다른 메서드는 호출되지 않습니다.

나는 당신의 테스트가 모의 객체 (phoneCall, 등)에게 그들의 메소드가 호출되었는지를 묻거나, 메소드가 예기치 않게 호출되는 경우 예외를 던지게함으로써 이것을 수행한다고 생각한다.

프로그램 입력의 로그 파일 (예 : 'OfficeClosedtrue을 반환)을 출력하고 다음을 출력합니다. 테스트를 실행하고 테스트가 로그 파일을 생성하도록 한 다음 생성 된 로그 파일은 해당 테스트의 예상 로그 파일 내용과 일치합니다.

1

흐름 제어를 테스트하지 마십시오. 예상되는 동작을 테스트하십시오. 즉, 단위 테스트는 구현 세부 사항에 대해 신경 쓰지 않고 메소드의 동작이 메소드의 스펙과 일치한다는 것입니다. 따라서 Add(int x, int y)4의 결과를 x = 2, y = 2에 생성해야한다면 출력이 4 인 지 테스트하십시오. 그러나 결과는 Add이 어떻게 생성되는지 걱정하지 않아도됩니다.

단위 테스트는 구현 세부 정보 및 리팩토링에서 불변해야합니다. 그러나 유닛 테스트에서 구현 세부 사항을 테스트하는 경우 유닛 테스트를 중단하지 않고 리팩토링 할 수 없습니다.예를 들어 GetPrime(int k) 메서드를 구현하여 k 소수를 반환하면 GetPrime(10)29을 반환하는지 확인하지만 메서드 내부의 흐름 제어는 테스트하지 마십시오. Eratóstenes의 시브 (Sieve of Eratóstenes)를 사용하여 GetPrime을 구현하고 메서드 내부의 흐름 컨트롤을 테스트 한 다음 나중에 Atkin의 시브 (Sieve of Atkin)를 사용하는 리펙터를 테스트 한 경우 단위 테스트가 중단됩니다. 다시 말하지만, 중요한 것은 GetPrime(10)29을 반환한다는 것입니다.

1

: 어떤 경우

, 이미 어떤 연산자에 대해 가능한 보호 때문에, 당신에게 말을 단순화 할 수 그것은 TDD 당신의 드라이브 것을 의미한다 디자인 및 변경 방법을 찾고 있으므로 수 있습니다 테스트.

다음 중 하나를 수행 할 수 있습니다 1) 상태를 확인합니다 SUT 실행 또는 2 이후 SUT의 상태를 확인) 동작을 확인 :이의 방법 중 하나가 마음에 들지 않으면 테스트 기대

에 준수하여 모의 객체 호출을 확인 접근법은 코드를 리펙토링 할 시간입니다.

+0

나는 완전히 동의한다. 제 질문은 어떻게하면 게이트를 테스트 할 수있는 코드로 리팩토링합니까? – JoshRivers

0

Aaron FengK. Scott Allen으로 설명되는 패턴이 내 문제를 해결할 것이고 테스트 가능성에 대한 우려입니다. 내가 보는 유일한 문제는 모든 계산이 앞에서 수행되어야한다는 것입니다. 의사 결정 데이터 객체는 모든 조건문보다 먼저 채워 져야합니다. 지속성 스토리지로의 순차적 인 라운드 트립이 필요하지 않는 한 그렇게 좋습니다.