Question

I have come across is situation when mocking a method with an output parameter using NSubstitute. I'm not sure how best to explain it in text, so I'll use some contrived examples and test cases...

In this contrived example, I'll be using a NSubstitute mock of a IDictionary<string, string>.

private static IDictionary<string, string> GetSubstituteDictionary()
{
    IDictionary<string, string> dict = Substitute.For<IDictionary<string, string>>();

    string s;
    dict.TryGetValue("key", out s).Returns(ci => { ci[1] = "value"; return true; });

    return dict;
}

Now, when I use this mock object in a simple manner, it returns as expected:

[Test]
public void ExampleOne()
{
    var dict = GetSubstituteDictionary();

    string value;
    bool result = dict.TryGetValue("key", out value);

    Assert.That(result, Is.True); // this assert passes.
    Assert.That(value, Is.EqualTo("value")); // this assert passes.
}

However, when I call the same code in a for loop, I get some unexpected behaviour:

[Test]
public void ExampleTwo()
{
    var dict = GetSubstituteDictionary();

    for (int i = 0; i < 2; i++)
    {
        string value;
        bool result = dict.TryGetValue("key", out value);

        Assert.That(result, Is.True); // this assert FAILS - unexpected!
        Assert.That(value, Is.EqualTo("value")); // this assert still passes.
    }
}

In particular, the Assert.That(result, Is.True); assertion passes on the first iteration of the loop, but fails on the second (and any subsequent) iteration.

However, if I modify the string value; line to be string value = null;, the assertion passes for all iterations.

What is causing this anomaly? Is this due to some semantics of C# for loops that I am missing, or is it an issue with the NSubstitute library?

Was it helpful?

Solution

The reason is that the value variable changes in the loop (set via the output parameter), so that it no longer matches the call you stubbed out.

You can try using .ReturnsForAnyArgs(), although you'll need to check the key within the stub then rather than via an argument matcher.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top