NSubstitute.Community.Instance
3.0.0
dotnet add package NSubstitute.Community.Instance --version 3.0.0
NuGet\Install-Package NSubstitute.Community.Instance -Version 3.0.0
<PackageReference Include="NSubstitute.Community.Instance" Version="3.0.0" />
paket add NSubstitute.Community.Instance --version 3.0.0
#r "nuget: NSubstitute.Community.Instance, 3.0.0"
// Install NSubstitute.Community.Instance as a Cake Addin #addin nuget:?package=NSubstitute.Community.Instance&version=3.0.0 // Install NSubstitute.Community.Instance as a Cake Tool #tool nuget:?package=NSubstitute.Community.Instance&version=3.0.0
NSubstitute.Community.Instance
This little addon to NSubstitute allows you to create instances of classes used in unit testing with all relevant dependencies automatically substituted by Substitute.For()
.
Quick Start
var service = Instance.Of<YourService>();
Motivation
Ever had to update dozens of unit tests when you added another dependency to your class? Well, this little addon to NSubstitute can help you save some time. Plus, it can help you reduce the amount of boilerplate code required to arrange your unit tests and simplify them.
Consider the following scenarios:
A) Reducing boilerplate code
Let's say you have a class with a constructor defined as follows:
public class DocumentRepository
{
public DocumentRepository(IRestClient restClient, IJwtTokenProvider tokenProvider,
IDocumentRepositoryOptions options, ILogger logger) { ... }
public TDocument GetDocument<TDocument>(int id) { ... }
}
Your typical unit test might look like this:
[Test]
public void GetDocument_ReturnsValidDocument_WhenDefaultOptionsUsed()
{
var restClient = Substitute.For<IRestClient>();
var tokenProvider = Substitute.For<IJwtTokenProvider>();
var options = Substitute.For<IDocumentRepositoryOptions>();
var logger = Substitute.For<ILogger>();
var documentRepository = new DocumentRepository(restClient, tokenProvider, options, logger);
var document = documentRepository.GetDocument<CarDetails>(32);
Assert.IsNotNull(document);
}
With Instance.Of
the arrange part of the unit test can be simplified to:
[Test]
public void GetDocument_ReturnsValidDocument_WhenDefaultOptionsUsed()
{
var documentRepository = Instance.Of<DocumentRepository>();
var document = documentRepository.GetDocument<CarDetails>(32);
Assert.IsNotNull(document);
}
B) Changing constructor signature does not break unit tests
Now you want to add more features to your DocumentRepository
and decide to add some validation. Thus you change the constructor signature to:
public DocumentRepository(IRestClient restClient, IJwtTokenProvider tokenProvider,
IDocumentRepositoryOptions options, IDocumentValidator validator, ILogger logger)
This could instantly break a lot of your unit tests code as it would not compile and you would need to spend a lot of time to fix all the mess. E.g. in the original GetDocument_ReturnsValidDocument_WhenDefaultOptionsUsed
unit test, you would need to add another line with another substitute to make the test compile. Then you would need to change the line where the instance is created.
Not so with Instance.Of
. It does all the substitution automatically for you and you do not need to add another line to your code.
The same applies when removing or reordering dependencies.
Features
Supports constructor overloading
Multiple constructors of a class are supported. The parameter matching logic will try to choose the most appropriate constructor.
Constructor parameters
You can pass optional arguments to Instance.Of
. These optional arguments will be used instead of substitutions. All other required constructor arguments will be substituted.
// Consider this constructor signature:
public DocumentRepository(IRestClient restClient, ILogger logger)
...
// Calling
var repository = Instance.Of<DocumentRepository>(restClient);
// is equivalent to
new DocumentRepository(restClient, Substitute.For<ILogger>());
Constructor parameters with default values
Constructor parameters with default values are not automatically substituted.
// Consider this constructor signature:
public DocumentRepository(IRestClient restClient, ILogger logger = null)
...
// Calling
Instance.Of<DocumentRepository>();
// is equivalent to
new DocumentRepository(Substitute.For<IRestClient>());
Null values in constructor parameters
This is currently supported by using Instance.Null<T>()
. Pasing null
will throw an exception.
// Consider this constructor signature:
public DocumentRepository(IRestClient restClient, ILogger logger)
// Calling
Instance.Of<DocumentRepository>(Instance.Null<IRestClient>());
// is equivalent to
new DocumentRepository(null, Substitute.For<ILogger>());
This is particularly useful in scenarios where constructor arguments need to be sanitised (checked for nulls) and the sanitising logic need to be tested:
// Actual contructor
public DocumentRepository(IRestClient restClient, ILogger logger)
{
_restClient = restClient ?? throw new ArgumentNullException(nameof(restClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
// And a sample unit test
[Test]
public void DocumentRepositoryConstructor_Throws_WhenLoggerNull()
{
Action action = () => Instance.Of<DocumentRepository>(Instance.Null<ILogger>());
Assert.Throws<ArgumentNullException>(action);
}
Supports non-public constructors
This is supported as long as the constructor is accessible to NSubstitute.
public class MyService
{
protected MyService(IDependency dependency) { ... }
}
// Calling
Instance.Of<MyService>();
// does not fail, it will call the protected constructor
Supports abstract classes
This is supported as long as any of abstract class constructor is accessible to NSubstitute. The actual class is proxied through Substitute.ForPartsOf
.
public abstract class MyBaseClass
{
protected MyBaseClass(IDependency dependency) { ... }
}
// Calling
Instance.Of<MyBaseClass>();
// does not fail, it will call the protected constructor with IDependency substituted
Interfaces are not supported
Instance.Of<TType>
supports only classes. Use Substitute.For<TType>
for interfaces.
Recursive constructor pattern is not supported
Creating an instance using a constructor with a parameter that takes the declaring type is not supported. Consider the following class:
public class Node
{
public Node(Node parent, int depth) { ... }
}
// Calling
Instance.Of<Node>();
// would fail with `NotSupportedException`
This is due to the fact that Instance.Of
creates substitutes for constructor parameters automatically. In this case the constructor parameter references itself and thus instantiation would cause stack overflow. To bypass this limitation, you need to pass the dependency itself or its null representation:
var parent = Instance.Null<Node>(); // or parent = new Node(null, 0);
var node = Instance.Of<Node>(parent);
Suported platforms
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- NSubstitute (>= 5.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.