首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用模拟的IHttpFilter和IHttpContent对HttpClient进行单元测试,MockedHttpFilter抛出System.InvalidCastException

使用模拟的IHttpFilter和IHttpContent对HttpClient进行单元测试,MockedHttpFilter抛出System.InvalidCastException
EN

Stack Overflow用户
提问于 2015-09-29 19:59:03
回答 3查看 630关注 0票数 13

我有一个依赖于Windows.Web.Http (Windows10UAP App)的HttpClient的类。我想进行单元测试,因此我需要“模拟”HttpClient来设置Get-Call应该返回什么。我从一个使用手写模拟的IHttpFilter和IHttpContent的HttpClient的“简单”单元测试开始。它没有像预期的那样工作,并且我在测试资源管理器中得到了一个InvalidCastException。

单元测试看起来像这样:

代码语言:javascript
复制
    [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:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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结果视图中的错误:

代码语言:javascript
复制
Test Name:  TestMockedHttpFilter
Test FullName:  xxx.UnitTests.xxxHttpClientUnitTests.TestMockedHttpFilter
Test Source:    xxx.UnitTests\xxxHttpClientUnitTests.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依赖(Windows10UAP)对代码进行单元测试?

EN

回答 3

Stack Overflow用户

发布于 2017-12-31 23:45:07

您的单元测试没有任何意义,您实际上并不是在测试您的类,而是在尝试测试HttpClient类。我将假设一些事情,因为我们没有在您的问题中测试的方法;我假设您做了类似以下的事情:

代码语言:javascript
复制
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支持填充(隔离)和存根(Mock)。隔离是控制具体类的成员调用所需要的。

那么,根据我的例子,哪些成员需要控制?

  1. HttpResponseMessage.Content_Get
  2. HttpContent.ReadAsStringAsync

我不认为您需要改变JsonConvert.Deserialize<T>()成员的行为,但如果您愿意的话,您可以这样做。Microsoft Fakes框架一开始有点让人望而生畏,但一旦你开始使用它并让它工作,它就会变得更容易使用。或者,您可以使用其他支持隔离的框架:

来自Telerik

  • TypeMock
  • JustMock的隔离器

也许其他人存在,但我对他们不熟悉。

票数 1
EN

Stack Overflow用户

发布于 2015-09-30 20:31:32

您似乎没有使用与httpClient.SendRequestAsync(...)的返回值匹配的AsTask重载

尝试更改此设置

代码语言:javascript
复制
var resultContentTask = await httpClient.SendRequestAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("http://dontcare.ch"))).AsTask().ConfigureAwait(false);

到这个

代码语言:javascript
复制
var resultContentTask = await httpClient.SendRequestAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("http://dontcare.ch"))).AsTask<IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress>>().ConfigureAwait(false);
票数 0
EN

Stack Overflow用户

发布于 2017-12-31 23:00:07

最简单的解决方案是使用这个库来模拟HttpClient

https://github.com/richardszalay/mockhttp

代码语言:javascript
复制
PM> Install-Package RichardSzalay.MockHttp

我还用你的附加代码测试了它

代码语言:javascript
复制
       //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);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32843437

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档