2010-06-08 6 views
8

내 응용 프로그램의 "접착제"레이어에 대한 단위 테스트를 작성 중이며 사용자가 조기에 작업을 취소 할 수있는 비동기 메서드에 대한 결정적 테스트를 만드는 데 어려움이 있습니다.장기 실행 유닛 테스트에서 취소 조정 작업

특히, 몇 가지 비동기 메소드에는 호출 취소에 반응하고 객체가 완료되기 전에 적절한 상태에 있음을 보장하는 코드가 있습니다. 이 코드 경로가 테스트에 의해 보장되는지 확인하고 싶습니다. 다음과 같이

이 시나리오에서 전형적인 비동기 방식을 예시 일부 C#을 의사 코드는 다음과 같습니다

public void FooAsync(CancellationToken token, Action<FooCompletedEventArgs> callback) 
{ 
    if (token.IsCancellationRequested) DoSomeCleanup0(); 

    // Call the four helper methods, checking for cancellations in between each 
    Exception encounteredException; 
    try 
    { 
     MyDependency.DoExpensiveStuff1(); 
     if (token.IsCancellationRequested) DoSomeCleanup1(); 

     MyDependency.DoExpensiveStuff2(); 
     if (token.IsCancellationRequested) DoSomeCleanup2(); 

     MyDependency.DoExpensiveStuff3(); 
     if (token.IsCancellationRequested) DoSomeCleanup3(); 

     MyDependency.DoExpensiveStuff4(); 
     if (token.IsCancellationRequested) DoSomeCleanup4(); 

    } 
    catch (Exception e) 
    { 
     encounteredException = e; 
    } 

    if (!token.IsCancellationRequested) 
    { 
     var args = new FooCompletedEventArgs(a bunch of params); 
     callback(args); 
    } 
} 

내가 접착제 층에 의해 포장되는 기본 MyDependency 작업을 조롱 지금까지 가지고 올 포함 한 솔루션 , 각자 임의의 시간 동안 잠을 자도록 강요한다. 그런 다음 비동기 메서드를 호출하고 비동기 요청을 취소하기 전에 수십 밀리 초 동안 내 장치 테스트에 절전 상태가 될 것을 말합니다. (예를 들어 코뿔소 모의 객체를 사용하여)이 같은

뭔가 : 단위 테스트에 Thread.sleep를 사용하여 구역질이 날 수 있다는 사실을 제외하고

[TestMethod] 
public void FooAsyncTest_CancelAfter2() 
{ 
    // arrange 
    var myDependency = MockRepository.GenerateStub<IMyDependency>(); 

    // Set these stubs up to take a little bit of time each so we can orcestrate the cancels 
    myDependency.Stub(x => x.DoExpensiveStuff1()).WhenCalled(x => Thread.Sleep(100)); 
    myDependency.Stub(x => x.DoExpensiveStuff2()).WhenCalled(x => Thread.Sleep(100)); 
    myDependency.Stub(x => x.DoExpensiveStuff3()).WhenCalled(x => Thread.Sleep(100)); 
    myDependency.Stub(x => x.DoExpensiveStuff4()).WhenCalled(x => Thread.Sleep(100)); 

    // act 
    var target = new FooClass(myDependency); 

    CancellationTokenSource cts = new CancellationTokenSource(); 
    bool wasCancelled = false; 

    target.FooAsync(
     cts.Token, 
     args => 
     { 
     wasCancelled = args.IsCancelled; 
     // Some other code to manipulate FooCompletedEventArgs 
     }); 

    // sleep long enough for two operations to complete, then cancel 
    Thread.Sleep(250); 
    cts.Cancel(); 

    // Some code to ensure the async call completes goes here 

    //assert 
    Assert.IsTrue(wasCancelled); 
    // Other assertions to validate state of target go here 
} 

는 더 큰 문제는 가끔 이런 테스트가 실패한다는 것입니다 중요한로드가 발생하는 경우 서버를 빌드하십시오. 비동기 호출이 너무 많이 발생하고 취소가 너무 늦습니다.

누구나 이와 같이 장기간 작동하는 단위 테스트 논리를보다 안정적으로 제공 할 수 있습니까? 어떤 아이디어라도 감사 할 것입니다.

답변

5

동기식으로 비동기 동작을 "시뮬레이트"하기 위해 모의 객체를 사용하려고합니다.

myDependency.Stub(x => x.DoExpensiveStuff1()); 
myDependency.Stub(x => x.DoExpensiveStuff2()); 
myDependency.Stub(x => x.DoExpensiveStuff3()).WhenCalled(x => cts.Cancel()); 
myDependency.Stub(x => x.DoExpensiveStuff4()); 

코드의 관점에서이는 것처럼 보이는 것 대신

myDependency.Stub(x => x.DoExpensiveStuff1()).WhenCalled(x => Thread.Sleep(100)); 

를 사용하여 다음 밀리 초 단위의 어떤 수에서 취소 플래그를 설정, 나는 그냥 콜백의 일부로 설정합니다 통화 중 취소가 발생했습니다.

+0

이것은 매우 유망 해 보입니다. 나는 오늘 오후에 그것을 시도 할 것이고, 뒤로보고 할 것이다. –

+0

그것은 아름답게 작동했습니다. 당신의 도움을 주셔서 감사합니다! –

1

각각의 장기 실행 작업은 실행을 시작할 때 이벤트를 시작해야합니다.

단위 테스트에서이 이벤트를 연결합니다. 이것은 이벤트가 미래에 유용 할 수있는 잠재력을 가진 결정 론적 결과를 제공합니다.