Поблочное тестирование событие, стреляющее из потока

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

public class ThreadRunner
{
    private bool keepRunning;

    public event EventHandler Started;
    public event EventHandler Finished;

    public void StartThreadTest()
    {
        this.keepRunning = true;
        var thread = new Thread(new ThreadStart(this.LongRunningMethod));
        thread.Start();
    }

    public void FinishThreadTest()
    {
        this.keepRunning = false;
    }

    protected void OnStarted()
    {
        if (this.Started != null)
            this.Started(this, new EventArgs());
    }

    protected void OnFinished()
    {
        if (this.Finished != null)
            this.Finished(this, new EventArgs());
    }

    private void LongRunningMethod()
    {   
        this.OnStarted();

        while (this.keepRunning)
            Thread.Sleep(100);

        this.OnFinished();
    }
}

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

[TestClass]
public class ThreadRunnerTests
{
    [TestMethod]
    public void CheckFinishedEventFiresTest()
    {
        var threadTest = new ThreadRunner();

        bool finished = false;

        object locker = new object();

        threadTest.Finished += delegate(object sender, EventArgs e)
        {
            lock (locker)
            {
                finished = true;
                Monitor.Pulse(locker);
            }
        };

        threadTest.StartThreadTest();
        threadTest.FinishThreadTest();

        lock (locker)
        {
            Monitor.Wait(locker, 1000);
            Assert.IsTrue(finished);
        }
    }
}

Так идея, здесь являющаяся, который тест заблокирует максимум для одной секунды - или до Finish событие запущено - прежде, чем проверить ли finished флаг установлен.

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

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

7
задан Dougc 25 February 2010 в 23:44
поделиться

4 ответа

Блокировка здесь просто неуместна, вы захотите сигнализировать о событии. Например:

    public void CheckFinishedEventFiresTest() {
        var threadTest = new ThreadRunner();
        var finished = new ManualResetEvent(false);
        threadTest.Finished += delegate(object sender, EventArgs e) {
            finished.Set();
        };
        threadTest.StartThreadTest();
        threadTest.FinishThreadTest();
        Assert.IsTrue(finished.WaitOne(1000));
    }
15
ответ дан 6 December 2019 в 07:25
поделиться

Кажется, ваш тест неверен. Предположим, что после threadTest.FinishThreadTest (); блокировка получена кодом в CheckFinishedEventFiresTest () . Тогда тест провалится. У вас есть четкое условие гонки.

Обратите внимание, что возврат от FinishThreadTest () не гарантирует , что поток завершен. Он просто устанавливает флаг для потока, который может быть принят во внимание в любой момент (ничто в принципе не гарантирует, что поток запускается планировщиком немедленно).

В вашем случае поток, скорее всего, будет занят Sleep () ing. После вызова threadTest.FinishThreadTest (); блокировка, скорее всего, будет получена потоком, в котором выполняется CheckFinishedEventFiresTest () . Монитор подождет 1 секунду, а затем откажется. После этого блокировка будет снята, поэтому делегат сможет заблокировать только в этот момент.

2
ответ дан 6 December 2019 в 07:25
поделиться

Влад абсолютно прав, но я попробую еще раз прояснить проблему:

// This runs on the other thread
threadTest.Finished += delegate(object sender, EventArgs e) {
    // I can't get this lock if the test thread gets here first!
    lock (locker) {
        finished = true;
        Monitor.Pulse(locker);
    }
};

Это можно сделать с помощью какого-нибудь хэндла ожидания. Я бы использовал ManualResetEvent:

ManualResetEvent waitHandle = new ManualResetEvent(false);
threadTest.Finished += delegate(object sender, EventArgs e) {
    finished = true;
    waitHandle.Set();
};

threadTest.StartThreadTest();
threadTest.FinishThreadTest();

// Specify a timeout so your test isn't hostage forever
if (waitHandle.WaitOne(timeout, true)) {
    Assert.IsTrue(finished);
}
4
ответ дан 6 December 2019 в 07:25
поделиться

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

Использование платформы тесты могут быть записаны следующим образом:

AsyncEventPublisher publisher = new AsyncEventPublisher();

Action test = () =>
{
    publisher.RaiseA();
    publisher.RaiseB();
    publisher.RaiseC();
};

var expectedSequence = new[] { "EventA", "EventB", "EventC" };

EventMonitor.Assert(test, publisher, expectedSequence, TimeoutMS);

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

В сообщениях много подробностей, описывающих проблемы и подходы, а также исходный код:

http://gojisoft.com/blog/2010/04/22/event-sequence-unit-testing-part -1 /

3
ответ дан 6 December 2019 в 07:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: