Search code examples
c#unit-testingmoqmstest

Moq - mocking factory not generating new object with each iteration


I'm trying to moq and test the following method:

public List<SkuCountByRetailerVm> GetSkuCountsByRetailer()
{
    var viewModels = _factory.GetEmptyListOfSkuCountByRetailerVms();
    foreach (var retailer in _helper.GetAllRetailersInReading())
    {
        var vm = _factory.GetSkuCountsByRetailerVm();
        vm.Retailer = retailer;
        vm.SkuCount = _helper.GetSkuCountByRetailerInReading(retailer);
        viewModels.Add(vm);
    }
    return viewModels;
}

The SkuCountByRetailerVm class is very basic data transfer object, it has got Retailer and SkuCount properties.

The class under test depends on IFactory and IHelper, both of which I'm mocking in the test class.

It looks like the code for the class under test is fine. The problem when mocking is that I get 3 same viewmodels added to the list (all like the one which I expect to be added as last one), instead of 3 different ones. I think the problem with my test is that the way I setup the moq of the factory mockFactory.Setup(m => m.GetSkuCountsByRetailerVm()).Returns(new SkuCountByRetailerVm()); the vm just points to the same object, so I would rather need to have it instantiate a new vm each time? Not sure if this is correct conclusion and if so how to do it differently way with Moq.

I paste the test class below should this be needed.


[TestInitialize]
public void Setup()
{
    Mock<IReadingHelper> mockHelper = new Mock<IReadingHelper>();
    Mock<IVmFactory> mockFactory = new Mock<IVmFactory>();
    mockFactory.Setup(m => m.GetEmptyListOfSkuCountByRetailerVms()).Returns(new List<SkuCountByRetailerVm>());

    mockFactory.Setup(m => m.GetSkuCountsByRetailerVm()).Returns(new SkuCountByRetailerVm());

    mockHelper.Setup(m => m.GetAllRetailersInReading()).Returns(
                new List<string> { "Fake1", "Fake2", "Fake3" });
    mockHelper.Setup(m => m.GetSkuCountByRetailerInReading("Fake1")).Returns(5);
    mockHelper.Setup(m => m.GetSkuCountByRetailerInReading("Fake2")).Returns(10);
    mockHelper.Setup(m => m.GetSkuCountByRetailerInReading("Fake3")).Returns(15);

    SkuCountByRetailerVmBuilder builder = new SkuCountByRetailerVmBuilder(mockHelper.Object, mockFactory.Object);

    _vms = builder.GetSkuCountsByRetailer();
}

Solution

  • The Returns method has lots of overloads.

    Currently you are using the one with the signature Returns(TResult) where you can specify the value to return and Moq use the same value every time the mocked method is called (so you get the same instance of your SkuCountByRetailerVm.

    You need to use one of the overloads which takes a Func<TResult> as a parameter with this can you specify a function that will calculate the value to return from the method:

    mockFactory.Setup(m => m.GetSkuCountsByRetailerVm())
               .Returns(() => new SkuCountByRetailerVm());
    

    With this setup you will get a different instance every time your mocked method is caled.