NSubstitute icon indicating copy to clipboard operation
NSubstitute copied to clipboard

Recieved.InOrder behavior change between V4.0.0 -> V4.2.2

Open sakennedy opened this issue 5 years ago • 3 comments

Describe the bug I'm attempting to update NSubstitute V4.0.0 -> V4.2.2 and am seeing a test fail that did not previously.

A call to 'Received.InOrder' is failing to detect a method call that it previously could though a call to .Received will.

To Reproduce I've created a standalone gist that will fail under V4.2.2 but works under V4.0.0: https://gist.github.com/sakennedy/787a12de838885c4c090e2ceaf4d254c

Expected behaviour I expect the test to succeeded but it fails within the 'Received.InOrder' at line 64.

If I change to the following code then it works:

runtimeEnvironment.Received(1).Run(Arg.Is<IRunnable>(runnable => runnable.Name == "someRunnable"));

Received.InOrder(() =>
{
    runtimeEnvironment.Run(Arg.Any<IRunnable>());
});

Environment:

  • NSubstitute version: 4.2.2 (from 4.0.0)
  • NSubstitute.Analyzers version: CSharp 1.0.14
  • Platform: .NET Framework 4.7.2 on Windows 10 Pro 19041.630

Additional context I have other unit tests that do similar checks that still succeed so I'm guessing its related to the specific signature.

sakennedy avatar Dec 08 '20 08:12 sakennedy

@sakennedy Is the issue reproducible if you refactor your sample a bit:

var runnable = Substitute.For<IRunnable>();
runnable.Name.Returns("someRunnable");

runnableManager
    .Find("someRunnable")
    .Returns(runnable);

It might be a bit tricky for NSubstitute to behave nicely when we create and configure other substitute in the Returns() callback.

zvirja avatar Dec 09 '20 12:12 zvirja

@zvirja I've updated the gist with the changes you suggested but the outcome remains the same:

NSubstitute.Exceptions.CallSequenceNotFoundException : Expected to receive these calls in order:

Run(x => (x.Name == "someRunnable"), "", <null>, False)

Actually received matching calls in this order:

*** Note: calls to property getters are not considered part of the query. *** at NSubstitute.Core.SequenceChecking.SequenceInOrderAssertion.Assert(IQueryResults queryResult) at NSubstitute.Received.InOrder(Action calls) at TestProject1.Tests.Test1() in ****\TestProject1\TestProject1\Tests.cs:line 6

I made further changes to gist to print the 'runnable' reference to the debugger output. The reference passed to the 'IRuntimeEnvironment.Run' call matches the substitute created but it appears that the substitute has been possibly reset afterwoods. The 'IRunnable.Name' property returns an empty string when called within the context of the 'Received.InOrder' call but is 'someRunnable', as expected, just before the call.

sakennedy avatar Dec 10 '20 02:12 sakennedy

This can be reproduced with very simple test:

    public Tests(ITestOutputHelper debug)
    {
        _debug = debug;
    }

    [Fact]
    public void Run()
    {
        var test = Substitute.For<ITest>();
        test.Name.Returns("example");

        // outputs: One: example
        _debug.WriteLine("One:" + test.Name);

        Received.InOrder(() =>
        {
            // outputs: Two:
            _debug.WriteLine("Two:" + test.Name);
        });
    }

    public interface ITest
    {
        string Name { get; }
    }

mantasaudickas avatar Jan 31 '22 11:01 mantasaudickas