Тестирование в C#: Unit, Integration, E2E от А до Я

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

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

Начать курс

Введение: Зачем нужно тестирование в C#?

В современной разработке на C# (.NET) качество кода — это не роскошь, а necessity. Тестирование позволяет не только отлавливать баги до попадания в продакшн, но и служит живой документацией, а также ускоряет рефакторинг. Существует три основных уровня тестирования, которые формируют «пирамиду тестов»: Unit (модульные), Integration (интеграционные) и E2E (сквозные). В этой статье мы разберем каждый из них на примерах реального кода на C#.

1. Unit-тестирование: Фундамент качества

Unit-тесты проверяют изолированный фрагмент кода (метод, класс) в отрыве от внешних зависимостей (базы данных, файловой системы, API). В экосистеме .NET популярны три фреймворка: xUnit (рекомендуется для новых проектов), NUnit и MSTest.

Основные принципы (FIRST)

  • Fast — быстрые (миллисекунды).
  • Isolated — изолированные (используем моки).
  • Repeatable — воспроизводимые.
  • Self-validating — с самопроверкой (Assert).
  • Timely — своевременные.

Пример Unit-теста с xUnit и Moq

Допустим, у нас есть сервис для расчета скидки:

public interface IDiscountService
{
    decimal CalculateDiscount(decimal price, bool isLoyaltyMember);
}

public class DiscountService : IDiscountService
{
    public decimal CalculateDiscount(decimal price, bool isLoyaltyMember)
    {
        if (isLoyaltyMember) return price * 0.9m; // 10% скидка
        return price;
    }
}

Тест с использованием Moq (для имитации зависимостей) и xUnit:

using Xunit;
using Moq;

public class DiscountServiceTests
{
    [Fact]
    public void CalculateDiscount_WhenLoyaltyMember_ReturnsDiscountedPrice()
    {
        // Arrange
        var service = new DiscountService();
        var price = 100m;

        // Act
        var result = service.CalculateDiscount(price, true);

        // Assert
        Assert.Equal(90m, result);
    }

    [Theory]
    [InlineData(100, false, 100)]
    [InlineData(200, true, 180)]
    public void CalculateDiscount_MultipleCases_ReturnsCorrectValues(decimal price, bool isLoyalty, decimal expected)
    {
        var service = new DiscountService();
        var result = service.CalculateDiscount(price, isLoyalty);
        Assert.Equal(expected, result);
    }
}

Совет: Используйте атрибут [Fact] для одиночных тестов и [Theory] + [InlineData] для параметризованных.

2. Интеграционное тестирование: Проверка связки компонентов

Интеграционные тесты проверяют, как несколько модулей работают вместе — например, контроллер, сервис и реальная база данных (или её замена, например, in-memory SQLite). В ASP.NET Core для этого используется WebApplicationFactory.

Настройка WebApplicationFactory

Создадим тестовый класс, который поднимает тестовый веб-хост:

using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using MyApp.Data;

public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            // Удаляем реальный DbContext
            var descriptor = services.SingleOrDefault(
                d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
            if (descriptor != null) services.Remove(descriptor);

            // Добавляем in-memory базу
            services.AddDbContext<AppDbContext>(options =>
            {
                options.UseInMemoryDatabase("TestDb");
            });
        });
    }
}

Теперь напишем тест для эндпоинта API:

public class ProductsApiTests : IClassFixture<CustomWebApplicationFactory>
{
    private readonly HttpClient _client;

    public ProductsApiTests(CustomWebApplicationFactory factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task GetProducts_ReturnsOkAndList()
    {
        // Act
        var response = await _client.GetAsync("/api/products");

        // Assert
        response.EnsureSuccessStatusCode();
        var content = await response.Content.ReadAsStringAsync();
        Assert.Contains("product", content.ToLower());
    }
}

Важно: Интеграционные тесты медленнее unit-тестов, поэтому их количество должно быть меньше. Они проверяют «склейку» между слоями.

3. E2E-тестирование: Симуляция пользователя

Сквозные (End-to-End) тесты имитируют действия реального пользователя в браузере. Для C# самым популярным инструментом является Selenium WebDriver (или более современный Playwright).

Пример E2E теста с Selenium

Установите пакет Selenium.WebDriver и Selenium.WebDriver.ChromeDriver. Сценарий: открыть страницу логина, ввести данные и проверить, что пользователь перенаправлен на главную.

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using Xunit;

public class LoginE2ETests
{
    [Fact]
    public void Login_WithValidCredentials_RedirectsToDashboard()
    {
        using var driver = new ChromeDriver();
        try
        {
            // Arrange
            driver.Navigate().GoToUrl("https://localhost:5001/login");
            driver.FindElement(By.Id("Email")).SendKeys("user@test.com");
            driver.FindElement(By.Id("Password")).SendKeys("Password123!");
            driver.FindElement(By.CssSelector("button[type='submit']")).Click();

            // Assert
            Assert.Contains("dashboard", driver.Url.ToLower());
        }
        finally
        {
            driver.Quit();
        }
    }
}

Рекомендация: Для надежности используйте явные ожидания (WebDriverWait), чтобы избежать ложных падений из-за асинхронности UI.

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.Url.Contains("dashboard"));

Заключение: Строим сбалансированную стратегию

Идеальная стратегия тестирования в C# — это пирамида: 70% unit-тестов (быстрые и дешевые), 20% интеграционных (провер

Блоги

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