- 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
Compatibility argument matchers
NSubstitute argument matchers depend on having C# 7.0 or later (as of NSubstitute 4.0). This lets them be used with out
and ref
parameters, but it also means that if you are stuck on an earlier version of C# you may get an error like the following when trying to use a matcher like Arg.Is(123)
:
CS7085: By-reference return type 'ref T' is not supported.
If you have C# 7.0-compatible tooling installed you can set <LangVersion />
in your test csproj file to 7
or higher, or to latest
or default
.
Stuck with pre-7.0 tooling? Then use Arg.Compat
instead of Arg
, or use CompatArg
in the NSubstitute.Compatibility
namespace. Arg.Compat
will work everywhere Arg
does, with the exception of matching out
and ref
args. For example, if the documentation mentions Arg.Is(42)
, you can instead use Arg.Compat.Is(42)
. CompatArg
is a bit trickier to setup, but may make migrating between Arg
and Arg.Compat
easier in some cases. Both options are described below.
Using Arg.Compat
The simplest work-around if you are stuck on pre-C#7 is to use Arg.Compat.
wherever you would normally use Arg.
. To migrate existing code, replace all instances of Arg.
with Arg.Compat.
.
calculator.Add(1, -10);
// Instead of `Arg.Is<int>(x => x < 0)`, use:
calculator.Received().Add(1, Arg.Compat.Is<int>(x => x < 0));
// Instead of `Arg.Any<int>()`, use:
calculator
.Received()
.Add(1, Arg.Compat.Any<int>());
// Same for Returns and DidNotReceive:
calculator.Add(Arg.Compat.Any<int>(), Arg.Compat.Is(42)).Returns(123);
calculator.DidNotReceive().Add(Arg.Compat.Is<int>(x => x > 10), -10);
Using NSubstitute.Compatibility.CompatArg
If you have a project with lots of existing arg matchers then migrating to Arg.Compat
can require a lot of code changes. A smaller change is to instead use an instance of the CompatArg
class in the NSubstitute.Compatibility
namespace. This approach may also make it easier to upgrade to the newer Arg
matchers in future.
[TestFixture]
public class SampleCompatArgFixture {
// Declare Arg field. Any existing `Arg` references will now go via `CompatArg`, instead
// of the new `Arg` type that is incompatible with older C# compilers.
private static readonly NSubstitute.Compatibility.CompatArg Arg = NSubstitute.Compatibility.CompatArg.Instance;
[Test]
public void DemonstrationOfCompatArgs() {
var calculator = Substitute.For<ICalculator>();
calculator.Add(1, -10);
// Arg.Is will now go via CompatArg. It is equivalent to Arg.Compat.Is.
calculator.Received().Add(1, Arg.Is<int>(x => x < 0));
}
}
This works particularly well if a common test base class is used.
public class BaseTestFixture {
// Declare Arg field. Any existing `Arg` references will now go via `CompatArg`, instead
// of the new `Arg` type that is incompatible with older C# compilers.
protected static readonly NSubstitute.Compatibility.CompatArg Arg = NSubstitute.Compatibility.CompatArg.Instance;
}
If you are later able to update the C# compiler your project is using, you can remove the CompatArg
field and all Arg
references will go through standard arg matchers (and you’ll now be able to use them with out
and ref
parameters!).
Argument matchers in expression trees
As of NSubstitute 4.0 argument matchers can no longer be used in expression trees. Doing so will cause a compilation error:
CS8153: An expression tree lambda may not contain a call to a method, property, or indexer that returns by reference
The Arg.Compat
matchers can be used to work around this issue. They do not return by reference so they are fine to use in expression trees.
public interface IFoo { void DoStuff(int i); }
[Test]
public void Sample() {
var sub = Substitute.For<IFoo>();
sub.DoStuff(42);
// If `specify` takes an `Expression<Action>` argument, this will fail with CS8153:
// specify(() => sub.Received().DoStuff(Arg.Any<int>()));
// Instead use compat matcher:
specify(() => sub.Received().DoStuff(Arg.Compat.Any<int>()));
// Or re-write without expression tree if possible:
sub.Received().DoStuff(Arg.Any<int>());
}