# NSubstitute review: a friendlier substitute for .NET mocking

> NSubstitute replaces the arrange-act-assert ceremony of older .NET mocking libraries with a syntax built around Substitute.For and Received. It suits teams writing interface-heavy unit tests, and it is the wrong tool for classes with non-virtual members.

**nsubstitute/NSubstitute** — A friendly substitute for .NET mocking libraries.

- Repository: https://github.com/nsubstitute/NSubstitute
- Website: https://nsubstitute.github.io
- Stars: 2,978 · Forks: 281
- Language: C#
- License: NOASSERTION
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/nsubstitute-nsubstitute

## The problem NSubstitute solves for .NET test authors

Most .NET mocking libraries ask you to configure a test double before you use it. You declare what a method should return, register it, then act. NSubstitute inverts the emphasis. The README describes it as "a friendly substitute for .NET mocking libraries" and says the goal is to keep attention on the intention of the test rather than on the configuration of the test double. That is a positioning claim, but it maps to a concrete API difference: you call a method on a substitute, then chain .Returns() on the result, instead of setting up a mock object through a separate configuration call.

The intended audience is stated plainly. The README calls it "perfect for those new to testing, and for others who would just like to get their tests written with less noise and fewer lambdas." So the target is not the team with a mature, deeply abstracted mocking helper layer. It is the team writing straightforward unit tests around interfaces, where the ceremony of the mocking library is a larger share of the test than the behaviour under test.

There is a second audience that the topics list implies rather than states: .NET Core and .NET projects generally. The repository topics include dotnet-core and dotnetcore alongside c-sharp and testing-tools, and the build supports Visual Studio, Visual Studio Code or any editor with .NET support.

## How Substitute.For, Returns and Received actually work

The mechanism is a dynamic proxy. When you call Substitute.For<ICalculator>(), NSubstitute creates an instance of the interface, or of a class where the members you substitute are virtual. Calls made on that instance are recorded. Returns() attaches a return value to the call expression it is chained onto, and Received() and DidNotReceive() query the recorded calls.

That recording is the part worth understanding, because it explains both the syntax and the failure modes. The README's calculator example shows a substitute created from an interface, a return value configured by chaining .Returns(3) onto the exact call Add(1, 2), and assertions written as _calculator.Received().Add(1, 2) and _calculator.DidNotReceive().Add(5, 7). The assertion reads like the call it checks, which is the whole design idea.

Argument matching extends the same pattern to calls you cannot spell out exactly. Arg.Any<int>() matches any integer, and Arg.Is<int>(x => x < 0) matches by predicate. Returns() can also take a function, so the return value can be computed from the arguments, and it can take several arguments in sequence so successive calls return successive values. Properties follow the same shape: _calculator.Mode.Returns("DEC") sets a getter, while a plain assignment sets a read/write property. Events are raised with Raise.Event<Action>().

The diagnostic output is the most distinctive part of the mechanism. When a Received() assertion fails, NSubstitute prints the calls it did record and marks the non-matching arguments with asterisks, so a mismatch between Add(1, 2) and the recorded Add(1, 5) is visible in the exception text rather than inferred from a count.

## Installing NSubstitute from NuGet and writing a first test

The README points at the NSubstitute package on nuget.org. The optional Roslyn analysers are recommended, with NSubstitute.Analyzers.CSharp for C# projects and NSubstitute.Analyzers.VisualBasic for VB projects.

After the restore completes, the NSubstitute namespace is available in your test project. Define the interface you want to substitute, then create the substitute and configure one call:

```csharp
public interface ICalculator
{
    int Add(int a, int b);
    string Mode { get; set; }
    event Action PoweringUp;
}

_calculator = Substitute.For<ICalculator>();
_calculator.Add(1, 2).Returns(3);
Assert.That(_calculator.Add(1, 2), Is.EqualTo(3));
```

The README gives exactly this shape. The substitute is created once, the return value is attached to the specific call expression, and the assertion confirms the configured value comes back.

The first real use worth trying is the failure path, because it is where NSubstitute differs most from its peers. Write an assertion you know is wrong and read the exception:

```csharp
_calculator.Add(1, 2);
_calculator.Received().Add(5, 7);
```

The README shows the resulting ReceivedCallsException: it prints the expected call, states that no matching calls were received, then lists the non-matching calls with the differing arguments wrapped in asterisks. If you see that output, the substitute is recording calls and the analyser package is installed correctly.

## The virtual member constraint and other cases where NSubstitute is the wrong tool

The README carries a warning that deserves more weight than its placement suggests: NSubstitute will only work properly with interfaces or with virtual members of classes, and you should be careful substituting for classes with non-virtual members. This is not a configuration detail. It is a boundary on what the library can do, and it is the single most common reason a test that looks correct fails to behave as expected.

If your production code calls sealed or non-virtual methods on a concrete class, a substitute for that class cannot intercept those calls. The call runs the real implementation, and any Returns() you configured for it is ignored. The failure is quiet in the sense that the test may still run, just not against the double you thought you had. The README links to a page on creating a substitute that covers substituting infrequently and carefully for classes, which is a fair signal that this path is supported but not the intended one.

A second limitation is scope. NSubstitute is a unit-testing tool. The README's own help section points people with Entity Framework questions toward StackOverflow, noting that other libraries may be involved that the team is not as familiar with. That is a reasonable division of labour, but it means NSubstitute does not attempt to solve integration testing against a real database or a real HTTP dependency.

The build instructions add a third detail that matters if you plan to run the project's own test suite: some tests are marked [Pending] and are not meant to pass, so the README advises excluding the Pending category from test runs. That applies to contributing to NSubstitute, not to consuming it, but it is easy to misread a local build of the repository as broken.

## NSubstitute vs Moq and FakeItEasy: what actually differs

The README names two alternatives directly. Moq is described as "the original Arrange-Act-Assert mocking library for .NET, and a big source of inspiration for NSubstitute." FakeItEasy is described as "another modern mocking library for .NET," with the blunt suggestion that if you are not sold on NSubstitute's syntax, you should try FakeItEasy.

The difference in approach is where configuration lives. In the arrange-act-assert style the README attributes to Moq, the setup of a test double is a distinct phase expressed through a configuration API. NSubstitute folds that phase into the call expression itself: _calculator.Add(1, 2).Returns(3) both names the call and configures it. The practical consequence is that NSubstitute tests read as a sequence of calls rather than as a sequence of declarations about calls.

That trade has a cost. Because setup and invocation use the same syntax, a line like _calculator.Add(1, 2).Returns(3) is doing something a reader must recognise as configuration, not as an ordinary call. The README acknowledges the general risk in its own way when it introduces the Returns() overload that takes a function, calling it "possibly too much, but that's your call." A computed return value inside a test setup can hide logic that would be clearer as a small fake class.

FakeItEasy occupies similar ground: a modern .NET mocking library with its own syntax. The README does not attempt a feature comparison, and neither should a reader infer one from the README's wording. The honest summary is that all three solve the same problem with different surface syntax, and NSubstitute's own authors point at the other two as legitimate choices rather than as inferior ones.

## Maintenance, releases and the licence question

The repository is not archived. The last push was on 2026-09-14, and the most recent releases are v6.2.0 on 2026-08-11, v6.1.0 on 2026-08-08 and v6.0.0 on 2026-07-12. That is a compact run of releases, and the presence of a BreakingChanges.md file at the repository root tells you the maintainers track compatibility breaks explicitly rather than leaving them to release notes. If you adopt NSubstitute, that file is the first thing to read before a major version bump.

The build documentation is thin in a way worth flagging. The README says NSubstitute and its tests can be compiled and run using Visual Studio, Visual Studio Code or any other editor with .NET support, and links to a release procedure on the project wiki. It does not document a rollback path, and it does not give a supported-version matrix. For consumers that mostly does not matter, since the package manager handles version pinning.

On licensing, the repository metadata reports the licence as NOASSERTION, which means the automated classifier could not map the LICENSE.txt file to a known SPDX identifier. The file exists at the repository root, so the terms are stated there, but you should read LICENSE.txt yourself rather than relying on a badge or a package listing. This is not legal advice, and the practical point is narrow: before adopting a dependency in a commercial codebase, confirm the terms from the source file, not from a summary.

## Conclusion

Adopt NSubstitute for unit tests that substitute interfaces or virtual members and want less setup noise than arrange-act-assert libraries require. Do not adopt it if your code under test depends heavily on non-virtual class members, because the README warns NSubstitute only works properly with interfaces or virtual members. Before committing, install the C# analyser package, run one test that uses Received() with a deliberately wrong argument, and read the failure message to confirm it points at the argument you got wrong.

## FAQ

### What is NSubstitute in C#?

It is a .NET mocking library that creates substitute instances of interfaces and virtual members, then lets you configure return values and assert on received calls. The README describes it as a friendly substitute for .NET mocking libraries, aimed at keeping tests focused on intent rather than double configuration.

### How do you use NSubstitute?

Create a substitute with Substitute.For<ICalculator>(), configure a call by chaining .Returns(3) onto it, and assert with Received() or DidNotReceive(). Argument matching through Arg.Any<int>() and Arg.Is<int>(x => x < 0) covers calls you cannot spell out exactly.

### Is NSubstitute open source?

The project is published on GitHub under the nsubstitute organisation and is not archived. The repository metadata reports the licence as NOASSERTION, so the terms are stated in the LICENSE.txt file at the repository root rather than in a recognised SPDX identifier.

### Is NSubstitute free?

The README lists the NSubstitute package on nuget.org and the optional Roslyn analyser packages, with no paid tier or licence key mentioned. The terms themselves are in the LICENSE.txt file at the repository root, which the repository metadata does not map to a recognised SPDX identifier.

### How does NSubstitute compare to Moq in C#?

The README calls Moq the original Arrange-Act-Assert mocking library for .NET and a big source of inspiration for NSubstitute. The practical difference is that NSubstitute attaches return values to the call expression itself, while the arrange-act-assert style expresses setup as a separate configuration phase.

### Is NSubstitute a substitute for Moq?

The README presents NSubstitute as a friendly substitute for .NET mocking libraries and names Moq as the original Arrange-Act-Assert library and a big source of inspiration. The two solve the same problem with different setup syntax rather than one replacing the other by definition.

## Sources

- [Issues](https://github.com/nsubstitute/NSubstitute/issues)
- [nsubstitute/NSubstitute on GitHub](https://github.com/nsubstitute/NSubstitute)
- [Project website](https://nsubstitute.github.io)
- [README](https://github.com/nsubstitute/NSubstitute/blob/main/README.md)
- [Releases](https://github.com/nsubstitute/NSubstitute/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nsubstitute-nsubstitute
