0

I want to create a spy using NSubstitute, but can't find out how after a lot of googling and reading the docs.

The idea would be to have the substitute with an overriden method that I could use to monitor received calls, etc. as usual with mocking. But how to achieve this with NSubstitute?

I would prefer not to relax neither protected nor abstract modifiers.

Here's what I would like to do:

[Test]
public void Items_Add_shouldCall_ItemAddedMethod()
{
    var sut = Substitute.For<ItemsRepository>();
    // how to configure???
    sut.Items.Add(Substitute.For<IItem>());

    sut.Received().ItemAdded();  // currently cannot do this

// ...

public abstract class ItemsRepository
{
    public ObservableCollection<IItem> Items { get; set; } 
        = new ObservableCollection<IItem>();

    // ...

    protected abstract void ItemAdded();
}
heltonbiker
  • 26,657
  • 28
  • 137
  • 252
  • 1
    How about avoiding the substitute all together and create a derived class that exposes the desired functionality – Nkosi Apr 01 '19 at 12:57
  • @Nkosi I want to use a mocking framework in the first place exactly so that I do _not_ need to do that. Since this seems a pretty trivial use-case, I just think there must be a way to do this using the framework. – heltonbiker Apr 01 '19 at 12:59
  • 1
    Possibly related https://github.com/nsubstitute/NSubstitute/issues/222 – Nkosi Apr 01 '19 at 13:10
  • @Nkosi thanks for the link. The OP in the example wanted to directly invoke a protected method and verify the result. Instead, what I want to do is to call a public method instead, and verify that a protected method has been called (that is, I want a _test spy_ behavior). I'm afraid the linked issue does not contain an answer for that. – heltonbiker Apr 01 '19 at 13:15

1 Answers1

2

The NSubstitute API relies on calling a method to configure it with Returns or check Received() calls, which means for standard use we can only work with calls that can be invoked via the public API (which excludes protected and private members). There is no problem with the abstract part of your question; it is fine to use NSubstitute with accessible abstract members.

Even though C#, by design, prevents us from interacting directly with protected members, NSubstitute still records calls made to these members. We can check these by using the "unofficial" .ReceivedCalls() extension:

public abstract class ItemsRepository {
    public virtual void Add(int i) { ItemAdded(); }
    protected abstract void ItemAdded();
}

public class Fixture {
    [Fact]
    public void CheckProtectedCall() {
        var sub = Substitute.For<ItemsRepository>();
        sub.When(x => x.Add(Arg.Any<int>())).CallBase();

        sub.Add(42);

        var called = sub.ReceivedCalls().Select(x => x.GetMethodInfo().Name);

        Assert.Contains("ItemAdded", called);
    }
}

Other options include creating a derived class (as @Nkosi pointed out), or making the member internal instead of protected and using InternalsVisibleTo to make the member accessible to your test code (if you do the latter I recommend installing NSubstitute.Analyzer to help ensure this is configured correctly).

David Tchepak
  • 9,826
  • 2
  • 56
  • 68
  • Thanks! This worked, but I didn't like the "stringly typed" argument in the assertion. I ended up creating a derived mock manually, but instead of using `InternalsVisibleTo`, I extended the mock to contain public numeric properties that are incremented when the (overriden) protected methods are called. But now at least I have more than one alternative to choose from. Thanks again! – heltonbiker Apr 02 '19 at 14:55
  • I think it is a good idea to create your own sub-class if you need to mock protected behavior. Moq has a .Protected() Method that allows to mock such Methods, but it also requires you to retrieve the Method by a String. And you lose all Type-safety, if you use .Protected(). – Spoc Feb 07 '22 at 15:59