Is Moq mocking the return value of subinterface and ignoring the intermediate error or function step?

I was building an app recently and a colleague wrote a customization that I vowed to fail. I was wrong. It created a factory method with the expected value true and returned an integer. Since we weren't mocking our config, bool would always be false.

The setup was:

 var homeStoreDataServiceFactory = new Mock<IHomeStoreDataServiceFactory>();
 homeStoreDataServiceFactory.Setup(service => service.Create(true).GetStoreNumber())
                .Returns(5);

      

I thought that the call factory.Create(false)

would not generate a mock object, and thus we get 0 for an integer, not because of mocked value 5. Instead, no matter what we changed service.Create (X) to , calls to GetStoreNumber always return 5, as if we were using It.IsAny () .

I created MVCE so you can understand what I am confused about:

using System;
using Moq;

namespace MoqBugMCV
{
    public interface IItemServiceFactory
    {
        IItemService Create(bool shouldCreateServiceA);
    }

    public class Item
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }

    public interface IItemService
    {
        Item GetItem();
    }

    public class ItemManager
    {
        private readonly IItemService _itemService;

        public ItemManager(IItemServiceFactory itemServiceFactory)
        {
            _itemService = itemServiceFactory.Create(true); //<==== configured true (like by app.config at runtime or something)
        }

        public Item GetAnItem()
        {
            return _itemService.GetItem();
        }
    }

    internal class Program
    {

        private static void Main(string[] args)
        {
            var itemServiceFactory = new Mock<IItemServiceFactory>();
            var chrisItem = new Item {Name = "Chris Amazing Item", Price = 1000000};
            itemServiceFactory.Setup(factory => factory.Create(true).GetItem())
                .Returns(chrisItem);

            var itemManager = new ItemManager(itemServiceFactory.Object);

            var theItem = itemManager.GetAnItem();

            Console.WriteLine("The item is {0} and costs {1}", theItem.Name, theItem.Price);

            var itemServiceFactoryBroken = new Mock<IItemServiceFactory>();
            itemServiceFactoryBroken.Setup(factory => factory.Create(false).GetItem()).Returns(chrisItem); //expecting this to fail, because IItemServiceFactory.Create(true) is configured

            itemManager = new ItemManager(itemServiceFactoryBroken.Object);
            theItem = itemManager.GetAnItem();

            Console.WriteLine("The item is {0} and costs {1}", theItem.Name, theItem.Price); //would expect the item would be null or values to be blank
        }
    }
}

      

So ... is this a bug or a feature, or am I missing something about Moq?

+3


source to share


2 answers


Yes, it seems that any filters applied to intermediate parsing in recursive Moqs are ignored like here - even with the It.Is<>

filter being ignored. that is, currently, recursive mocks will always generate an equivalent It.IsAny<>

for any parameters in between, as you noted.

I currently crashed:

.Setup(service => service.Create(true).GetStoreNumber()

      

and split it into:

mockFactory.Setup(f => f.Create(xx))
mockService.Setup(service => service.GetStoreNumber())

      

Applying this split to your example MVCE

, the setting below only changes factory.Create(false)

, leaving factory.Create(true)

up to the default null

for reference types:



var itemServiceFactory = new Mock<IItemServiceFactory>();
var itemService = new Mock<IItemService>();

itemService.Setup(svc => svc.GetItem()).Returns(chrisItem);
itemServiceFactory.Setup(factory => factory.Create(false))
    .Returns(itemService.Object);

var itemManager = new ItemManager(itemServiceFactory.Object);
var theItem = itemManager.GetAnItem(); // Get NRE on _itemService.GetItem as expected

      

And accordingly, the mock factory.Create(true)

will use the customization service and GetAnItem()

return an awesome item.

Edit

There seems to be a way to get the filter in between and compress both factory and utility mocks settings in one go using Linq to Mocks :

var itemServiceFactory = Mock.Of<IItemServiceFactory>(
    fac => fac.Create(false) == Mock.Of<IItemService>(
        svc => svc.GetItem() == chrisItem));

var itemManager = new ItemManager(itemServiceFactory);
var theItem = itemManager.GetAnItem();

      

+2


source


After a couple of battles with a few other things, I came back to this to research further.

If you change the second example to:

        var itemServiceFactoryBroken = new Mock<IItemServiceFactory>(MockBehavior.Strict); //Strict behavior is important!!
        itemServiceFactoryBroken.Setup(factory => factory.Create(false).GetItem()).Returns(chrisItem); //expecting this to fail, because IItemServiceFactory.Create(true) is configured

        itemManager = new ItemManager(itemServiceFactoryBroken.Object);
        theItem = itemManager.GetAnItem();

        Console.WriteLine("The item is {0} and costs {1}", theItem.Name, theItem.Price);

      



It throws an exception because the setting has not been configured. I still believe this is a bug, its definitely not what you expect. GetItem should not return chrisItem as the factory is not configured to output a mocking class that returns anything given the configuration of the system under test.

However, the strict behavioral layouts seem to be "aware" that this setting is invalid and throws an exception. There seems to be a difference in behavior in these cases.

+1


source







All Articles