在单元测试中模拟HttpClient [英] Mocking HttpClient in unit tests

查看:405
本文介绍了在单元测试中模拟HttpClient的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在尝试包装要在单元测试中使用的代码时遇到了一些问题.问题是这样的.我有接口IHttpHandler:

I have some issues trying to wrap my code to be used in unit tests. The issues is this. I Have the interface IHttpHandler:

public interface IHttpHandler
{
    HttpClient client { get; }
}

使用它的类HttpHandler:

And the class using it, HttpHandler:

public class HttpHandler : IHttpHandler
{
    public HttpClient client
    {
        get
        {
            return new HttpClient();
        }
    }
}

然后是Connection类,该类使用simpleIOC注入客户端实现:

And then the Connection class, which uses simpleIOC to inject the client implementation:

public class Connection
{
    private IHttpHandler _httpClient;

    public Connection(IHttpHandler httpClient)
    {
        _httpClient = httpClient;
    }
}

然后我有一个具有此类的单元测试项目:

And then I have a unit test project which has this class:

private IHttpHandler _httpClient;

[TestMethod]
public void TestMockConnection()
{
    var client = new Connection(_httpClient);

    client.doSomething();  

    // Here I want to somehow create a mock instance of the http client
    // Instead of the real one. How Should I approach this?     

}

现在,显然我将在Connection类中使用一些方法,这些方法将从后端获取数据(JSON).但是,我想为此类编写单元测试,显然我不想针对真正的后端编写测试,而只是想模拟一个后端.我曾试图用谷歌搜索一个很好的答案,但没有取得很大的成功.我可以并且曾经使用过Moq进行模拟,但是从未使用过诸如httpClient之类的东西.我应该如何解决这个问题?

Now obviously I will have methods in the Connection class that will retrieve data (JSON) from a my back end. However, I want to write unit tests for this class, and obviously I don't want to write tests against the real back end, rather a mocked one. I Have tried to google a good answer to this without great success. I can and have used Moq to mock before, but never on something like httpClient. How Should I approach this problem?

谢谢.

推荐答案

您的接口公开了具体的HttpClient类,因此,使用该接口的所有类均已绑定到该类,这意味着无法对其进行模拟.

Your interface exposes the concrete HttpClient class, therefore any classes that use this interface are tied to it, this means that it cannot be mocked.

HttpClient不会从任何接口继承,因此您必须编写自己的接口.我建议使用类似于装饰器的模式:

HttpClient does not inherit from any interface so you will have to write your own. I suggest a decorator-like pattern:

public interface IHttpHandler
{
    HttpResponseMessage Get(string url);
    HttpResponseMessage Post(string url, HttpContent content);
    Task<HttpResponseMessage> GetAsync(string url);
    Task<HttpResponseMessage> PostAsync(string url, HttpContent content);
}

您的课程将如下所示:

public class HttpClientHandler : IHttpHandler
{
    private HttpClient _client = new HttpClient();

    public HttpResponseMessage Get(string url)
    {
        return GetAsync(url).Result;
    }

    public HttpResponseMessage Post(string url, HttpContent content)
    {
        return PostAsync(url, content).Result;
    }

    public async Task<HttpResponseMessage> GetAsync(string url)
    {
        return await _client.GetAsync(url);
    }

    public async Task<HttpResponseMessage> PostAsync(string url, HttpContent content)
    {
        return await _client.PostAsync(url, content);
    }
}

所有这些的要点是HttpClientHandler创建自己的HttpClient,然后您当然可以创建多个以不同方式实现IHttpHandler的类.

The point in all of this is that HttpClientHandler creates its own HttpClient, you could then of course create multiple classes that implement IHttpHandler in different ways.

此方法的主要问题是,您正在有效地编写一个仅在另一个类中调用方法的类,但是您可以从HttpClient创建一个继承的类(请参见 Nkosi的例如,这是比我更好的方法).如果HttpClient具有您可以模拟的接口,那么生活会容易得多,不幸的是,它没有.

The main issue with this approach is that you are effectively writing a class that just calls methods in another class, however you could create a class that inherits from HttpClient (See Nkosi's example, it's a much better approach than mine). Life would be much easier if HttpClient had an interface that you could mock, unfortunately it does not.

此示例不是黄金票. IHttpHandler仍然依赖于HttpResponseMessage,它属于System.Net.Http命名空间,因此,如果您确实需要除HttpClient之外的其他实现,则将必须执行某种映射以将其响应转换为HttpResponseMessage对象. 如果您需要使用IHttpHandler的多种实现,这当然只是一个问题,但这看起来并不像世界末日,但这是您需要考虑的问题.

This example is not the golden ticket however. IHttpHandler still relies on HttpResponseMessage, which belongs to System.Net.Http namespace, therefore if you do need other implementations other than HttpClient, you will have to perform some kind of mapping to convert their responses into HttpResponseMessage objects. This of course is only a problem if you need to use multiple implementations of IHttpHandler but it doesn't look like you do so it's not the end of the world, but it's something to think about.

无论如何,您可以简单地模拟IHttpHandler,而不必担心具体的HttpClient类,因为它已经被抽象掉了.

Anyway, you can simply mock IHttpHandler without having to worry about the concrete HttpClient class as it has been abstracted away.

我建议测试非异步方法,因为它们仍然调用异步方法,而不必担心单元测试异步方法的麻烦,请参见

I recommend testing the non-async methods, as these still call the asynchronous methods but without the hassle of having to worry about unit testing asynchronous methods, see here

这篇关于在单元测试中模拟HttpClient的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆