NSubstitute icon indicating copy to clipboard operation
NSubstitute copied to clipboard

How to override the default behavior for unconfigured methods

Open penenkel opened this issue 4 years ago • 1 comments

I'm using NSubstitute (in conjunction with AutoFixture) to auto-mock service interfaces. Consider the following example

interface ISomeDependency
{
  object MethodA();
  object MethodB();
  object MethodC();
}
class SomeClass
{
   public SomeClass(ISomeDependency dependency) { /* ... */}
   public object DoSomething() 
   {
      return _dependency.MethodA(); // uses only MethodA
   }
}


// in test
var dep = Fixture.Freeze<ISomeDependency>();
dep.MethodA().Returns(new object());
var sut = Fixture.Create<SomeClass>();
var result = sut.DoSomething();

As you can see the SUT only uses one method of the dependency interface. In this scenario I would like to ensure that all methods on the dependency substitute are either

  • explicitly considered/configured by the test author
  • never called by the sut

I see two approaches to this: Either I configure all other methods on the substitute to throw an exception or I assert that all other have not been called. The problem with both of those is that I have to do this explicitly for each of the other methods. This means that the test has to be adapted whenever the dependency is extended by an other method. A task which is all to easy ily overlooked.

As such the default NSubstitute behavior of silently returning null for any not configured method makes little sense. I would rather have it throw a NotImplemented exception so that I can quickly see when I have forgotten to configure a method. Is there a way to do this?

Alternatively I would welcome a variant of .ReturnsForAll<T>() that is not bound to a specific return type. More like a .DoThisForAllOther(Func<ICall> handler).

A third option would be a .DidNotReceiveAnyOtherCallWithAnyArgs() assert.

penenkel avatar Nov 23 '20 12:11 penenkel

Hi @penenkel ,

I think the ReceivedCalls extension can be adapted to do what you want:

var allCalls = dep.ReceivedCalls();
foreach (var call in allCalls) {
    Console.WriteLine(call.GetMethodInfo());
}

You could look at injecting an auto-value provider in to throw if not stubbed, but I think it would be easiest and most consistent to implement a method over ReceivedCalls, so ending up with something like !dep.ReceivedCalls().Where(call => call != allowedCalls).Any() (or a ReceivedOnly method that contains a call like that).

dtchepak avatar Nov 23 '20 23:11 dtchepak