2010-11-18 5 views
1

현재 TDD를 사용하여 클래스를 만들고 있습니다. 클래스는 특정 윈도우가 활성화 될 때까지 기다린 다음 몇 가지 메소드를 실행하는 작업을 담당합니다.Rhino Mock으로 블로킹 호출 조롱하기

내가 원하는 동작이 실제로 AutoIt의 단일 메서드이기 때문에 AutoIt COM 라이브러리 (AutoIt에 대한 자세한 내용은 here을 참조하십시오)를 사용하고 있습니다.

코드는 다음과 같이 거의이다

public class WindowMonitor 
{ 
    private readonly IAutoItX3 _autoItLib; 

    public WindowMonitor(IAutoItX3 autoItLib) 
    { 
     _autoItLib = autoItLib; 
    } 


    public void Run() // indefinitely 
    { 
     while(true) 
     { 
      _autoItLib.WinWaitActive("Open File", "", 0); 
      // Do stuff now that the window named "Open File" is finally active. 
     } 
    } 
} 

당신은 AutoIt이 COM 라이브러리 내가 (NUnit과와 코뿔소 모의 객체를 사용) 조롱 수있는 인터페이스 느릅 나무 구현 볼 수 있듯이 :

[TestFixture] 
public class When_running_the_monitor 
{ 
    WindowMonitor subject; 
    IAutoItX3 mockAutoItLibrary; 
    AutoResetEvent continueWinWaitActive; 
    AutoResetEvent winWaitActiveIsCalled; 


[SetUp] 
public void Setup() 
    { 
    // Arrange 
    mockAutoItLibrary = MockRepository.GenerateStub<IAutoItX3>(); 
    mockAutoItLib.Stub(m => m.WinWaitActive("", "", 0)) 
       .IgnoreArguments() 
       .Do((Func<string, string, int, int>) ((a, b, c) => 
       { 
        winWaitActiveIsCalled.Set(); 
        continueWinWaitActive.WaitOne(); 
        return 1; 
       })); 

    subject = new Subject(mockAutoItLibrary) 

    // Act 
    new Thread(new ThreadStart(subject.Run)).Start(); 
    winWaitActiveIsCalled.WaitOne(); 
    } 

    // Assert 

    [Test] 
    [Timeout(1000)] 
    public void should_call_winWaitActive() 
    { 
     mockAutoItLib.AssertWasCalled(m => m.WinWaitActive("Bestand selecteren", "", 0)); 
    } 

    [Test] 
    [Timeout(1000)] 
    public void ensure_that_nothing_is_done_while_window_is_not_active_yet() 
    { 
     // When you do an "AssertWasCalled" for the actions when the window becomes active, put an equivalent "AssertWasNotCalled" here. 

    } 

을 }

문제는 첫 번째 테스트가 시간 초과를 유지한다는 것입니다. 필자는 이미 스텁 "WinWaitActive"가 호출 될 때 (의도 된대로 별도의 스레드에서) 차단하고, 그 후에 "AssertWasCalled"가 호출되면 실행이 반환되지 않는다는 것을 알았습니다.

진행 방법을 잃어 버렸고 차단 호출을 조롱하는 예를 찾을 수 없었습니다. 그래서 결론에

:

시험 시간 제한을하지 않고 차단 호출을 조롱하는 방법이 있나요?

(추신 : 여기서는 가능하지 않을 수 있으므로 디자인을 변경하는 데 덜 관심이 있습니다 (예 : "차단 호출을 사용하지 마십시오").하지만 훨씬 어려운 경우가 있습니다. 디자인을 변경하려면 좀 더 일반적인 솔루션에 관심이 있습니다. 그러나 차단 호출을 모의하기가 단순히 불가능한 경우 해당 제안은 더 환영합니다!)

+2

실제로 테스트하고 싶은 것은 무엇입니까? 왜 모의를 차단해야합니까? 테스트에 멀티 스레딩이 필요한 이유는 무엇입니까? –

답변

3

이 문제를 이해하는 경우 확실하지 않습니다.

코드가 모의 메소드 (WinWaitActive)를 호출하고 있습니다. 물론 호출이 반환되기 전에 진행할 수 없습니다. 이것은 프로그래밍 언어의 성격을 띠기 때문에 테스트 할 필요가 없습니다.

따라서 WinWaitActive이 호출되면 테스트가 완료됩니다. 어떤 것보다 먼저 WinWaitActive이 호출되는지 테스트 할 수는 있지만, 이것은 오래된 스타일의 rhino mocks 구문이 필요하고 일반적으로 수행 할 가치가없는 순서화 된 기대를 필요로합니다.

mockAutoItLibrary = MockRepository.GenerateStub<IAutoItX3>(); 

    subject = new Subject(mockAutoItLibrary) 
    subject.Run() 

    mockAutoItLib.AssertWasCalled(m => m.WinWaitActive("Open File", "", 0)); 

다른 방법으로는 메소드를 호출하지 않으므로 테스트 할 다른 것이 없습니다.

편집 : 출구 무한 루프

당신은 그것이 모의 객체에서 예외를 발생하여 무한 루프를 종료 할 수 있습니다. 이것은별로 좋지는 않지만 단위 테스트에서이 모든 멀티 스레딩 구성 요소를 사용하지 않아도됩니다.

mockAutoItLibrary = MockRepository.GenerateStub<IAutoItX3>(); 

    // make loop throw an exception on second call 
    // to exit the infinite loop 
    mockAutoItLib 
    .Stub(m => m.WinWaitActive(
     Arg<string>.Is.Anything, 
     Arg<string>.Is.Anything, 
     Arg<int>.Is.Anything)); 
    .Repeat.Once(); 

    mockAutoItLib 
    .Stub(m => m.WinWaitActive(
     Arg<string>.Is.Anything, 
     Arg<string>.Is.Anything, 
     Arg<int>.Is.Anything)); 
    .Throw(new StopInfiniteLoopException()); 

    subject = new Subject(mockAutoItLibrary) 
    try 
    { 
    subject.Run() 
    } 
    catch(StopInfiniteLoopException) 
    {} // expected exception thrown by mock 

    mockAutoItLib.AssertWasCalled(m => m.WinWaitActive("Open File", "", 0)); 
+0

mock에 대한 호출을 차단하려는 이유는 그것이 모퉁이에 있음을 나타냅니다. 내가 그것을 막지 않으면 Run 메서드의 모든 문장을 반복적으로 반복하여 반복 할 것입니다. 이것은 실제 응용 프로그램의 의도 된 동작이지만 단위 테스트에서 아플 수 있습니다. – dvdvorle

+0

이제 문제를 이해합니다. 내 대답에 섹션을 추가했습니다. –

+0

좋은 답변, 어쨌든 나는 멀티 쓰레딩에 만족하지 못했습니다. Run() 메소드에서 catch하는 예외가있을 경우에는 불가능합니다. 그러나 그것이 여기의 경우가 아니기 때문에, 이것은 내가 필요한 것입니다. 감사! – dvdvorle

1

테스트에는 조롱 된 메서드에 대한 호출 만 포함됩니다. 따라서 실제 코드 대신 모의 객체 만 테스트하므로 이상한 일입니다. 문제를 이해하기 위해서는 좀 더 많은 내용이 필요합니다.

AutoResetEvent 대신 Thread.Sleep()을 사용하십시오. 차단 창 활성 확인을 수행하는 COM 개체를 조롱하고 있으므로 동작을 모방 한 다음 실제로 창이 활성 상태인지 확인하십시오. 프로그래밍 방식으로 활성화합니다. 어떻게 블록 테스트에서 중요하지 않아야 만 당신은 상당한 시간 동안 블록.

코드에서 winWaitActiveIsCancelled 및 continueWinWaitActive가 어떻게 기여하는지 명확하지 않지만 WinWaitActive mock에서 제외해야한다고 생각합니다. 그것들을 Thread.Sleep(500)으로 교체하십시오.

+0

AutoResetEvents를 사용하여 스레드를 동기화합니다. 의도 된 동작은 SetUp에서 winWaitActiveCalled.WaitOne()을 호출하면 실행 스레드의 실행이 winWaitActive 호출임을 확인하는 것입니다. – dvdvorle

관련 문제