簡體   English   中英

如何從另一個線程中止單元測試?

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

我有基本上運行 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方法的任務線程出現異常,而另一個線程繼續運行並且測試通過。

我正在尋找一種使測試方法失敗的方法

您可以嘗試獲取對測試線程的引用並在其上調用Abort() 您可以將異常 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!");
    }
}

如果您不想修改大約 100 個測試,您可以使用像 PostSharp 這樣的工具來修改您的測試用例,方法是為您在每個測試用例周圍插入try {} catch {}邏輯。 歸結起來就是編寫一個屬性並用它裝飾你的測試程序集(它稱為面向方面的編程,PostSharp 中的框架稱為老撾)。

這個想法很簡單 - 只需在任務/線程中運行主要測試邏輯,並將超時處理代碼放在測試方法/測試初始化中。 某種控制反轉

// 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

如果 millisecondsTimeout 為零,則該方法不會阻塞。 它測試等待句柄的 state 並立即返回。

這篇文章有點舊,但最近我遇到了類似的問題。

我的解決方案是不同的:

使用 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

利用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