小编典典

在单元测试中模拟 HttpClient

all

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

public interface IHttpHandler
{
    HttpClient client { get; }
}

和使用它的类HttpHandler

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

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

public class Connection
{
    private IHttpHandler _httpClient;

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

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

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. 我应该如何解决这个问题?


阅读 64

收藏
2022-07-07

共1个答案

小编典典

您的接口公开了具体HttpClient类,因此使用此接口的任何类都与它相关联,这意味着它不能被模拟。

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

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以不同方式实现的类。

这种方法的主要问题是您实际上是在编写一个只调用另一个类中的方法的类,但是您可以创建一个 继承 自的类HttpClient(参见 Nkosi
的示例
,它比我的方法好得多)。如果有一个可以模拟的界面,生活会容易得多HttpClient,不幸的是它没有。

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

无论如何,您可以简单地模拟IHttpHandler,而不必担心具体HttpClient类已被抽象掉。

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

2022-07-07