Тестирование в C#: Unit, Integration и E2E тесты для .NET проектов

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Тестирование в 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 — современный фреймворк от Microsoft
  • Selenium 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                

Блоги

Рекомендаций по книгам