如何编写一个MSTest单元测试来监听从另一个线程引发的事件?

时间:2008-10-20 19:37:10

标签: multithreading unit-testing mstest

我正在编写一个测试,希望从正在调用的对象接收事件。具体来说,我正在调用一个通过SSH连接到AIX机器的对象(使用开源Granados项目),然后断开连接,我想确保收到断开连接期间引发的OnConnectionClosed事件。这听起来很简单,我过去曾写过很多像这样的测试,但这次发生了一些我认为与线程有关的奇怪行为。

基本上,我调用的对象是在与我调用它不同的线程上引发'OnConnectionClosed'事件。我所看到的是,当我通过从UI中选择“调试测试”来运行测试时,它会通过,但是如果我选择“运行测试”,它就会失败(即使在调试运行期间没有设置断点)。我做了一些谷歌搜索,发现this post似乎表明默认情况下MSTest主机以单线程模式运行,但配置更改可以使其在多线程模式下运行。这听起来像是逻辑上解决我的问题,但当然,它没有。

我遇到的其他一些帖子也让我觉得MSTest根本就没有监视后台线程(所以他们提出的事件没有被“听到”)。这也是有道理的,因为它似乎在调试模式下工作,似乎上面的修复应该在逻辑上解决这个问题,然后我很困惑为什么它不起作用。有可能我只是没有正确处理线程,尽管如果是这样的话,我希望在调试模式下仍然存在问题。

还有其他人试图以类似的方式测试某些东西吗?如果是这样,你遇到过类似的问题吗?如果是这样,你是如何解决它们的?

我已粘贴下面的相关单元测试代码(出于安全原因,我已删除了连接信息)。

[TestClass]
public class SSHReaderTests
{
    private bool received = false;
    private delegate bool SimpleFunc();

    [TestInitialize]
    public void MyTestInitialize()
    {
        received = false;
    }

    [TestMethod]
    public void Should_raise_OnReaderConnectionClosed_event_after_successful_connection_is_disconnected()
    {
        IReader reader = new SSHReader();

        reader.OnReaderConnectionClosed += delegate
                                     {
                                         received = true;
                                     };

        reader.Connect("*****", "*****", "*****");

        //Assert.IsTrue(reader.IsConnected);

        reader.Disconnect();

        //Assert.IsFalse(reader.IsConnected);

        Assert.IsTrue(WaitUntilTrue(delegate {
            return received; }, 30000, 1000));
    }

    private static bool WaitUntilTrue(SimpleFunc func, int timeoutInMillis, int timeBetweenChecksInMillis)
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        while(stopwatch.ElapsedMilliseconds < timeoutInMillis)
        {
            if (func())
                return true;

            Thread.Sleep(timeBetweenChecksInMillis);
        }
        return false;
    }
}

2 个答案:

答案 0 :(得分:2)

使用System.Threading命名空间中的WaitHandle类。 AutoResetEvent或ManualResetEvent。两者之间的区别在于AutoResetEvent允许每次设置一个线程,而ManualResetEvent释放set上的所有等待线程。

您的示例不起作用的原因与编译器优化有关。代码实际上并没有编译成你初看起来的想法。最有可能的是,编译器会执行类似的操作,例如将局部变量放在寄存器中,并且在检查的循环期间从不实际获取它。您可以使用volatile关键字来避免这种类型的事情,但我强烈建议您阅读线程和并发性以获取更多详细信息。 Joe Duffy在http://www.bluebytesoftware.com的博客是一个很好的入门资源,我强烈推荐他即将发布的Windows并发编程书。

答案 1 :(得分:0)

不完全是您所询问的内容,但您可以通过查看名为CHESS的MS Research项目找到一些可行的解决方案或至少一些想法。它适用于.net中的多线程并发测试。