NSubstitute public virtual get internal set property not being auto substituted?

Created on 24 Jun 2020  路  5Comments  路  Source: nsubstitute/NSubstitute

I am attempting to substitute a class with a public virtual IThing Thing { get; internal set; } property and was expecting it to behave like a fully public property in terms of how NSubstitute automatically ties the get and the set together, but it doesn't seem to do that and I don't understand why. Is it expected behavior or could it be a bug, or enhancement?

To Reproduce

// in production code assembly
public interface IThing {}

internal class Example
{
  public virtual IThing Thing { get; internal set; }
}

// in test assembly
[Test]
public void Test()
{
  var thing = Substitute.For<IThing>();
  var ex = Substitute.For<Example>();
  ex.Thing = thing;

  ex.Thing.ShouldBe(thing); // throws exception because the proxies are not the same
} 

Expected behaviour
I was expecting that assertion to pass, but instead it throws because there are two different proxy instances there. The only arrangement that works as I was expecting is if the Thing property is public virtual {get; set; }, every other combination I've tried fails the same way.

Environment:

  • NSubstitute version: 4.2.2
  • NSubstitute.Analyzers version: CSharp 1.0.13
  • Platform: .NET 4.8

Additional context
The production assembly under test has it's AssemblyInfo.cs set up with InternalsVisibleTo the test assembly AND [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

bug

Most helpful comment

That is what I ended up doing, at least for now, but it was very surprising and confusing to me, so I will likely have to abandon the approach of substituting the virtual class (which I know is not recommended anyway, but this was the one scenario where it really seemed like it would make sense and clean things up a lot in my code base).

Here's why I found it so confusing:

public class Example
{
  public virtual IThing Thing1 { get; internal set; }
  public IThing Thing2 { get; internal set; }
  public virtual IThing Thing3 { get; set; }
}

[Test]
public void Test()
{
  var thing = Substitute.For<IThing>();
  var ex = Substitute.For<Example>();

  // code under test
  ex.Thing1 = thing;
  ex.Thing2 = thing;
  ex.Thing3 = thing;

  // back in the test
  ex.Thing2.ShouldBe(thing); // works as expected!
  ex.Thing3.ShouldBe(thing); // works as expected!
  ex.Thing1.ShouldBe(thing); // throws!!
}

Thing2 works because the property is not virtual, so there is no substituting happening at all. Thing3 works because that's how NSubstitute automatically makes properties work. But Thing1 doesn't work, which for the patterns where I wanted to apply this technique will be a real foot gun. So that's why I wanted to ask about if this is intended behavior in NSubstitute and if so why so I can better understand why I maybe shouldn't be trying this approach.

Thanks again!

All 5 comments

My answer will not explain why the code above doesn't work, maybe @dtchepak or @zvirja will know the details, however you can achieve expected result (and still having internal setter) by changing
```c#
ex.Thing = thing;

to
```c#
ex.Thing.Returns(thing);

Thanks @tpodolak, I over simplified the example, in my actual scenario the ex.Thing = thing part is done in the code under test (not in the test itself) and I'm trying to assert that it was set in the test.

If I understood you correctly it sounds like you are trying to verify received calls. If that is the case, you can verify that with the
```c#
sut.Received(1).Thing = thing;

```c#
public class MyClass
    {
        // in test assembly
        [Test]
        public void Test()
        {
            var thing = Substitute.For<IThing>();
            var sut = Substitute.For<Example>();

            sut.DoStuff(thing);

            sut.Received(1).Thing = thing;
        }
    }

Would it be enough? Or you need to use Shoudly to verify some sort of complex scenario?

That is what I ended up doing, at least for now, but it was very surprising and confusing to me, so I will likely have to abandon the approach of substituting the virtual class (which I know is not recommended anyway, but this was the one scenario where it really seemed like it would make sense and clean things up a lot in my code base).

Here's why I found it so confusing:

public class Example
{
  public virtual IThing Thing1 { get; internal set; }
  public IThing Thing2 { get; internal set; }
  public virtual IThing Thing3 { get; set; }
}

[Test]
public void Test()
{
  var thing = Substitute.For<IThing>();
  var ex = Substitute.For<Example>();

  // code under test
  ex.Thing1 = thing;
  ex.Thing2 = thing;
  ex.Thing3 = thing;

  // back in the test
  ex.Thing2.ShouldBe(thing); // works as expected!
  ex.Thing3.ShouldBe(thing); // works as expected!
  ex.Thing1.ShouldBe(thing); // throws!!
}

Thing2 works because the property is not virtual, so there is no substituting happening at all. Thing3 works because that's how NSubstitute automatically makes properties work. But Thing1 doesn't work, which for the patterns where I wanted to apply this technique will be a real foot gun. So that's why I wanted to ask about if this is intended behavior in NSubstitute and if so why so I can better understand why I maybe shouldn't be trying this approach.

Thanks again!

I think this may be a bug. When we look up the getter to stub from a set call, we use .GetProperties() which will only return public members.

Thank you for the report! (And thanks for the troubleshooting @tpodolak)

Related code:

Was this page helpful?
0 / 5 - 0 ratings