NSubstitute
02 / 02

NSubstitute: Exceptions, Async & Dynamic Returns

NSubstitute: Exceptions, Async & Dynamic Returns

Simulating Failures

// .Throws() configures the call to raise an exception instead of
// returning normally -- tests error-handling without a real failing dependency
repo.Save(Arg.Any<User>()).Throws(new InvalidOperationException());

var result = service.TrySaveUser(newUser);
Assert.False(result.Success);

Async Methods

// Modern NSubstitute often infers Task<T> wrapping automatically --
// no manual Task.FromResult() needed
repo.GetUserByIdAsync(1).Returns(new User { Id = 1 });

var user = await service.GetUserAsync(1);

Dynamic & Sequential Return Values

// The returned value depends on the actual argument passed in --
// more flexible than one fixed static return value
repo.GetUserById(Arg.Any<int>())
    .Returns(x => new User { Id = x.Arg<int>() });

// Different return value on each successive call -- simulating a
// retry mechanism where the first two attempts fail, third succeeds
service.Attempt().Returns(false, false, true);

Why Interfaces (Same Constraint as Moq)

Like other .NET mocking libraries, NSubstitute generates a runtime dynamic proxy to intercept calls -- this only works for members that CAN be overridden (interface members always are; virtual/abstract class members are). A class depending on an interface (IPaymentGateway) rather than a sealed concrete class stays mockable this way.

NSubstitute vs. Moq

Both libraries solve the identical problem with comparable feature sets. Moq uses an expression-based API (mock.Setup(x => x.Method())); NSubstitute favors calling methods directly on the substitute (substitute.Method().Returns(...)). The choice often comes down to team syntax preference rather than a strong technical reason to prefer one over the other.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free