我们有以下ASP.NET代码(为清楚起见,我将其简化了):
var httpContent = new StringContent(postData, Encoding.UTF8);
using (var client = this.GetClient(url, contentType))
{
using (var response = await client.PostAsync(url, httpContent).ConfigureAwait(true);
{
ret.Response = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
}
}
“客户端”在PROD中运行时是HttpClient对象,但在我们的单元测试中是:
MockRepository.GenerateMock<IHttpClient>();
请注意,我们使用 .ConfigureAwait(true)。这样做的原因是我们将对象存储在HttpContext.Current.Items集合中(这可能不是理想的选择,但实际上是这样)。如果我们没有持久化线程的Context,则HttpContext.Current将返回null。
在PROD中运行时,所有这些都按预期工作。
但是,当运行单元测试时,执行以下行会丢失Context和HttpContext。Current变为null:
await response.Content.ReadAsStringAsync().ConfigureAwait(true);
请注意-存根上的PostAsync后续调用将维护上下文。
那么我们如何编程存根?
using (var httpResponse = new HttpResponseMessage())
{
httpResponse.Content = new StringContent(msg, Encoding.UTF8);
httpResponse.StatusCode = HttpStatusCode.OK;
this.FakeHttpClient
.Stub(i => i.PostAsync("", "")).IgnoreArguments()
.Return(Task.FromResult(this.httpResponse)).Repeat.Once();
// Act
}
运行测试时,将返回数据,因此假对象的行为完美,但ReadAsStringAsync()。ConfigureAwait(true)会破坏上下文。
StringContent无法直接模拟,因为它没有无参数的构造函数。我创建了一个包装器类,以查看是否可以模拟它,但是当我有以下代码时:
var fakeStringContent = MockRepository.GenerateStub<Wrap>();
fakeStringContent
.Stub(fsc => fsc.ReadAsStringAsync())
.Return(Task.FromResult("<response/>"))
.Repeat.Once();
然后,.Stub(...)行上引发了一个异常:
System.InvalidOperationException:'异步操作未返回 一个System.Threading.Tasks.Task对象。