Самый надежный подход к модульному тестированию, используемый как часть принятия сборки, когда класс содержит многопоточность

Рассмотрим этот модульный тест:

    [TestMethod]
    public void QueueManager_OnNextItem()
    {
        bool called = false;
        var queue = new QueueManager<int>(
                        new Moq.Mock<ILogger>().Object, 
                        new Moq.Mock<IItemQueueManagerPerformanceTracker>().Object);

        queue.OnNextItem += i => { called = true; };

        queue.Add(1);
        queue.Start();

        //need to RELIABLY wait here

        Assert.IsTrue(called);
    }

QueueManager имеет внутренний рабочий поток (Start() запускает его), который удаляет элемент из очереди и вызывает OnNextItem.

В этом тесте он всегда всегда терпит неудачу, так как Assert... вызывается до переключения контекста, чтобы разрешить выполнение called = true.

Я поставил Thread.Sleep(blah) до Assert....

Это работает, когда тест выполняется изолированно.

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

процесс агента был остановлен на время выполнения теста

... который не имеет немедленного разрешения Модульное тестирование - процесс агента был остановлен во время выполнения теста. и оставляет вас уязвимым для Ошибка VS 2010 Test Runner« Процесс агента был остановлен во время выполнения теста. ”

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

TIA


person MattC    schedule 24.03.2011    source источник
comment
Я получал это сообщение, и оказалось, что в потоке, созданном ThreadPool.QueueUserWorkitem, возникло исключение. Установка точки останова (и соответствующее ведение журнала) привело меня к ее обнаружению.   -  person StingyJack    schedule 02.05.2012


Ответы (1)


Один из вариантов - сделать OnNextItem ответственным за разблокировку:

// Setup as before
object monitor = new object();
queue.OnNextItem += i => { lock(monitor) { Monitor.Pulse(monitor); }};

lock (monitor)
{
    queue.Add(1);
    queue.Start();
    Assert.IsTrue(Monitor.Wait(monitor, 1000));
}

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

person Jon Skeet    schedule 24.03.2011
comment
Да, очень :) Однако вся проблема с тестированием многопоточного кода станет только более очевидной, особенно с более широким использованием TPL, который имеет свои собственные ошибки и предостережения для правильного использования. - person MattC; 24.03.2011