我正在尝试围绕异步pub / sub系统编写单元测试。在我的单元测试中,我创建了一个TaskCompletionSource<int>
并在订阅回调中为其分配了一个值。在订阅回调中,我取消订阅这些出版物。下次我发布时,我想验证回调永远不会被击中。
[TestMethod]
[Owner("Johnathon Sullinger")]
[TestCategory("Domain")]
[TestCategory("Domain - Events")]
public async Task DomainEvents_subscription_stops_receiving_messages_after_unsubscribing()
{
// Arrange
string content = "Domain Test";
var completionTask = new TaskCompletionSource<int>();
DomainEvents.Subscribe<FakeDomainEvent>(
(domainEvent, subscription) =>
{
// Set the completion source so the awaited task can fetch the result.
completionTask.TrySetResult(1);
subscription.Unsubscribe();
return completionTask.Task;
});
// Act
// Publish the first message
DomainEvents.Publish(new FakeDomainEvent(content));
await completionTask.Task;
// Get the first result
int firstResult = completionTask.Task.Result;
// Publish the second message
completionTask = new TaskCompletionSource<int>();
DomainEvents.Publish(new FakeDomainEvent(content));
await completionTask.Task;
// Get the second result
int secondResult = completionTask.Task.Result;
// Assert
Assert.AreEqual(1, firstResult, "The first result did not receive the expected value from the subscription delegate.");
Assert.AreEqual(default(int), secondResult, "The second result had a value assigned to it when it shouldn't have. The unsubscription did not work.");
}
当我这样做时,测试会在第二个await
处挂起。我知道这是因为Task永远不会返回。我不确定如何解决它。我知道我可以很容易地创建一个本地字段,我只需要赋值如下:
[TestMethod]
[Owner("Johnathon Sullinger")]
[TestCategory("Domain")]
[TestCategory("Domain - Events")]
public void omainEvents_subscription_stops_receiving_messages_after_unsubscribing()
{
// Arrange
string content = "Domain Test";
int callbackResult = 0;
DomainEvents.Subscribe<FakeDomainEvent>(
(domainEvent, subscription) =>
{
// Set the completion source so the awaited task can fetch the result.
callbackResult++;
subscription.Unsubscribe();
return Task.FromResult(callbackResult);
});
// Act
// Publish the first message
DomainEvents.Publish(new FakeDomainEvent(content));
// Publish the second message
DomainEvents.Publish(new FakeDomainEvent(content));
// Assert
Assert.AreEqual(1, firstResult, "The callback was hit more than expected, or not hit at all.");
}
但这感觉不对。这假设我从不在整个堆栈中执行await操作(我在他们是订阅者时执行)。此测试不是一个安全的测试,因为测试可以在发布完成之前完成。这里的意图是我的回调是异步的,出版物是非阻塞的后台进程。
如何在这种情况下处理CompletionSource?
答案 0 :(得分:1)
很难测试不会发生的事情。关于你能做的最好的事情就是测试在合理的时间内没有发生。我有一个异步协调原语库,为了对这个场景进行单元测试,我不得不求助于在一段时间内观察任务,然后假设成功(see AssertEx.NeverCompletesAsync
)。
但这不是唯一的解决方案。也许逻辑上最干净的解决方案是伪造时间本身。也就是说,如果你的系统有足够的钩子用于假时间系统,那么你实际上可以编写一个测试来确保永远不会调用回调。这听起来很奇怪,但它非常强大。缺点是需要进行大量的代码修改 - 而不仅仅是返回Task
。如果您对Rx is the place to start感兴趣,TestScheduler
type。