NSubstitute: Substitutes, Returns & Verification
NSubstitute is a mocking library for .NET, providing a friendly, minimal-syntax API for creating substitute (mock/fake) implementations of interfaces and virtual class members. It serves the same purpose as Moq -- isolating a class under test from real dependencies -- with a design emphasizing direct, natural-reading syntax over expression-based setups.
Creating a Substitute & Configuring Returns
// Immediately gives you an IUserRepository -- no .Object-style
// unwrapping step needed
var repo = Substitute.For<IUserRepository>();
// Calling the method directly on the substitute, followed by
// .Returns() -- no .Setup() wrapper needed, unlike Moq
repo.GetUserById(1).Returns(new User { Id = 1, Name = "Alice" });
var service = new UserService(repo);
var user = service.GetUser(1);
Assert.Equal("Alice", user.Name);Argument Matching
// Any positive int matches -- returns defaultUser regardless of
// the specific id passed in
repo.GetUserById(Arg.Any<int>()).Returns(defaultUser);
// Arg.Is<T>() matches against a predicate, not one exact hardcoded value
repo.GetUserById(Arg.Is<int>(id => id > 0)).Returns(validUser);
// Property-based matching for object arguments -- checks VALUES,
// not reference identity (important since the code under test likely
// constructs a NEW object to pass in)
repo.Received().Save(Arg.Is<User>(u => u.Name == "Alice" && u.Age == 30));Verifying Interactions
service.SaveUser(newUser);
// Reads like a natural-language assertion
repo.Received().Save(newUser);
// Exact call count -- e.g. confirming a retry happened exactly 3 times
retryHandler.Received(3).Execute(Arg.Any<Action>());
// Verifying an ABSENCE of interaction is just as testable --
// catches a regression where a future change accidentally introduces
// an unwanted call
emailService.DidNotReceive().SendWelcomeEmail(Arg.Any<string>());Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free