简体   繁体   English

如何从另一个线程中止单元测试?

[英]How to abort unit test from another thread?

I have unit test sample code that basically runs 2 threads: the main test thread and another thread I'm launching that is supposed to fail the test execution after some time (this is basically a timeout thread)我有基本上运行 2 个线程的单元测试示例代码:主测试线程和我正在启动的另一个线程,它应该在一段时间后测试执行失败(这基本上是一个超时线程)

Code is as follows:代码如下:

[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!");

    }
}

Well, I was expecting that the TestMethod() test should fail but what actually happens is that the task thread that runs the Assert.Fail method gets an exception while the other thread keeps on running and the test passes.好吧,我原以为TestMethod()测试应该失败,但实际发生的是运行Assert.Fail方法的任务线程出现异常,而另一个线程继续运行并且测试通过。

I'm looking for a way to fail the test method我正在寻找一种使测试方法失败的方法

You can try and get a reference to the test thread and call Abort() on it.您可以尝试获取对测试线程的引用并在其上调用Abort() You can pass an exception state object to Abort() which you can use to pass a fail message:您可以将异常 state object 传递给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!");
    }
}

In case you do not want to modify some 100 tests you can use a tool like PostSharp to modify your test cases by inserting the try {} catch {} logic around each test case for you.如果您不想修改大约 100 个测试,您可以使用像 PostSharp 这样的工具来修改您的测试用例,方法是为您在每个测试用例周围插入try {} catch {}逻辑。 All it boils down to is to write an attribute and decorate you test assembly with it (it's called Aspect Oriented Programming and the framework in PostSharp for that is called Laos).归结起来就是编写一个属性并用它装饰你的测试程序集(它称为面向方面的编程,PostSharp 中的框架称为老挝)。

The idea is simple - just run main test logic in a task/thread, and just put timeout handling code in the test method/test initialize.这个想法很简单 - 只需在任务/线程中运行主要测试逻辑,并将超时处理代码放在测试方法/测试初始化中。 Some kind of 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: Regarding WaitOne(0) trick, MSDN : PS:关于WaitOne(0)技巧, MSDN

If millisecondsTimeout is zero, the method does not block.如果 millisecondsTimeout 为零,则该方法不会阻塞。 It tests the state of the wait handle and returns immediately.它测试等待句柄的 state 并立即返回。

This post is somewhat old, but recently I was having a similar issue.这篇文章有点旧,但最近我遇到了类似的问题。

My solution is different:我的解决方案是不同的:

use the NUnit TimeoutAttribute使用 NUnit TimeoutAttribute



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

A solution leveraging TimeoutAttribute and/or TestContext.CancellationTokenSource :利用TimeoutAttribute和/或TestContext.CancellationTokenSource的解决方案:

[TestClass]
public class SomeTestClass
{
    private Task abortTask;

    // Test runner will set this automatically.
    public TestContext Context { get; set; }

    [TestInitialize]
    public void BeforeTest()
    {
        this.abortTask = Task.Run(
            () => 
            {
                this.WaitForAbort();
                this.Context.CancellationTokenSource.Cancel();
            });
    }

    [TestMethod]
    public Task TestMethod1()
    {
        // Cancellation triggered by manual abort logic.
        this.TestWithCancellation(Context.CancellationTokenSource.Token);
    }

    [TestMethod]
    [Timeout(5000)]
    public Task TestMethod2()
    {
        // Cancellation triggered automatically by the runner after 5 sec.
        this.TestWithCancellation(Context.CancellationTokenSource.Token);
    }

    private void WaitForAbort()
    {
        // Concurrently check for some abort condition...
    }

    private void TestWithCancellation(CancellationToken cancellationToken)
    {
        // Do your test, but pass/respect the token!
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM