Методы модульного тестирования с локальным потоком - PullRequest
3 голосов
/ 03 декабря 2010

У меня есть метод, который выглядит следующим образом:

protected void OnBarcodeScan(BarcodeScannerEventArgs e)
{
    // We need to do this on a seperate thread so we don't block the main thread.
    ThreadStart starter = () => SendScanMessage(e, _scanDelegates);
    Thread scanThread = new Thread(starter);

    scanThread.Start();
}

Затем поток прерывается и выполняет некоторую логику (и в конечном итоге вызывает делегата в моем тесте).

Моя проблема в том, что мой модульный тест завершается раньше, чем завершается поток. Так что мой тест не пройден.

Я могу просто добавить System.Threading.Thread.Sleep(1000); и надеяться, что логика никогда не займет больше секунды (она не должна). Но это похоже на взлом.

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

Есть ли какой-нибудь классный способ снова найти этот поток и дождаться его в моем модульном тесте?

Примерно так:

[TestMethod]
[HostType("Moles")]
public void AddDelegateToScanner_ScanHappens_ScanDelegateIsCalled()
{
    // Arrange
    bool scanCalled = false;
    MCoreDLL.GetTopWindow = () => (new IntPtr(FauxHandle));

    // Act
    _scanner.AddDelegateToScanner(_formIdentity, ((evnt) => { scanCalled = true; }));
    _scanner.SendScan(new BarcodeScannerEventArgs("12345678910"));

    // This line is fake!
    System.Threading.Thread.CoolMethodToFindMyThread().Join();

    // Assert
    Assert.IsTrue(scanCalled);
}

Я, очевидно, составил метод CoolMethodToFindMyThread . Но есть ли какие-то причины для этого?

Ответы [ 2 ]

8 голосов
/ 03 декабря 2010

Так что, если я понимаю, как это работает, то вы регистрируете делегатов, которые вызываются во втором потоке, верно?В этом случае вы можете использовать синхронизацию потоков в своем тесте и вызываемом делегате.Я делаю подобные вещи в своих модульных тестах все время.

Примерно так:

[TestMethod]
[HostType("Moles")]
public void AddDelegateToScanner_ScanHappens_ScanDelegateIsCalled()
{
    // Arrange
    var scanCalledEvent = new ManualResetEvent(false);
    MCoreDLL.GetTopWindow = () => (new IntPtr(FauxHandle));

    // Act
    _scanner.AddDelegateToScanner(_formIdentity, ((evnt) => { scanCalledEvent.Set(); }));
    _scanner.SendScan(new BarcodeScannerEventArgs("12345678910"));

    // Wait for event to fire
    bool scanCalledInTime = scanCalledEvent.WaitOne(SOME_TIMEOUT_IN_MILLISECONDS);

    // Assert
    Assert.IsTrue(scanCalledInTime);
}

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

(ПРЕДУПРЕЖДЕНИЕ: у меня может быть возвращаемое значение в обратном направлении - я не помню, если у меня в голове,Значение true означает, что событие установлено, или если значение true означает, что тайм-аут истек. Проверьте документы.)

Существует несколько примитивов синхронизации, которые можно использовать здесь, один из которых зависит от того, что вы хотите сделать.ManualResetEvent обычно работает довольно хорошо для меня.

1 голос
/ 03 декабря 2010

Есть еще один способ сделать что-то:

Во-первых, имейте AutoResetEvent (или ManualResetEvent, если хотите) в вашем тестовом классе.

В вашем методе испытаний:

//set up stuff

testEvent.WaitOne();

//ensure everything works

В вашем обратном вызове

testEvent.Set();

Ваш метод тестирования остановится, пока не будет вызван обратный вызов.

Предположительно, вам понадобится какое-то время ожидания для этого ожидающего вызова.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...