Testing in C#: Unit, Integration and E2E — a complete guide for .NET developers
Software quality starts with tests. In the C# (.NET) ecosystem, there is an established testing culture that includes three key levels: unit, integration, and E2E tests. In this article, we will break down each level with practical examples, show the best libraries, and explain how to build an effective testing strategy for your .NET project.
1. Unit testing in C#: the foundation of code quality
Unit tests verify the operation of individual program components — methods, classes, functions — in isolation from external dependencies. In .NET, the main frameworks are xUnit, NUnit, and MSTest. The most popular is xUnit due to its flexibility and integration with .NET Core.
Libraries for unit tests:
xUnit— main framework (recommended by Microsoft)Moq— creating mock objects for dependency isolationFluentAssertions— improved assertion expressionsAutoFixture— automatic test data generation
Example of a unit test with xUnit and Moq:
using Xunit;using Moq;using FluentAssertions;
public class OrderServiceTests{ [Fact] public void CalculateTotal_WithValidItems_ReturnsCorrectSum() { // Arrange var mockRepo = new Mock<IProductRepository>(); mockRepo.Setup(r => r.GetPrice(1)).Returns(100m); mockRepo.Setup(r => r.GetPrice(2)).Returns(50m);
var service = new OrderService(mockRepo.Object); var items = new List<OrderItem> { new OrderItem { ProductId = 1, Quantity = 2 }, new OrderItem { ProductId = 2, Quantity = 1 } };
// Act var total = service.CalculateTotal(items);
// Assert total.Should().Be(250m); mockRepo.Verify(r => r.GetPrice(1), Times.Once); mockRepo.Verify(r => r.GetPrice(2), Times.Once); }}Tips for unit tests:
- Follow the AAA pattern (Arrange, Act, Assert)
- One test — one check (one assert)
- Use
[Theory]with[InlineData]for parameterization - Do not test external services — use mocks
2. Integration testing: verifying component interaction
Integration tests verify how different system modules work together: database, file system, external APIs. Unlike unit tests, here we use real or near-real dependencies.
Tools for integration testing in .NET:
Microsoft.AspNetCore.TestHost— embeddable web serverTestContainers— database containerization (PostgreSQL, SQL Server, Redis)Respawn— database cleanup between testsWebApplicationFactory<T>— creating a test host
Example of an integration test with TestContainers and WebApplicationFactory:
using Microsoft.AspNetCore.Mvc.Testing;using Testcontainers.PostgreSql;using Xunit;
public class UserApiIntegrationTests : IAsyncLifetime{ private readonly PostgreSqlContainer _dbContainer; private WebApplicationFactory<Program> _factory;
public UserApiIntegrationTests() { _dbContainer = new PostgreSqlBuilder() .WithImage("postgres:15") .WithDatabase("testdb") .WithUsername("tes