Question

I have an interface which I am mocking with 'NSubstitute' which contains properties that return concreate classes, that is the return value is not an interface. e.g

public interface ISomething
{
    SomeObj First { get; }
    SomeObj Second { get; }
}

The 'SomeObj' concrete class has a default constructor but 'NSubstitute' always returns 'null' for these properties. The class itself is not under my control so I cannot simply make it derive from an interface.

Can 'NSubstitute' mock these type of properties? Or is there a way to override the behaviour? Otherwise I have to manually initialise the mock before the test and that can be a lot of code (even if its reused through a common method).

Perhaps there is a simpler solution that I have over-looked?

Était-ce utile?

La solution

Classes will be auto-mocked if they have a default (parameterless) constructor and all its members are virtual (see the note in the intro of Auto and recursive mocks). The aim of this is to reduce the potential for unwanted (destructive?) side-effects if we are using a substitute and suddenly hit a non-virtual, unmocked code path that does bad stuff in an instance we thought was fake.

NSubstitute doesn't have a way override this behaviour. Instead, I'd recommend creating all your substitutes via your own factory method (e.g. a static Sub.For<T>(...) method in your test project) that uses NSubstitute to produce a substitute, then applies all the specific initialisation rules you need, like using reflection to stub out values for each class property.

Hope this helps.

Possibly related links:

Autres conseils

It doesn't count as auto-mocking but you did also ask "Or is there a way to override the behaviour?" and "Perhaps there is a simpler solution that I have over-looked?"

This answer relies on the statements in your question that:

  • SomeObj is a class outside of your control, from which I assume it is either separately tested or else not testable
  • SomeObj has a default constructor

Sure, it requires you to "manually initialise the mock before the test" but since you've not told us what this object is it's not possible to know how much work it would take to implement fully.

public class SomeObj
{
    // Non-virtual to prevent auto-mocking
    public void Dummy() { }
}
public interface ISomething
{
    SomeObj First { get; }
    SomeObj Second { get; }
}
[TestMethod]
public void Test_17182355ms()
{
    ISomething mockedSomething = Substitute.For<ISomething>();

    SomeObj firstObj = mockedSomething.First;
    Assert.IsNull(firstObj);
    mockedSomething.First.Returns(new SomeObj());
    mockedSomething.Second.Returns(new SomeObj());
    firstObj = mockedSomething.First;
    Assert.IsNotNull(firstObj);
}

Another approach, though not without its own drawbacks, would be to extract your own interface for SomeObj, something like this:

public interface ISomeObj
{
    void Dummy();
}
public class MySomeObj : SomeObj, ISomeObj
{
}

and then mock ISomeObj in your test.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top