Дразнящий блокирующий звонок с Rhino Mocks - PullRequest
1 голос
/ 18 ноября 2010

Я сейчас строю класс, используя TDD.Класс отвечает за ожидание того, чтобы конкретное окно стало активным, и затем запускает какой-то метод.

Я использую библиотеку AutoIt COM (для получения дополнительной информации об AutoIt смотрите здесь ), так какповедение, которое я хочу, на самом деле является единственным методом в AutoIt.

Код выглядит примерно так:

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 реализует интерфейс, который я могуmock (с использованием NUnit и Rhino Mocks):

[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», выполнение никогда не возвращается.

I 'Я в растерянности, как действовать, и я не смог найти никаких примеров макетирования блокирующего вызова.

Итак, в заключение:

Есть ли способ высмеять блокирующий вызов, не делаятайм-аут тестов?

(PS Меня меньше интересует изменение дизайна (т. е. «Не используйте блокирующий вызов»), поскольку здесь можно сделать это, но я уверен, что естьв тех случаях, когда изменить дизайн намного сложнее, и меня интересует более общее решение, но если просто невозможно высмеять блокировку вызовов, подобные предложения приветствуются!)

Ответы [ 2 ]

3 голосов
/ 18 ноября 2010

Не уверен, что понимаю проблему.

Ваш код просто вызывает метод на макете (WinWaitActive). Конечно, это не может продолжаться, пока звонок не вернется. Это характерно для языка программирования, и вам ничего не нужно тестировать.

Итак, если вы проверяете, что WinWaitActive вызывается, ваш тест завершен. Вы можете проверить, вызывается ли WinWaitActive раньше, чем что-либо еще, но для этого требуются упорядоченные ожидания, которые требуют синтаксиса насмешек в старом стиле и, как правило, этого не стоит делать.

   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));
1 голос
/ 18 ноября 2010

Ваш тест содержит только вызов смоделированного метода.Поэтому он проверяет только ваш макет вместо любого реального кода, что является странной вещью.Нам может понадобиться немного больше контекста, чтобы понять проблему.

Используйте Thread.Sleep() вместо AutoResetEvent s: так как вы дразните COM-объект, который выполняет блокировку проверки активного окна, вы можете просто подождать некоторое время, чтобы имитировать поведение, а затем убедиться, чтоокно действительно активно, делая его активным программно. То, как вы блокируете, не должно быть важным в тесте, только , что вы блокируете в течение значительного времени.

Хотя из вашего кода не ясно, как winWaitActiveIsCancelled и continueWinWaitActiveспособствовать, я подозреваю, что они должны быть исключены из макета WinWaitActive.Замените их на Thread.Sleep(500).

...