【发布时间】:2016-01-16 04:35:12
【问题描述】:
我正在尝试围绕异步发布/订阅系统编写单元测试。在我的单元测试中,我创建了一个 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 挂起。我知道这是由于任务永远不会返回而发生的。我不确定如何解决它。我知道我可以轻松地创建一个本地字段,我只需像这样分配值:
[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.");
}
这感觉不对。这假设我从未在整个堆栈中执行等待操作(当他们是订阅者时我会这样做)。此测试不是安全测试,因为测试可能会在发布完全完成之前完成。这里的意图是我的回调是异步的,并且发布是非阻塞的后台进程。
在这种情况下如何处理 CompletionSource?
【问题讨论】:
-
听起来像 ManualResetEvent 或 CountdownEvent 会更合适。
-
我还要说我认为 Publish 应该返回一个任务,如果它还没有的话。这非常有用,因为调用者可以等待,如果他们只希望在所有订阅者都处理完事件后进行处理。它也会使测试变得微不足道。只需等待发布并确保计数器为 1。
-
这个场景的目的是为 DomainEvents 提供一个异步工作流,同时保持我的域模型同步。我不想让整个域层异步来促进订阅事件的数据和服务层。如果我让
Publish方法返回Task并且不在我的域中等待它,那么在使用TPL 时这不是一个不好的做法吗? -
我更改了我的 Publish 以继续并返回
Task。 My Domain 不必等待它,所以这没什么大不了的。我只是不确定在使用 TPL 时是否通常不赞成这一点。感觉就像我正在更改我的代码以促进测试,我不应该这样做:/ -
还有其他可能性。一种是在第二次发布后简单地等待 TCS 任务超时,而不是等待,比如 5 秒。任务永远不应该完成。另一种选择可能是使用您知道将在第一个处理程序之后调用的另一个处理程序,尽管这可能依赖于订阅排序中的实现细节。测试没有发生的事情可能很困难。
标签: c# asynchronous task taskcompletionsource