- Getting started
- Creating a substitute
- Setting a return value
- Return for specific args
- Return for any args
- Return from a function
- Multiple return values
- Replacing return values
- Checking received calls
- Clearing received calls
- Argument matchers
- Callbacks, void calls and When..Do
- Throwing exceptions
- Safe configuration and overlapping calls
- Raising events
- Auto and recursive mocks
- Setting out and ref args
- Actions with argument matchers
- Checking call order
- Partial subs and test spies
- Return for all calls of a type
- Threading
- Compatibility argument matchers
- NSubstitute.Analyzers
- How NSubstitute works
- Search
Callbacks, void calls and When..Do
Sometimes it is useful to execute some arbitrary code whenever a particular call is made. We have already seen an example of this when passing functions to Returns()
.
var counter = 0;
calculator
.Add(default, default)
.ReturnsForAnyArgs(x => 0)
.AndDoes(x => counter++);
calculator.Add(7, 3);
calculator.Add(2, 2);
calculator.Add(11, -3);
Assert.AreEqual(counter, 3);
The Return from a function topic has more information on the arguments passed to the callback.
Callbacks for void
calls
Returns()
can be used to get callbacks for members that return a value, but for void
members we need a different technique, because we can’t call a method on a void
return. For these cases we can use the When..Do
syntax.
When called, do this
When..Do
uses two calls to configure our callback. First, When()
is called on the substitute and passed a function. The argument to the function is the substitute itself, and we can call the member we are interested in here, even if it returns void
. We then call Do()
and pass in our callback that will be executed when the substitute’s member is called.
public interface IFoo {
void SayHello(string to);
}
[Test]
public void SayHello() {
var counter = 0;
var foo = Substitute.For<IFoo>();
foo.When(x => x.SayHello("World"))
.Do(x => counter++);
foo.SayHello("World");
foo.SayHello("World");
Assert.AreEqual(2, counter);
}
The argument passed to the Do()
method is the same call information passed to the Returns()
callback, which gives us access to the arguments used for the call.
Note that we can also use When..Do
syntax for non-void members, but generally the Returns()
syntax is preferred for brevity and clarity. You may still find it useful for non-voids when you want to execute a function without changing a previous return value.
var counter = 0;
calculator.Add(1, 2).Returns(3);
calculator
.When(x => x.Add(Arg.Any<int>(), Arg.Any<int>()))
.Do(x => counter++);
var result = calculator.Add(1, 2);
Assert.AreEqual(3, result);
Assert.AreEqual(1, counter);
Per argument callbacks
In cases where we only need callbacks for a particular argument we may be able to use per argument callbacks like Arg.Do()
and Arg.Invoke()
instead of When..Do
.
Argument callbacks give us slightly more concise code in a style that is more in keeping with the rest of the NSubstitute API. See Actions with arguments for more information and examples.
Callback builder for more complex callbacks
The Callback
builder lets us create more complex Do()
scenarios. We can use Callback.First()
followed by Then()
, ThenThrow()
and ThenKeepDoing()
to build chains of callbacks. We can also use Always()
and AlwaysThrow()
to specify callbacks called every time. Note that a callback set by an Always()
method will be called even if other callbacks will throw an exception.
var sub = Substitute.For<ISomething>();
var calls = new List<string>();
var counter = 0;
sub
.When(x => x.Something())
.Do(
Callback.First(x => calls.Add("1"))
.Then(x => calls.Add("2"))
.Then(x => calls.Add("3"))
.ThenKeepDoing(x => calls.Add("+"))
.AndAlways(x => counter++)
);
for (int i = 0; i < 5; i++)
{
sub.Something();
}
Assert.That(String.Concat(calls), Is.EqualTo("123++"));
Assert.That(counter, Is.EqualTo(5));