我在尝试包装要在单元测试中使用的代码时遇到了一些问题。问题是这样的。我有接口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之类的东西。我应该如何解决这个问题?
提前致谢。
您的接口公开了具体的HttpClient类,因此,使用该接口的所有类都将与其绑定在一起,这意味着无法对其进行模拟。
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以不同方式实现的多个类。
HttpClientHandler
IHttpHandler
这种方法的主要问题是,您正在有效地编写一个仅在另一个类中调用方法的类,但是您可以创建一个 继承 自该类的类HttpClient(请参阅 Nkosi的示例 ,这比我的方法要好得多)。如果HttpClient拥有一个您可以嘲笑的界面,生活会容易得多,但不幸的是,它却没有。
但是,此示例 不是 黄金票。IHttpHandler仍然依赖于HttpResponseMessage,它属于System.Net.Http名称空间,因此,如果您确实需要除以外的其他实现HttpClient,则必须执行某种映射以将其响应转换为HttpResponseMessage对象。当然,这仅是一个问题, 如果您需要使用的多个实现 ,IHttpHandler但看起来却并非如此,这不是世界末日,而是要考虑的问题。
HttpResponseMessage
System.Net.Http
无论如何,您可以简单地进行模拟,IHttpHandler而不必担心具体HttpClient类被抽象化了。
我建议测试 非异步 方法,因为它们仍然调用异步方法,而不必担心单元测试异步方法的麻烦,请参见此处