首页 > 解决方案 > 单元测试嵌套方法

问题描述

虽然我知道在对方法进行单元测试时,模拟它的所有依赖项非常重要,但我仍然对嵌套方法时会发生什么感到困惑?我是只模拟父方法的依赖关系,还是模拟子方法的依赖关系,还是对依赖对象的调用设置期望并设置确切的返回值,以便我可以执行测试我想要什么?

例如,在下面的例子中,如果我们想对方法 B 进行单元测试,我们是只模拟IHttpClientFactory&ILogger还是我们也将方法的返回值设置为我们实际期望的值,否则当测试方法执行时它会继续并尝试执行methodC失败的地方,因为执行client后的var client = _clientFactory.CreateClient()值为null?

using System.Net.Http;
...

public class classA
{
   private readonly IHttpClientFactory _clientFactory;
   private sting url = "...";
   private ILogger _log { get; set; }
   ...

   public classA(ILogger log, IHttpClientFactory clientFactory, ...)
   {
     _log = log;
     _clientFactory = clientFactory;
     ...
   }

   public string methodB(string inputB)
   {
      var varB = methodC(inputB);
      ...
      return ..;
   }

   public string methodC(string inputC)
   {
      ...
      var client = _clientFactory.CreateClient();
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      HttpResponseMessage httpResponseMessage = await client.PostAsync(url, new StringContent(inputC, Encoding.UTF8, "application/json"));
      responJsonText = await httpResponseMessage.Content.ReadAsStringAsync();
      ...
      return ..;
   }

}

标签: c#unit-testingmockingmoq

解决方案


所以你有一个 HTTP 客户端,一个获取一些结构化数据的高级方法和一个获取响应内容的低级方法。

这些东西更像是一门艺术,而不是硬性规则,但我大多数时候更喜欢的规则是编写可以抽象其所有 I/O 的代码,然后模拟或测试实现 I/O 本身。这样,大部分业务逻辑都是可测试的。

I/O 可以是很多东西——文件、网络、用户输入,甚至是诸如从证书存储中获取证书或读取注册表设置之类的东西。无论采用何种方法,在运行时从进程外部产生的任何数据都是 I/O。

当您模拟功能时,您最感兴趣的事情是验证方法的输入或模拟其输出(或两者兼而有之)。所以在你的模拟中,你不应该太关心实际的实现,因为你没有测试你的模拟方法——你正在测试调用它的任何东西。

所以...关于您的示例代码。如果您正在尝试测试 MethodB,则需要 MethodC 进行测试实现 - 通过模拟它所依赖的 HttpClient 或通过使其virtual在测试中覆盖它。

旁注:重用 HttpClient,将其保留在类中


推荐阅读