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

Properties from a substitute object are empty when accessing in a Received.InOrder bloc #744

Open
gabrielheming opened this issue Oct 18, 2023 · 0 comments
Labels
bug Reported problem with NSubstitute behaviour

Comments

@gabrielheming
Copy link

gabrielheming commented Oct 18, 2023

Describe the bug
Properties from a substitute object are empty when accessing in a Received.InOrder bloc. It will be clear checking the tests below.

To Reproduce

This is the simplest end-to-end example that I could get the same behaviour.

public interface IMyClass
{
    public Uri Uri { get; set; }
}

public interface IMyService
{
    public IMyClass GetMyClassAsync();
    public void PassUri(Uri uri);
}

public class SystemUnderTest
{
    private readonly IMyService service;

    public SystemUnderTest(IMyService service)
    {
        this.service = service;
    }

    public void Execute()
    {
        var myClass = service.GetMyClassAsync();
        service.PassUri(myClass.Uri);
    }
}

The following

public class ReceivedInOrderTests
{
    private readonly IMyClass myClass = Substitute.For<IMyClass>();
    private readonly IMyService myService = Substitute.For<IMyService>();
    private readonly SystemUnderTest sut;

    public ReceivedInOrderTests()
    {
        myClass.Uri.Returns(new Uri("https://test.com"));

        myService.Configure().GetMyClassAsync().ReturnsForAnyArgs(myClass);
        sut = new SystemUnderTest(myService);
    }

    // This works fine
    [Fact]
    public void PassMyClass_WithoutReceivedInOrder_ShouldValidateAllCalls()
    {
        // Arrange
        // Act
        sut.Execute();

        // Assert
        myService.Received(1).GetMyClassAsync();
        myService.Received(1).PassUri(Arg.Is<Uri>(uri => uri == myClass.Uri));
    }

    // This doesn't work
    [Fact]
    public void PassMyClass_WithReceivedInOrder_ShouldValidateOrderOfCalls()
    {
        // Arrange
        // Act
        sut.Execute();

        // Assert
        Received.InOrder(() =>
        {
            myService.GetMyClassAsync();
     
            // myClass.Uri is null right here
            myService.PassUri(myClass.Uri);
        });

        // this works and assets that myClass.Uri is not null outside the Received.InOrder bloc
        myClass.Uri.ShouldNotBeNull();
    }
}

What happens is that inside the property myClass.Uri inside Received.InOrder is null. For some reason the properties from the substitute object are being cleared when testing inside the Receive.InOrder.

Actual behaviour

NSubstitute.Exceptions.CallSequenceNotFoundException:

NSubstitute.Exceptions.CallSequenceNotFoundException

Expected to receive these calls in order:

GetMyClassAsync()
PassUri(<null>)

Actually received matching calls in this order:

GetMyClassAsync()

*** 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 Vincotte.ViA.Storage.UnitTests.ReceivedInOrderTests.PassMyClass_WithReceivedInOrder_ShouldValidateOrderOfCalls()

It doesn't happens when I implement a concrete class, below will you find the example:

public class MyClass : IMyClass
{
    public Uri Uri { get; set; }
}

The new test:

[Fact]
public void PassMyClass_WithConcreteMyClass_ShouldValidateOrderOfCalls()
{
    // Arrange
    var myConcreteClass = new MyClass
    {
        Uri = new Uri("https://test.com")
    };

    myService.Configure().GetMyClassAsync().ReturnsForAnyArgs(myConcreteClass);

    // Act
    sut.Execute();

    // Assert
    Received.InOrder(() =>
    {
        myService.GetMyClassAsync();

       // Works just fine
        myService.PassUri(myConcreteClass.Uri);
    });

    myClass.Uri.ShouldNotBeNull();
}

Expected behaviour
Success

Environment:

  • NSubstitute version: 5.1.0
  • NSubstitute.Analyzers version: CSharp 1.0.16
  • Platform: dotnet6.0 project on Mac

Feel free to ask more questions if the explanation is not enough.

@304NotModified 304NotModified added the bug Reported problem with NSubstitute behaviour label Apr 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Reported problem with NSubstitute behaviour
Projects
None yet
Development

No branches or pull requests

2 participants