Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
pjank opened this issue Nov 6, 2024 · 1 comment
Open

Comments

@pjank
Copy link

pjank commented Nov 6, 2024

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 pjank added the bug label Nov 6, 2024
@kzu
Copy link
Member

kzu commented Nov 12, 2024

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 kzu added discussion and removed bug labels Nov 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants