一尘不染

在单元测试中模拟HttpClient

c#

我在尝试包装要在单元测试中使用的代码时遇到了一些问题。问题是这样的。我有接口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之类的东西。我应该如何解决这个问题?

提前致谢。


阅读 502

收藏
2020-05-19

共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类被抽象化了。

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

2020-05-19