Moq: Async Mocks, Callbacks & Strict Mode
Mocking Async Methods
// .ReturnsAsync() is shorthand for .Returns(Task.FromResult(value)),
// tailored for mocking Task<T>-returning async methods
mockRepo.Setup(r => r.GetUserByIdAsync(1))
.ReturnsAsync(new User { Id = 1, Name = "Alice" });
var user = await service.GetUserAsync(1);Capturing Arguments with .Callback()
User savedUser = null;
// Callback() runs custom code on each call -- useful for capturing
// the actual arguments for later, more detailed assertions
mockRepo.Setup(r => r.Save(It.IsAny<User>()))
.Callback<User>(u => savedUser = u);
service.SaveUser(new User { Name = "Bob" });
Assert.Equal("Bob", savedUser.Name);MockBehavior.Strict vs. Loose
// Loose (default) -- an unconfigured member call returns a default
// value (null, 0, empty collection) instead of throwing
var looseMock = new Mock<IUserRepository>();
// Strict -- calling any member WITHOUT an explicit Setup() throws,
// surfacing unexpected/unaccounted-for interactions more aggressively
var strictMock = new Mock<IUserRepository>(MockBehavior.Strict);Why Interfaces (Not Sealed Classes)
Moq generates a runtime dynamic proxy that inherits from (or implements) the mocked type to intercept calls. A sealed class can't be inherited from at all in .NET, so it can't be mocked directly -- code that needs to be mockable should depend on an interface (IPaymentProcessor) rather than a sealed concrete class (PaymentProcessor). For a plain class, only virtual members can be mocked, since Moq's proxy needs to override them.
MockRepository for Multiple Related Mocks
var repo = new MockRepository(MockBehavior.Strict);
var mockUserRepo = repo.Create<IUserRepository>();
var mockEmailService = repo.Create<IEmailService>();
// ... configure and exercise both mocks ...
// Verifies every setup across BOTH mocks in one call
repo.VerifyAll();Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free