Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 单元测试嵌套方法_C#_Unit Testing_Mocking_Moq - Fatal编程技术网

C# 单元测试嵌套方法

C# 单元测试嵌套方法,c#,unit-testing,mocking,moq,C#,Unit Testing,Mocking,Moq,虽然我理解在单元测试一个方法时,模拟它的所有依赖项是非常重要的,但我仍然不清楚的是,当该方法被嵌套时会发生什么?我是只模拟父方法的依赖关系,还是也模拟子方法的依赖关系,还是设置对依赖对象的调用的期望值,并设置准确的返回值,以便执行所需的测试 例如,在下面的例子中,如果我们想对方法B进行单元测试,我们是否只模拟IHttpClientFactory&ILogger,或者我们是否也将方法的返回值设置为我们实际期望的值,因为否则,当测试方法执行时,它会继续执行,并尝试执行methodC,但失败的原因是c

虽然我理解在单元测试一个方法时,模拟它的所有依赖项是非常重要的,但我仍然不清楚的是,当该方法被嵌套时会发生什么?我是只模拟父方法的依赖关系,还是也模拟子方法的依赖关系,还是设置对依赖对象的调用的期望值,并设置准确的返回值,以便执行所需的测试

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

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 ..;
   }

}

因此,您有一个HTTP客户端,一个获取结构化数据的高级方法和一个获取响应内容的低级方法

这些东西比硬规则更像是一门艺术,但我最喜欢的规则是编写代码,将所有I/O抽象出来,然后模拟或测试实现I/O本身。这样,大部分业务逻辑都是可测试的

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

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

所以。。。关于您的示例代码。如果您试图测试MethodB,您需要MethodC有一个测试实现——要么模拟它所依赖的HttpClient,要么使其
虚拟化
,并在测试中重写它


旁注:重用HttpClient,并将其与类保持在一起

它被称为单元测试,因为您只测试一个单元—您可以在代码中找到的最小公共部分:f.i.类的方法。如果此方法调用另一个方法(当然是同一类的,否则它将是您已经模拟过的依赖项),那么您不应该关心,因为您也将为该另一个方法编写一些单元测试:o)另一方面,如果methodB调用(同一类的)某些受保护或私有方法从打电话的人的角度来看,你会在意吗?不,您希望该方法能够像文档中描述的那样工作,而不关心实现细节。这正是您使用单元测试测试的内容:单元的行为是否像文档一样?您只需要模拟您实际调用的方法,而不需要删除整个类。是的,您将模拟响应,使其成为您对测试用例的期望。无论被测试的具体单元需要完成测试,模拟中没有其他东西是重要的。下面是一个MS如何做到这一点的例子@SirRufo是一个很好的例子!这是抽象高级客户机的方法。如果OP正在编写该客户机,那么抽象将围绕HttpClient