Mocked a method to return true still returns false

1.3k Views Asked by At

I have a controller method like this :

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();
    }
}

The test case to test this controller is as below:

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");
     }
}

Even though I mocked the method SaveDetails to return true, it always returns false.
Can anyone tell me what am I doing wrong here? Many thanks.

2

There are 2 best solutions below

0
Nkosi On BEST ANSWER

The expectation was setup with a different IFormFile (reference/instance) than the one that was used when invoking the member under test. This causes the mock to return the default value of the invoked member, which is false since it is a boolean.

When GetFile() is invoked, it returns a new instance every time.

So either use the same instance, like was done with 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);
 }

Or loosen up the expectation using argument matchers like It.IsAny<>()

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);
 }
0
Peter Csala On

The problem with your Setup is that it is too specific.
Please bear mind that parameter matching is based on reference check in case of classes.

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

If you would like verify the call then you need to
save the result of the GetFile and then make the assertion:

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

Instead of casting the response of the controller's action to OkObjectResult I would recommend to assert that as well. (Just like I did in the above test case)