Unit Testing: Mocking returning null

64 Views Asked by At

I have done this many times with the same, scenario but this time it is failing and I am not able to figure out the reason, please help me out.

Below is my code, I have tried with both Moq, and NSubsitute, but for both, returns is returning null but it should return the json, I am putting inside, I have done the same in previous projects as well, but never faced such an issue, my mind totally not working.

public interface IMyInterface
{
    public string myOrder(List<Order> order);       
}

Target Class

public class funcMyFunctionApp
{
    private readonly IMyInterface myInterface;
    private readonly TelemetryClient telemetryClient;
    private readonly ILogger _logger;

    public funcMyFunctionApp(IMyInterface _myInterface)
    {
        myInterface = _myInterface;
    }

    [FunctionName("func1")]
    public IActionResult func1([HttpTrigger(AuthorizationLevel.Function, "post",Route = "func1")] string jsoninput)
    {
        try
        {
            if (!string.IsNullOrEmpty(jsoninput))
            {
                return new OkObjectResult(myInterface.myOrder(JsonConvert.DeserializeObject<List<Order>>(jsoninput)));
            }
            else
            {
                return new BadRequestObjectResult("Bad Order");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex.Message);
            if(ex.InnerException != null)
            {
                _logger.LogError(ex.InnerException.Message.ToString());
            }
            return new BadRequestObjectResult("Bad Order");
        }
    }
}

Test Fixture

public void shouldReturnOkObjectResultifInputisValid()
{
    string ValidJson1 = @"[
        {
          ""CreatedBy"":""[email protected]"",
          ""OrderNo"":""Pizz111"",
          ""Amount"":""10$"",
        }
    ]";
    string ValidOutputJson1 = @"{
        ""OrderNo"": ""808098"",
        ""Item"": ""Pizza""
    }";
    Mock<IMyInterface> myInterfaceMock =new Mock<IMyInterface>();
    IMyInterface myInterfaceSubsititueMock = Substitute.For<IMyInterface>();
    var input = JsonConvert.DeserializeObject<List<Order>>(ValidJson1);
    myInterfaceMock.Setup(m=>m.myOrder(input)).Returns(ValidOutputJson1);
    
    //Local Arrange
    myInterfaceSubsititueMock.myOrder(ValidJson1).Returns(ValidOutputJson1);
    funcMyFunctionApp funcRequest = new funcMyFunctionApp(myInterfaceMock.Object);
    
    // Act
    var result = funcRequest.func1(ValidJson1);
    
    // Assert
    Assert.Multiple(() =>
    {
        Assert.That((((OkObjectResult)result).Value as string), Is.EqualTo(ValidOutputJson1));
    });
}  

I am expecting that it should return the string provided under Returns

1

There are 1 best solutions below

2
Peter Csala On BEST ANSWER

TL;DR: JsonConvert.DeserializeObject<List<Order>>(jsoninput) is not the same as JsonConvert.DeserializeObject<List<Order>>(ValidJson1).

In case of Moq, the library uses object.Equals to determine whether the formal and actual parameters are the same. In other words it performs reference check for reference types. That's why even though your two JsonConvert.DeserializeObject method calls return the same collection, but they will be two different List instances.

In order to solve your problem use It.IsAny<List<Order>>() in your Setup call:

myInterfaceMock
   .Setup(m=>m.myOrder(It.IsAny<List<Order>>()))
   .Returns(ValidOutputJson1);

Dotnet fiddle: https://dotnetfiddle.net/dYcKk6


UPDATE #1

As it was suggested by Neil you can further restrict your Setup. The It.IsAny does not care about formal and actual parameters equality. Rather than it checks only data type.

If you want to make sure that the formal and actual collection parameters are the same you can utilize It.Is:

// If ordering does not matter
.Setup(m=>m.myOrder(It.Is<List<Order>>(actual => input.All(actual.Contains))))
//OR
.Setup(m=>m.myOrder(It.Is<List<Order>>(actual => actual.All(input.Contains))))

// If ordering matters
.Setup(m=>m.myOrder(It.Is<List<Order>>(actual => input.SequenceEqual(actual))))
// OR
.Setup(m=>m.myOrder(It.Is<List<Order>>(actual => actual.SequenceEqual(input))))

To make this work Order must be either a record or implement the IEquatable interface.