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

查看:57
本文介绍了在单元测试中模拟 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:

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天全站免登陆