【问题标题】:Mocked a method to return true still returns false模拟了一个返回 true 的方法仍然返回 false
【发布时间】:2021-04-01 07:25:41
【问题描述】:

我有一个这样的控制器方法:

public class ValuesController : Controller
{
    private readonly IService fService;
    public ValuesController( IService fService)
    {
        this.fService = fService;            
    }

    public IActionResult InsertDetails( [FromForm]Details details, IFormFile file)
    {
        bool result = fService.SaveDetails(details, file);
        return OK();
    }
}

测试这个控制器的测试用例如下:

public class ValuesControllerTests
{
     readonly Mock<IService> mockService = new Mock<IService>();
     
     [TestMethod]
     public void SaveDetails_Test_Success()
     {
         var details = GetMockDetails(); // gets the sample DTO values
         mockService.Setup(x => x.SaveDetails(details, GetFile())).Returns(true); ---- this is returning false even after mocking the value
         var controller = new ValuesController(mockService.Object);
         controller.ControllerContext.HttpContext = new DefaultHttpContext();
         var res = controller.InsertDetails(details, GetFile()) as OkObjectResult;
         Assert.AreEqual("File Details Saved Successfully", res.Value.ToString());
         Assert.AreEqual(200, res.StatusCode);
     }

     IFormFile GetFile()
     {
         return new FormFile(new MemoryStream(Encoding.UTF8.GetBytes("This is a dummy file")), 0, 0, "Data", "dummy.txt");
     }
}

即使我将方法 SaveDetails 模拟为返回 true,它总是返回 false
谁能告诉我我在这里做错了什么?非常感谢。

【问题讨论】:

    标签: mocking asp.net-core-webapi moq vs-unit-testing-framework


    【解决方案1】:

    期望设置的IFormFile(引用/实例)与调用被测成员时使用的不同。这会导致模拟返回被调用成员的默认值,即false,因为它是一个布尔值。

    GetFile()被调用时,每次都会返回一个新的实例。

    所以要么使用相同的实例,就像 details 所做的那样

    public void SaveDetails_Test_Success() {
        Details details = GetMockDetails();
        IFormFile file = GetFile(); //single instance
        mockService.Setup(x => x.SaveDetails(details, file)).Returns(true); //used here
        ValuesController controller = new ValuesController(mockService.Object);
        controller.ControllerContext.HttpContext = new DefaultHttpContext();
        var res = controller.InsertDetails(details, file) as OkObjectResult; //and here
        Assert.AreEqual("File Details Saved Successfully", res.Value.ToString());
        Assert.AreEqual(200, res.StatusCode);
     }
    

    或者使用像It.IsAny&lt;&gt;()这样的参数匹配器来放松期望

    public void SaveDetails_Test_Success() {
        Details details = GetMockDetails();
        mockService.Setup(x => x.SaveDetails(details, It.IsAny<IFormFile>())).Returns(true); //<-- NOTE
        ValuesController controller = new ValuesController(mockService.Object);
        controller.ControllerContext.HttpContext = new DefaultHttpContext();
        var res = controller.InsertDetails(details, GetFile()) as OkObjectResult; 
        Assert.AreEqual("File Details Saved Successfully", res.Value.ToString());
        Assert.AreEqual(200, res.StatusCode);
     }
    

    【讨论】:

      【解决方案2】:

      您的Setup 的问题在于它过于具体。
      请记住,参数匹配基于类的引用检查。

      mockService
            .Setup(service => service.SaveDetails(It.IsAny<Details>(), It.IsAny<IFormFile>()))
            .Returns(true); 
      

      如果您想验证通话,则需要
      保存GetFile 的结果,然后进行断言:

      public void SaveDetails_Test_Success()
      {
          //Arrange
          var details = GetMockDetails(); 
          var file = GetFile(); //same instance is used everywhere
      
          mockService
            .Setup(x => x.SaveDetails(It.IsAny<Details>(), It.IsAny<IFormFile>()))
            .Returns(true); 
      
          var controller = new ValuesController(mockService.Object);
          controller.ControllerContext.HttpContext = new DefaultHttpContext();
          
          //Act
          var result = controller.InsertDetails(details, file);
          
          //Assert
          var res = Assert.IsAssignableForm<OkObjectResult>(result); //Here we test the assignability as well
          Assert.AreEqual("File Details Saved Successfully", res.Value.ToString());
          //Assert.AreEqual(200, res.StatusCode); //This assert is unnecessary
      
          mockService
            .Verify(x => x.SaveDetails(details, file), Times.Once); 
      }
      

      我建议不要将控制器操作的响应强制转换为 OkObjectResult。 (就像我在上面的测试用例中所做的那样

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-01
        • 2013-09-20
        • 2012-02-21
        • 2018-11-09
        • 2012-07-07
        • 1970-01-01
        相关资源
        最近更新 更多