Ser*_*ych 7 c# unit-testing asynchronous rhino-mocks
我想测试一个应该连续运行直到被杀死的任务。假设正在测试以下方法:
public class Worker
{
public async Task Run(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
// do something like claim a resource
}
catch (Exception e)
{
// catch exceptions and print to the log
}
finally
{
// release the resource
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
和一个测试用例
[TestCase]
public async System.Threading.Tasks.Task Run_ShallAlwaysReleaseResources()
{
// Act
await domainStateSerializationWorker.Run(new CancellationToken());
// Assert
// assert that resource release has been called
}
Run Code Online (Sandbox Code Playgroud)
问题是任务永远不会终止,因为永远不会请求取消。最终,我想创建一个CancellationToken存根,MockRepository.GenerateStub<CancellationToken>()并告诉它IsCancellationRequested返回哪个调用true,但CancellationToken不是引用类型,因此不可能。
所以问题是如何进行测试,Run执行n迭代然后终止?不重构有可能Run吗?
Nko*_*osi 10
这取决于Run. 如果有一些注入的依赖
例如
public interface IDependency {
Task DoSomething();
}
public class Worker {
private readonly IDependency dependency;
public Worker(IDependency dependency) {
this.dependency = dependency;
}
public async Task Run(CancellationToken cancellationToken) {
while (!cancellationToken.IsCancellationRequested) {
try {
// do something like claim a resource
await dependency.DoSomething();
} catch (Exception e) {
// catch exceptions and print to the log
} finally {
// release the resource
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后可以模拟和监视它以计算某个成员被调用的次数。
[TestClass]
public class WorkerTests {
[TestMethod]
public async Task Sohuld_Cancel_Run() {
//Arrange
int expectedCount = 5;
int count = 0;
CancellationTokenSource cts = new CancellationTokenSource();
var mock = new Mock<IDependency>();
mock.Setup(_ => _.DoSomething())
.Callback(() => {
count++;
if (count == expectedCount)
cts.Cancel();
})
.Returns(() => Task.FromResult<object>(null));
var worker = new Worker(mock.Object);
//Act
await worker.Run(cts.Token);
//Assert
mock.Verify(_ => _.DoSomething(), Times.Exactly(expectedCount));
}
}
Run Code Online (Sandbox Code Playgroud)
在不更改代码的情况下,您可以做的最好的事情就是在特定时间后取消。该CancellationTokenSource.CancelAfter()方法使这变得简单:
[TestCase]
public async System.Threading.Tasks.Task Run_ShallAlwaysReleaseResources()
{
// Signal cancellation after 5 seconds
var cts = new TestCancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5));
// Act
await domainStateSerializationWorker.Run(cts.Token);
// Assert
// assert that resource release has been called
}
Run Code Online (Sandbox Code Playgroud)
代码的编写方式(IsCancellationRequested每次迭代仅检查一次)意味着取消将在一定次数的完整迭代后发生。只是每次的数字不会相同。
如果您想在特定次数的迭代后取消,那么您唯一的选择是修改代码以跟踪发生了多少次迭代。
我想我也许能够创建一个继承自的新类来CancellationTokenSource跟踪IsCancellationRequested已经测试了多少次,但这是不可能的。
| 归档时间: |
|
| 查看次数: |
5083 次 |
| 最近记录: |