Тестирование в C#: Unit, Integration и E2E — полное руководство для .NET разработчика
Качество программного обеспечения начинается с тестов. В экосистеме C# (.NET) существует устоявшаяся культура тестирования, которая включает три ключевых уровня: модульные (unit), интеграционные (integration) и сквозные (E2E) тесты. В этой статье мы разберем каждый уровень на практических примерах, покажем лучшие библиотеки и расскажем, как выстроить эффективную стратегию тестирования для вашего .NET проекта.
1. Unit-тестирование в C#: основа качества кода
Unit-тесты (модульные тесты) проверяют работу отдельных компонентов программы — методов, классов, функций — в изоляции от внешних зависимостей. В .NET основными фреймворками являются xUnit, NUnit и MSTest. Самым популярным считается xUnit благодаря своей гибкости и интеграции с .NET Core.
Библиотеки для unit-тестов:
xUnit— основной фреймворк (рекомендуется Microsoft)Moq— создание mock-объектов для изоляции зависимостейFluentAssertions— улучшенные assertion-выраженияAutoFixture— автоматическая генерация тестовых данных
Пример unit-теста на xUnit с 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);
}
}
Советы по unit-тестам:
- Следуйте шаблону AAA (Arrange, Act, Assert)
- Один тест — одна проверка (один assert)
- Используйте
[Theory]с[InlineData]для параметризации - Не тестируйте внешние сервисы — используйте моки
2. Интеграционное тестирование: проверка взаимодействия компонентов
Интеграционные тесты проверяют, как различные модули системы работают вместе: база данных, файловая система, внешние API. В отличие от unit-тестов, здесь мы используем реальные или близкие к реальным зависимости.
Инструменты для интеграционного тестирования в .NET:
Microsoft.AspNetCore.TestHost— встраиваемый веб-серверTestContainers— контейнеризация БД (PostgreSQL, SQL Server, Redis)Respawn— очистка базы данных между тестамиWebApplicationFactory<T>— создание тестового хоста
Пример интеграционного теста с TestContainers и 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("test")
.WithPassword("test123")
.Build();
}
public async Task InitializeAsync()
{
await _dbContainer.StartAsync();
_factory = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.UseSetting("ConnectionStrings:DefaultConnection",
_dbContainer.GetConnectionString());
});
}
[Fact]
public async Task GetUsers_WhenCalled_ReturnsOkStatus()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync("/api/users");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
public async Task DisposeAsync()
{
await _dbContainer.StopAsync();
_factory?.Dispose();
}
}
Важные моменты:
- Используйте
IAsyncLifetimeдля управления контейнерами - Каждый тест должен работать с изолированными данными
- Не злоупотребляйте интеграционными тестами — они медленнее unit
3. E2E-тестирование: сквозная проверка системы
End-to-End (E2E) тесты проверяют работу всей системы целиком — от пользовательского интерфейса до базы данных. В контексте веб-приложений .NET это часто означает тестирование через браузер с помощью Selenium, Playwright или Puppeteer.
Инструменты для E2E в .NET:
Playwright for .NET— современный фреймворк от MicrosoftSelenium WebDriver— классика, но медленнееPuppeteerSharp— управление Chrome headless
Пример E2E теста с Playwright:
using Microsoft.Playwright;
using Xunit;
public class LoginE2ETests
{
[Fact]
public async Task Login_WithValidCredentials_RedirectsToDashboard()
{
// Arrange
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = true
});
var page = await browser.NewPageAsync();
await page.GotoAsync("https://localhost:5001/login");
// Act
await page.FillAsync("input[name='email']", "user@example.com");
await page.FillAsync("input[name='password']", "SecurePass123!");
await page.ClickAsync("button[type='submit']");
// Assert
await page.WaitForURLAsync("**/dashboard");
var title = await page