单元测试Windows.Web.Http HttpClient与模拟的IHttpFilter和IHttpContent,MockedHttpFilter抛出System.InvalidCastExce
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单元测试Windows.Web.Http HttpClient与模拟的IHttpFilter和IHttpContent,MockedHttpFilter抛出System.InvalidCastExce相关的知识,希望对你有一定的参考价值。
我有一个类依赖于Windows.Web.Http(Windows 10 UAP App)中的HttpClient。我想进行单元测试,因此我需要“模拟”HttpClient来设置Get-Call应该返回的内容。我开始使用HttpClient进行“简单”的单元测试,使用手写模拟的IHttpFilter和IHttpContent。它没有按预期工作,我在Test-Explorer中得到一个InvalidCastException。
单元测试看起来像:
[TestMethod]
public async Task TestMockedHttpFilter()
{
MockedHttpContent mockedContent = new MockedHttpContent("Content from MockedHttpContent");
MockedHttpFilter mockedHttpFilter = new MockedHttpFilter(HttpStatusCode.Ok, mockedContent);
HttpClient httpClient = new HttpClient(mockedHttpFilter);
var resultContentTask = await httpClient.SendRequestAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("http://dontcare.ch"))).AsTask().ConfigureAwait(false);
// Test stops here, throwing System.InvalidCastException: Specified cast is not valid
// Code not reached...
var result = await resultContentTask.Content.ReadAsStringAsync();
Assert.AreEqual("Content from MockedHttpContent", result);
}
我在MockedHttpFilter中实现了IHttpFilter:
public class MockedHttpFilter : IHttpFilter
{
private HttpStatusCode _statusCode;
private IHttpContent _content;
public MockedHttpFilter(HttpStatusCode statusCode, IHttpContent content)
{
_statusCode = statusCode;
_content = content;
}
public IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> SendRequestAsync(HttpRequestMessage request)
{
return AsyncInfo.Run<HttpResponseMessage, HttpProgress>((token, progress) =>
Task.Run<HttpResponseMessage>(()=>
{
HttpResponseMessage response = new HttpResponseMessage(_statusCode);
response.Content = _content;
return response; // Exception thrown after return, but not catched by code/debugger...
}));
}
}
我在MockedHttpContent中实现了IHttpContent:
public class MockedHttpContent : IHttpContent
{
private string _contentToReturn;
public MockedHttpContent(string contentToReturn)
{
_contentToReturn = contentToReturn;
}
public HttpContentHeaderCollection Headers
{
get
{
return new HttpContentHeaderCollection();
}
}
public IAsyncOperationWithProgress<string, ulong> ReadAsStringAsync()
{
return AsyncInfo.Run<string, ulong>((token, progress) => Task.Run<string>(() =>
{
return _contentToReturn;
}));
}
}
Test-Explorer结果视图中的错误:
Test Name: TestMockedHttpFilter
Test FullName: xxx.UnitTests.xxxHttpClientUnitTests.TestMockedHttpFilter
Test Source: xxx.UnitTestsxxxHttpClientUnitTests.cs : line 22
Test Outcome: Failed
Test Duration: 0:00:00.1990313
Result StackTrace:
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at xxx.UnitTests.xxxHttpClientUnitTests.<TestMockedHttpFilter>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Result Message: Test method xxx.UnitTests.xxxHttpClientUnitTests.TestMockedHttpFilter threw exception:
System.InvalidCastException: Specified cast is not valid.
首先,不确定抛出异常的原因/我做错了什么。也许有人可以指出我正确的方向或提示下一步检查/测试什么?
其次,是否有更好的方法来使用HttpClient依赖(Windows 10 UAP)对代码进行单元测试?
你的单元测试没有意义,你并没有真正测试你的类,而是你试图测试HttpClient类。我会假设一些事情,因为我们在你的问题中没有测试方法;我假设您执行以下操作:
public async Task<MyCustomClass> MethodUnderTest()
{
// ...
using (var client = new HttpClient(...))
{
// ...
var json = response.Content.ReadAsStringAsync();
return JsonConvert.Deserialize<MyCustomClass>(json);
}
}
如果是这种情况,那么您必须了解您的类不接受某些依赖项。您可以更改类,以便注入每个外部依赖项,但这可能是一个轻微的矫枉过正......您真的希望任何消耗和实例化您的类的人必须向您提供HttpClient吗?所以,更好的选择是使用一个模拟框架,它可以模拟一个具体类的依赖;大多数模拟框架都可以处理模拟接口(这些很容易),但很少有人可以处理模拟具体类。
我建议使用的框架是Microsoft Fakes框架。 Microsoft Fakes支持Shims(隔离)和Stubs(Mocks)。隔离是您控制具体类的成员调用所需的。
那么,根据我的例子,需要控制哪些成员?
我认为你不需要改变JsonConvert.Deserialize<T>()
成员的行为,但如果你愿意,你可以。微软Fakes框架起初有点令人生畏,但是一旦你开始使用它并使其工作,它将变得更容易使用。或者,您可以使用其他支持隔离的框架:
- 来自Telerik的JustMock
- TypeMock隔离器
也许其他人存在,我不熟悉他们。
看来你没有使用与AsTask
的返回值匹配的httpClient.SendRequestAsync(...)
重载
尝试改变这一点
var resultContentTask = await httpClient.SendRequestAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("http://dontcare.ch"))).AsTask().ConfigureAwait(false);
对此
var resultContentTask = await httpClient.SendRequestAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("http://dontcare.ch"))).AsTask<IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress>>().ConfigureAwait(false);
最简单的解决方案是使用此库来模拟HttpClient
https://github.com/richardszalay/mockhttp
PM> Install-Package RichardSzalay.MockHttp
我还用附加的代码测试了它
//Arrange
var mockHttp = new MockHttpMessageHandler();
mockHttp.When("http://dontcare.ch")
.Respond("application/txt", "Content from MockedHttpContent"); // Respond with content
var client = mockHttp.ToHttpClient();
//Act
var response = await client.GetAsync("http://dontcare.ch");
var result = await response.Content.ReadAsStringAsync();
//Assert
Assert.AreEqual("Content from MockedHttpContent", result);
以上是关于单元测试Windows.Web.Http HttpClient与模拟的IHttpFilter和IHttpContent,MockedHttpFilter抛出System.InvalidCastExce的主要内容,如果未能解决你的问题,请参考以下文章
Windows.Web.Http.HttpClient 抛出 COMException c#
使用 Windows.Web.Http.HttpClient 类 PATCH 异步请求
System.Net.Http.HttpClient vs Windows.Web.Http.HttpClient - 主要区别是啥?
在 Windows.Web.Http 和 System.Net.Http 中混淆使用哪个版本的 HttpClient;