等待的呼叫在单元测试中失去上下文

时间:2018-11-02 08:59:07

标签: asp.net-mvc unit-testing async-await dotnet-httpclient configureawait

我们有以下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对象。

0 个答案:

没有答案