【问题标题】:How to abort unit test from another thread?如何从另一个线程中止单元测试?
【发布时间】:2012-04-10 09:02:47
【问题描述】:

我有基本运行 2 个线程的单元测试示例代码:主测试线程和我正在启动的另一个线程应该在一段时间后测试执行失败(这基本上是一个超时线程)

代码如下:

[TestClass]
public class SomeTestClass
{
    [TestInitialize]
    public void BeforeTest()
    {
        var task = new Task(abortIfTestStilRunsAfterTimeout);
        task.Start();
    }

    [TestMethod]
    public void TestMethod()
    {
        Thread.Sleep(5000);
    }

    private void abortIfTestStilRunsAfterTimeout()
    {
        Assert.Fail("timeout passed!");

    }
}

好吧,我原以为TestMethod() 测试会失败,但实际发生的是运行Assert.Fail 方法的任务线程出现异常,而另一个线程继续运行并且测试通过。

我正在寻找一种使测试方法失败的方法

【问题讨论】:

  • 你到底想测试什么?
  • 我有 100 个测试用例在我的系统上运行。该系统是一个多线程网络应用程序。当测试用例剂量是以正确的顺序发送我的应用程序消息时,它会等待特定的回复顺序
  • 所以如果某些测试需要超过 5000 毫秒,你会手动失败吗?
  • 通常,当你有线程时,你正在做的测试类型不被认为是“单元测试”。单元测试用于测试确定性案例,而线程是非确定性的。
  • 如果这听起来很奇怪,我很抱歉,但是什么信号成功,什么信号失败?每个测试用例都由一个单独的头运行,对吧?因此,您创建并启动运行测试用例的线程。你怎么知道它已经成功完成了?测试线程是否调用了一些“OnComplete”委托?

标签: c# multithreading visual-studio-2010 unit-testing


【解决方案1】:

您可以尝试获取对测试线程的引用并在其上调用Abort()。您可以将异常状态对象传递给Abort(),您可以使用它来传递失败消息:

[TestClass]
public class SomeTestClass
{
    Thread testThread;

    [TestInitialize]
    public void BeforeTest()
    {
        testThread = Thread.CurrentThread;
        var task = new Task(abortIfTestStilRunsAfterTimeout);
        task.Start();
    }

    [TestMethod]
    public void TestMethod()
    {
        try
        {
            Thread.Sleep(5000);
        }
        catch (ThreadAbortException e)
        {
             Assert.Fail((string)e.ExceptionState);
        }
    }

    private void abortIfTestStilRunsAfterTimeout()
    {
        testThread.Abort("timeout passed!");
    }
}

如果您不想修改大约 100 个测试,您可以使用 PostSharp 之类的工具通过在每个测试用例周围插入 try {} catch {} 逻辑来修改您的测试用例。归根结底就是编写一个属性并用它装饰你的测试程序集(它被称为面向方面的编程,而 PostSharp 中的框架被称为老挝)。

【讨论】:

  • 我也试过这种方法,但是使用这种方法没有通过断言描述。我担心如果其他人在我的测试中遇到问题,他\她将无法理解导致这次流产的原因
  • @Tzahi:您可以通过Abort 传递消息 - 我调整了代码。
  • 这比上一个建议要好,但需要我修改 100 个测试。他们没有什么不那么侵入性的吗?
  • @Tzahi:你可以使用 PostSharp 为你做这件事。
  • @Tzahi:免费入门版支持方法级别方面,这应该是您所需要的。或者,您可以尝试 Afterthought:github.com/vc3/Afterthought 或 CciSharp:ccisamples.codeplex.com/…
【解决方案2】:

这个想法很简单——只需在任务/线程中运行主测试逻辑,并将超时处理代码放入测试方法/测试初始化​​中。某种Inversion of Control

// Test class level
ManualResetEvent mre = new ManualResetEvent(false);                                

[TestMethod]     
public void TestMethod()     
{                     
    // start waiting task
    Task task = Task.Factory.StartNew(() =>
        {
            // Test Body HERE!
            // ... 

            // if test passed - set event explicitly
            mre.Set();
        });


    // Timeout handling logic, 
    // !!! I believe you can put it once in the TestInitialize, just check
    // whether Assert.Fail() fails the test when called from TestInitialize
    mre.WaitOne(5000);

    // Check whether ManualResetEvent was set explicitly or was timeouted
    if (!mre.WaitOne(0))
    {
        task.Dispose();
        Assert.Fail("Timeout");
    }                
}                    

PS:关于WaitOne(0)诡计,MSDN

如果毫秒超时为零,则该方法不会阻塞。它测试 等待句柄的状态并立即返回。

【讨论】:

  • 不幸的是,自动休息事件不是这种情况下的解决方案。我写了这个测试用例,因为我有一个非常复杂的测试,在某些情况下我发现测试卡住了,这意味着有一段时间什么都没有发生。我的意图是每次测试开始时运行一个新的超时线程,当测试完成时,这个线程将被破坏,但是如果线程超时已经通过,它应该使这个测试失败并允许其他测试继续运行(我有几个 100 的。 ..)。您的解决方案要求我修改所有测试并且无法工作,因为我无法阻止我的测试方法的执行
  • 此解决方案需要我在测试中编写大量代码,这正是我想使用从类 init 而不是测试方法启动的线程的原因。这也需要我为每个测试编写 2 个方法 - 1 个用于减速部分,以便我在测试管理器上看到它,另一个用于实现(而不是编写匿名代码)
  • '当测试完成时这个线程将被销毁' - 你怎么知道测试已经完成并且你可以销毁超时线程?
  • @Tzai:您必须将每个测试包装在以下代码块中:Task task = Task.Factory.StartNew(() => { ... TEST HERE });,我相信没什么大不了的
  • martin - 我从之前的测试方法开始的每个任务都将以取消令牌开始,当测试退出时,我将在新方法上具有 [TestCleanup] 属性,它将取消超时任务。通过这个我会知道我的测试已经完成(通过\失败状态 - 这并不重要)
【解决方案3】:

这篇文章有点旧,但最近我遇到了类似的问题。

我的解决方案不同:

使用 NUnit TimeoutAttribute



在此处查看 NUnit 文档:
NUnit 2.5:https://nunit.org/docs/2.5/timeout.html
NUnit 3.0:https://github.com/nunit/docs/wiki/Timeout-Attribute

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-29
    相关资源
    最近更新 更多