moq icon indicating copy to clipboard operation
moq copied to clipboard

DefaultValue.Empty vs "enumerables", and the use of SetReturnsDefault

Open pjank opened this issue 1 year ago • 2 comments

Describe the Bug

Firstly, DefaultValue.Empty is described as: "Default behavior, which generates ... empty array and enumerables ...." It may be a matter of semantics but I expected something like a List<T> to be also treated as "enumerables" in this context. Depending on point of view, applying this to everything implementing IEnumerable<T> may be too much (should a string be "empty" or "null" in this mode?) but how about everything related to ICollection<T>? If nothing else, I hope this post will help others who encounter the same issue and wonder how to "make it work" (without explicit Setup for each such method on a mock).

Secondly, SetReturnsDefault - it's currently very "picky" ;) It would be nice, IMHO, if it automatically supported all derived types... and/or wrapped in a Task, ValueTask.

Steps to Reproduce

This (MSTest-style) test code should explain it all:

[TestClass]
public class DefaultValue_Empty_Enumerables
{
    public interface IFoo
    {
        int[] GetArray();
        IEnumerable<int> GetIEnumerable();
        ICollection<int> GetICollection();
        IList<int> GetIList();
        List<int> GetList();

        Task<int> GetArrayAsync();
        Task<List<int>> GetListAsync();
    }

    [TestMethod]
    public void AllThoseTypesCanBeCalled_Enumerables()
    {
        Assert.IsTrue(typeof(int[]).IsAssignableTo(typeof(IEnumerable<int>)));
        Assert.IsTrue(typeof(ICollection<int>).IsAssignableTo(typeof(IEnumerable<int>)));
        Assert.IsTrue(typeof(IList<int>).IsAssignableTo(typeof(IEnumerable<int>)));
        Assert.IsTrue(typeof(List<int>).IsAssignableTo(typeof(IEnumerable<int>)));

        Assert.IsTrue(typeof(string).IsAssignableTo(typeof(IEnumerable<char>)), "something to watch out for");
        Assert.IsFalse(typeof(string).IsAssignableTo(typeof(ICollection<char>)), "a more specific workaround?");
    }

    [TestMethod]
    public void UsingDefaultMoqSettings_DefaultValue_Empty()
    {
        var mock = new Mock<IFoo>();
        Assert.AreEqual(DefaultValue.Empty, mock.DefaultValue);
    }

    [TestMethod]
    public async Task MethodsWithTheseReturnTypes_DefaultToEmpty()
    {
        var mock = new Mock<IFoo>();
        Assert.IsNotNull(mock.Object.GetArray());
        Assert.IsNotNull(mock.Object.GetIEnumerable());
        Assert.IsNotNull(await mock.Object.GetArrayAsync());
    }

    [TestMethod]
    public async Task MethodsWithTheseReturnTypes_DefaultToNull()
    {
        var mock = new Mock<IFoo>();
        Assert.IsNull(mock.Object.GetICollection());
        Assert.IsNull(mock.Object.GetIList());
        Assert.IsNull(mock.Object.GetList());
        Assert.IsNull(await mock.Object.GetListAsync());
    }

    [TestMethod]
    public async Task MakingItAllWork_Almost()
    {
        var mock = new Mock<IFoo>();
        mock.SetReturnsDefault<ICollection<int>>([]);
        mock.SetReturnsDefault<IList<int>>([]);
        mock.SetReturnsDefault<List<int>>([]);

        Assert.IsNotNull(mock.Object.GetICollection());
        Assert.IsNotNull(mock.Object.GetIList());
        Assert.IsNotNull(mock.Object.GetList());
        Assert.IsNull(await mock.Object.GetListAsync());
    }

    [TestMethod]
    public async Task MakingItAllWork_IncludingAsync()
    {
        var mock = new Mock<IFoo>();
        mock.SetReturnsDefault<ICollection<int>>([]);
        mock.SetReturnsDefault<IList<int>>([]);
        mock.SetReturnsDefault<List<int>>([]);
        mock.SetReturnsDefault(Task.FromResult(new List<int>()));

        Assert.IsNotNull(mock.Object.GetICollection());
        Assert.IsNotNull(mock.Object.GetIList());
        Assert.IsNotNull(mock.Object.GetList());
        Assert.IsNotNull(await mock.Object.GetListAsync());
    }

    [TestMethod]
    public void BTW_WatchOutForReset_ItResetsTheDefaultSettingsAsWell()
    {
        var mock = new Mock<IFoo>();
        mock.SetReturnsDefault<List<int>>([]);
        Assert.IsNotNull(mock.Object.GetList());

        mock.Reset();
        Assert.IsNull(mock.Object.GetList());

        mock.SetReturnsDefault<List<int>>([]);
        Assert.IsNotNull(mock.Object.GetList());
    }
}

Version Info

Moq v4.20.70

Back this issue Back this issue

pjank avatar Nov 06 '24 23:11 pjank

I think SetReturnsDefault<T> satisfies specific needs already, without introducing the potential for severe breaking changes if the current behavior were modified. I don't think that will happen.

kzu avatar Nov 12 '24 16:11 kzu

Due to lack of recent activity, this issue has been labeled as 'stale'. It will be closed if no further activity occurs within 30 more days. Any new comment will remove the label.

github-actions[bot] avatar May 15 '25 01:05 github-actions[bot]

This issue will now be closed since it has been labeled 'stale' without activity for 30 days.

github-actions[bot] avatar Jun 15 '25 01:06 github-actions[bot]