Mocking Frameworks (Moq, NSubstitute, FakeItEasy)
Isolate your code from external dependencies like databases and APIs by replacing them with controlled test doubles.
What You'll Learn
- • Create mocks with Moq — Setup, Returns, Verify
- • Use argument matchers, callbacks, and sequential returns
- • Compare Moq with NSubstitute's cleaner syntax
- • Verify call counts and interaction patterns
🎭 Real-World Analogy
Mocking is like using a stunt double in a movie. When the scene involves jumping off a building, you don't risk the actual actor — you use a stand-in that looks the same but behaves in a controlled way. In testing, mocks replace real databases, APIs, and email services with controlled substitutes, so you can test your code's logic in isolation.
Moq Basics — Setup, Returns & Verify
Moq is the most widely used .NET mocking library. Create a Mock<T>, set up return values with .Setup().ReturnsAsync(), and verify calls with .Verify().
Moq — Setup, Returns & Verify
Mock repositories and email services to test UserService in isolation.
using Moq;
using Xunit;
// Interfaces to mock
public interface IUserRepository
{
Task<User?> GetByIdAsync(int id);
Task<List<User>> GetAllAsync();
Task<bool> ExistsAsync(string email);
Task AddAsync(User user);
}
public interface IEmailService
{
Task SendWelcomeEmailAsync(string email, string name);
Task SendPasswordResetAsync(string email, string token);
}
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string
...Advanced Moq — Matchers, Callbacks & Sequences
Use It.IsAny, It.Is, and It.IsInRange for flexible argument matching. Callbacks capture arguments, and SetupSequence returns different values on successive calls.
Advanced Moq Patterns
Argument matching, callbacks, sequential returns, and verification patterns.
using Moq;
using Xunit;
public class AdvancedMoqTests
{
// Argument matching — flexible setup
[Fact]
public async Task ArgumentMatching_FlexibleSetup()
{
var mockRepo = new Mock<IUserRepository>();
// Match ANY integer
mockRepo.Setup(r => r.GetByIdAsync(It.IsAny<int>()))
.ReturnsAsync(new User { Name = "Default" });
// Match specific range
mockRepo.Setup(r => r.GetByIdAsync(It.IsInRange(1, 100, Moq.Range.Inclusive)))
...NSubstitute — A Cleaner Alternative
NSubstitute offers a more readable syntax — no .Object, no Setup(). Just call methods directly on the substitute. Great for teams that prefer minimal ceremony.
NSubstitute — Clean Mocking Syntax
Compare NSubstitute's natural-language style with Moq's Setup/Verify pattern.
using NSubstitute;
using Xunit;
// NSubstitute — cleaner syntax alternative to Moq
public class NSubstituteTests
{
[Fact]
public async Task NSubstitute_CleanerSyntax()
{
// Create substitute (no .Object needed)
var repo = Substitute.For<IUserRepository>();
var email = Substitute.For<IEmailService>();
// Setup returns — reads like natural language
repo.GetByIdAsync(1).Returns(new User { Id = 1, Name = "Alice" });
repo.ExistsAsync("new@
...Pro Tip
Enable MockBehavior.Strict in Moq to throw exceptions for any unexpected call: new Mock<IRepo>(MockBehavior.Strict). This catches unintended interactions but requires you to set up every expected call explicitly.
Common Mistakes
- • Over-mocking — mocking too many layers makes tests brittle and hard to understand
- • Not verifying interactions — your test passes but the service never called the expected method
- • Mocking concrete classes — always mock interfaces; concrete mocks require virtual methods
Lesson Complete!
You've mastered mocking with Moq and NSubstitute. Next, apply Test-Driven Development to build features from tests up.
Sign up for free to track which lessons you've completed and get learning reminders.