How to use NSubstitute to return a generic passed in value like we do in moq?

198 Views Asked by At

Imagine I want to mock the Set method of this interface to return what is being passed on the generic parameter:

public interface IMemoryCache
{
    TItem Set<TItem>(object key, TItem value);
}

Using moq I can do it this way:

[Fact]
public void SetupUsingMoq()
{
    Mock<IMemoryCache> mock = new();

    mock
        .Setup(x => x.Set(It.IsAny<object>(), It.IsAny<It.IsAnyType>()))
        .Returns((object _, object input) => input);

    mock.Object.Set(1, "ok").Should().Be("ok");
}

I've tried doing the same thing using NSubstitute but, unfortunately, this doesn't work:

[Fact]
public void SetupUsingNSubstitute()
{
    IMemoryCache mock = Substitute.For<IMemoryCache>();

    mock
        .Set(Arg.Any<object>(), Arg.Any<Arg.AnyType>())
        .Returns(callInfo => callInfo.ArgAt<object>(1));

    mock.Set(1, "ok").Should().Be("ok");
}
2

There are 2 best solutions below

1
Mark Seemann On

If you only want to set it up for strings, you can make the test pass by explicitly configuring the object for that type:

[Fact]
public void SetupUsingNSubstitute()
{
    IMemoryCache mock = Substitute.For<IMemoryCache>();
    mock
        .Set(Arg.Any<object>(), Arg.Any<string>())
        .Returns(callInfo => callInfo.ArgAt<string>(1));

    var actual = mock.Set(1, "ok");

    Assert.Equal("ok", actual);
}
4
pfx On

For now, I didn't find anything official the documentation, but the tests succeed when setting up the mock via mock.Set(default!, Arg.Any<object>())

[Fact]
public void SetupUsingNSubstitute()
{
    IMemoryCache mock = Substitute.For<IMemoryCache>();

    mock
        .Set(default!, Arg.Any<object>())
        .Returns(callInfo => callInfo.ArgAt<object>(1));

    mock.Set(1, "ok").Should().Be("ok");
    mock.Set(2, "nok").Should().Be("nok");
    mock.Set("foo", 3).Should().Be(3);
}