如何从另一个线程中止单元测试
本文关键字:单元测试 线程 另一个 | 更新日期: 2023-09-27 18:30:29
我有单元测试示例代码,基本上运行 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()
。可以将异常状态对象传递给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:
如果毫秒超时为零,则该方法不会阻止。它测试 等待句柄的状态并立即返回。
这篇文章有点旧,但最近我遇到了类似的问题。
我的解决方案是不同的:
使用 NUnit 超时属性
请参阅此处的 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!
}
}