C# tutorials > Testing and Debugging > Unit Testing > Understanding test doubles (mocks, stubs, fakes)
Understanding test doubles (mocks, stubs, fakes)
What are Test Doubles?
Stubs: Providing Predefined Responses
Stub Example
public interface IOrderService
{
Order GetOrder(int orderId);
}
public class OrderServiceStub : IOrderService
{
public Order GetOrder(int orderId)
{
// Predefined Order for testing purposes
return new Order { OrderId = orderId, CustomerName = "Test Customer", TotalAmount = 100 };
}
}
public class OrderProcessor
{
private readonly IOrderService _orderService;
public OrderProcessor(IOrderService orderService)
{
_orderService = orderService;
}
public decimal CalculateDiscountedPrice(int orderId)
{
var order = _orderService.GetOrder(orderId);
if (order != null && order.TotalAmount > 50)
{
return order.TotalAmount * 0.9m; // 10% discount
}
return order.TotalAmount;
}
}
Mocks: Verifying Interactions
Mock Example
public interface IEmailService
{
void SendEmail(string to, string subject, string body);
}
public class OrderConfirmationService
{
private readonly IEmailService _emailService;
public OrderConfirmationService(IEmailService emailService)
{
_emailService = emailService;
}
public void ConfirmOrder(Order order)
{
// Process the order (simplified for the example)
// Send order confirmation email
_emailService.SendEmail(order.CustomerEmail, "Order Confirmation", "Your order has been confirmed.");
}
}
Mock Example (using Moq)
// Using Moq NuGet Package
using Moq;
using NUnit.Framework;
[TestFixture]
public class OrderConfirmationServiceTests
{
[Test]
public void ConfirmOrder_SendsConfirmationEmail()
{
// Arrange
var mockEmailService = new Mock<IEmailService>();
var orderConfirmationService = new OrderConfirmationService(mockEmailService.Object);
var order = new Order { CustomerEmail = "test@example.com" };
// Act
orderConfirmationService.ConfirmOrder(order);
// Assert
mockEmailService.Verify(x => x.SendEmail("test@example.com", "Order Confirmation", "Your order has been confirmed."), Times.Once);
}
}
Fakes: Simplified Implementations
Fake Example (In-Memory Repository)
public interface IProductRepository
{
Product GetProduct(int productId);
void SaveProduct(Product product);
}
public class InMemoryProductRepository : IProductRepository
{
private readonly Dictionary<int, Product> _products = new Dictionary<int, Product>();
public Product GetProduct(int productId)
{
if (_products.ContainsKey(productId))
{
return _products[productId];
}
return null;
}
public void SaveProduct(Product product)
{
_products[product.ProductId] = product;
}
}
public class ProductService
{
private readonly IProductRepository _productRepository;
public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public void UpdateProductName(int productId, string newName)
{
var product = _productRepository.GetProduct(productId);
if (product != null)
{
product.Name = newName;
_productRepository.SaveProduct(product);
}
}
}
When to Use Each Type
Real-Life Use Case Section
Best Practices
Interview Tip
Memory Footprint
Alternatives
Pros of Using Test Doubles
Cons of Using Test Doubles
FAQ
-
What is the difference between a stub and a mock?
A stub provides predefined responses to method calls, while a mock verifies that specific interactions occur between the SUT and its dependencies. Stubs focus on providing data, while mocks focus on verifying behavior. -
When should I use a mocking framework?
Use a mocking framework when you need to create and manage mocks with complex behaviors or when you need to verify interactions with dependencies. Mocking frameworks simplify the process of creating mocks and reduce boilerplate code. -
Are test doubles only useful for unit testing?
Test doubles are primarily used in unit testing to isolate the code being tested. While less common, they can occasionally be adapted for integration testing in specific scenarios where controlling external dependencies is beneficial for certain test cases. However, the primary value is in focused unit tests.