Mocking and Test Doubles in .NET
Mocking is the practice of replacing real dependencies with controlled substitutes during testing. A mock intercepts calls to a dependency, lets you define return values and exceptions per method, and records invocations for later assertion. The broader family of techniques is called “test doubles” and includes dummies, stubs, fakes, spies, and mocks proper. In everyday .NET usage, the terms overlap and “mock” covers most of them.
The .NET mocking landscape has shifted considerably over the past few years. Moq dominated for nearly a decade, then lost community trust in 2023 after a privacy incident tied to its SponsorLink dependency. NSubstitute stepped in as the practical default for new projects, built on the same Castle.DynamicProxy foundation that powers most dynamic mocking in .NET. More recently, TUnit.Mocks introduced a source-generated alternative that removes runtime IL (Intermediate Language) emission entirely, making mocks compatible with NativeAOT and trimming-heavy deployments.
Key Concepts
Setup and verification. A mock setup defines what a method returns or throws for a given set of argument matchers. Verification confirms that a method was called, how many times, and with what arguments. Most .NET mocking APIs separate these into distinct fluent calls; TUnit.Mocks chains them onto the same setup expression.
Argument matchers. Instead of matching exact values, matchers let you accept any argument (Arg.Any<T>()), match by predicate (Arg.Is<T>(x => x > 0)), or enforce equality explicitly. Matchers make test arrange blocks resilient to incidental argument changes.
Strict vs. loose behavior. Loose mocks (the default in NSubstitute and TUnit.Mocks) return default values for unconfigured calls. Strict mocks throw on any unconfigured call, which forces explicit setup for every interaction and catches accidental coupling early.
Libraries Covered
- NSubstitute: The current de-facto standard for .NET mocking. Clean API, built on
Castle.DynamicProxy. Not compatible with NativeAOT. - TUnit.Mocks: Source-generated mocks that ship with the TUnit framework. AOT-compatible, no runtime IL emission, statically typed setups.
- Moq: Historically dominant. Now a maintenance burden on most teams following the 2023 SponsorLink incident.
Related Topics
Mocking is closely related to Testing practices and Source Generators in the context of compile-time mock generation.

TUnit.Mocks: No Castle, No Reflection, No Drama
NSubstitute and Castle.DynamicProxy. That foundation is cracking: NativeAOT breaks it, trimming strips it, cold-start costs accumulate. TUnit.Mocks takes a different route: a source generator emits typed mocks at compile time, IService.Mock() (or Mock.Of<T>()) is the entry point, runtime reflection is gone.