Где провести границу между юнит-тестами и интеграционными тестами

· Обновлено: · · Тестирование, Юнит-тесты, Интеграционные тесты, Проектирование тестов, Разработка Windows, C# / .NET

История изменений (5 обновлений, последнее 30 Aug 2026)

Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.

Переведены имена методов в примерах xUnit. Утверждения статьи не менялись.
Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
В начало статьи добавлен раздел «Карта знаний этой статьи». Понятия из текста и связи между ними собраны в краткое изложение, схему и ссылку на страницу сведений. Утверждения статьи не менялись.
Текст обновлён по результатам внешнего ревью (1283 замечания). Содержание отдельных правок — в записях ниже.
В начале явно сказано, что статья исходит из C# и .NET, а примеры кода — на xUnit. Добавлены минимальные примеры юнит-теста и интеграционного теста, таблица видов тестовых двойников (stub, mock, fake) и связь с формулировкой «нужно 7 mock-ов». Отдельно зафиксировано, как эта статья трактует E2E.
Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.21619801)

Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.

Го Комура (2026). Где провести границу между юнит-тестами и интеграционными тестами. KomuraSoft LLC. https://comcomponent.com/ru/blog/2026/03/25/004-unit-test-vs-integration-test-boundary-guide/

DOI (зарегистрированный архив)
10.5281/zenodo.21619801
DOI (последняя зарегистрированная версия)
10.5281/zenodo.21619802

В разговорах о проектировании тестов каждый раз тихо сложно одно и то же: сколько запихивать в юнит-тесты и с какого момента поднимать проверку в интеграционные.

Опасно здесь два края:

  • хочется быстрого цикла — и всё становится юнит-тестами
  • хочется близости к реальности — и всё становится интеграционными тестами

Первое обрастает mock-ами и легко пропускает как раз те места, что ломаются в проде; второе обычно даёт медленный и хрупкий набор тестов. На практике оси, на которые стоит смотреть, заметно яснее.

  • Вы хотите проверить свою логику или соединение с внешним миром?
  • Если подставить in-memory fake, смысл проверки не пропадает?
  • Поведение БД / файлов / HTTP / DI / конфигурации / фреймворка / ОС — это и есть предмет проверки?
  • Нужно быстро прогнать много входных вариантов?

Как только эти четыре пункта видны, границу между юнит-тестами и интеграционными тестами провести гораздо проще.

Статья исходит из проектирования автотестов на C# / .NET. Примеры кода — на xUnit, но сами критерии от фреймворка не зависят. Тот же ход мысли читается и в JUnit, и в pytest.

Содержание опирается на то, что на март 2026 года доступно в Microsoft Learn: «Integration tests in ASP.NET Core»1, «Unit testing best practices for .NET»2 и «The Practical Test Pyramid» Мартина Фаулера3. Дальше, чтобы ссылки не сводились к одним номерам, в тексте называем источники по имени. URL первоисточников собраны в справочных материалах в конце статьи.

1. Сначала вывод

Грубо, но так, чтобы этим можно было пользоваться:

  1. Чистая логика — в юнит-тесты
  2. Соединения, связка, преобразования и различия среды — в интеграционные тесты
  3. Если проверить можно и там, и там — начинайте с юнит-теста
  4. Интеграционные тесты лучше не раздувать вширь и в тяжесть, а сужать до границы

Одной фразой: юнит-тесты — это «тесты решений», интеграционные тесты — «тесты соединений».

Расчёт суммы, переходы состояний, проверка ввода, условия одобрения, классификация исключений — то, чей смысл завершается без внешних ресурсов, — быстрее, устойчивее и позволяет плотнее гонять входные варианты, если сдвинуть их в юнит-тесты. С другой стороны, выполнение SQL, сериализация JSON / CSV, маршрутизация, привязка модели, регистрация в DI, файловые блокировки, права доступа, регистрация COM, 32-bit / 64-bit, STA / MTA — то, что «подводит в момент соединения», — безопаснее держать на стороне интеграционных тестов.

В материале Microsoft Learn Integration tests in ASP.NET Core тоже советуют сужать интеграционные тесты до важных инфраструктурных сценариев и выбирать юнит-тесты там, где их достаточно.

На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 18, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle

2. Что в этой статье называется юнит-тестом и интеграционным тестом

Термины здесь такие.

Уровень Что проверяет Типичная конфигурация
Юнит-тест Корректность одной изолированной обязанности fake / mock / stub, внешние ресурсы отсечены
Интеграционный тест Соединения нескольких компонентов и поведение, в котором участвуют инфраструктура и фреймворки Реальная БД, реальные файлы, реальный serializer, реальный host, реальный pipeline и т. п.
E2E / функциональный тест Пользовательский поток через всё приложение Развёрнутое приложение, несколько сервисов, реальный браузер или реальный процесс

В материалах .NET по юнит-тестированию хороший юнит-тест описывают как fast / isolated / repeatable — без зависимости от внешних факторов вроде файловой системы или БД. Это ясно разобрано в Unit testing best practices for .NET.

Кроме того, интеграционный тест — это не только «тяжёлый тест, который непременно ходит в другой процесс или на другой сервер». Даже внутри одного процесса, если вы соединяете несколько реальных компонентов и проверяете настоящее поведение фреймворка или инфраструктуры, это уже ближе к интеграционному тесту.

Например, при юнит-тесте controller action в ASP.NET Core официальная рекомендация — сузить предмет до решений внутри самого action, а взаимодействие с фреймворком вроде routing, model binding, filters отдать интеграционным тестам. Подробный разбор есть в Unit test controller logic in ASP.NET Core.

2.1. Чем отличаются fake / mock / stub

В таблице выше стоят fake / mock / stub рядом, но это не одно и то же. Как виды тестовых двойников (заменителей для теста) в этой статье они значат следующее. Разделение совпадает с тем, как его проводит Мартин Фаулер в «Mocks Aren’t Stubs».4

Название Что делает Типичное место
stub Заменитель, который только возвращает заранее заданное значение. Как его вызвали — не проверяют Когда нужно зафиксировать вход, например «остаток всегда 3»
mock Заменитель, у которого проверяют сам вызов. Assert на число вызовов и аргументы Когда нужно убедиться, что сохранение вызвали ровно один раз
fake То же поведение, что у настоящего объекта, но лёгкая реализация In-memory репозиторий, файловое хранилище во временном каталоге

Грубо: stub подменяет вход, mock проверяет вызов, fake — упрощённая реализация. Это различие начинает работать в главе 4. Признак «нужно 7 mock-ов» точнее значит семь объектов, у которых проверяют вызовы, и это сигнал, что тест смотрит не на одно решение, а на то, как соединены несколько частей.

2.2. Как эта статья трактует E2E

Тема статьи — именно граница между юнит-тестами и интеграционными тестами. E2E / функциональные тесты стоят в таблице выше для контраста; в тексте их глубоко не разбираем.

По месту в пирамиде тестов: чем ниже слой, тем больше тестов и тем они быстрее; чем выше — тем меньше и тем они медленнее. Исходная постановка — у Мартина Фаулера в «The Practical Test Pyramid».3 То есть юнит-тесты как основание, интеграционные — по границам, E2E — только на основные потоки. Конкретная раскладка — в трёхслойной схеме главы 7.

3. Таблица решений на одном листе

Сначала — таблица, которой на практике удобнее всего пользоваться.

Что хотите проверить Основной тест Заметка
Расчёт суммы, скидки, переходы состояний, проверка ввода Юнит-тест Хочется плотно гонять входные варианты
Классификация исключений, выбор текста ошибки, решение — повторять ли попытку Юнит-тест Смысл завершается без реального I/O
Преобразование SQL / ORM в Repository, transaction Интеграционный тест Предмет — поведение реальной БД или реального provider
serialize / deserialize JSON / XML / CSV Интеграционный тест Расхождение wire format через fake почти не видно
Маршрутизация, привязка модели, фильтры, middleware Интеграционный тест Проверка соединения с фреймворком
Переходы состояний ViewModel или Presenter в WPF / WinForms Юнит-тест Смысл есть и без поднятого UI
Настоящий Binding, Dispatcher, control lifecycle, message loop Интеграционный тест или UI-тест Предмет — поведение фреймворка и потоков
Пути к файлам, права доступа, блокировки, общие папки, символы конца строки, кодировки Интеграционный тест Нужно реальное поведение ОС и файловой системы
Регистрация COM, 32-bit / 64-bit, STA / MTA, откуда грузится DLL Интеграционный тест Предмет — различия среды и границы процессов
Запуск приложения целиком, сквозная проверка основных сценариев E2E / smoke Число тестов может быть небольшим

Секрет чтения таблицы — спрашивать, какой тест ближе всего к «причине, по которой это ломается в проде». Решать по той неопределённости, которую хотите снизить, а не по тому, где лежит код, — так меньше плывёт.

4. Что должно жить в юнит-тестах

Юнит-тестам подходит обязанность, у которой смысл остаётся, даже если внешний мир убрать.

Например:

  • бизнес-правила
  • ветвления
  • переходы состояний
  • проверка ввода
  • классификация ошибок
  • решение о политике повторов
  • изменение состояния ViewModel / Presenter
  • сама логика преобразования

Особенно чем больше комбинаций, тем выше цена сдвига в юнит-тесты.

Например,

  • купон есть / нет
  • товар в наличии / нет
  • первый заказ / повторный
  • администратор / обычный пользователь
  • нормальное значение / граничное / некорректное

чем больше таких условий, тем тяжелее гонять их все интеграционными тестами. Здесь разумнее нарезать мелко юнит-тестами.

Кроме того, в юнит-тестах важно держать внешние факторы под контролем.

  • Текущее время внедрять
  • GUID и случайные числа делать подменяемыми
  • Не ждать через sleep
  • Не трогать реальную БД и реальные файлы
  • Не выходить в реальную сеть

Если это соблюдается, тесты заметно стабильнее.

4.1. Когда в юнит-тесте слишком много mock-ов

Если при попытке написать юнит-тест оказывается, что

  • нужно 7 mock-ов
  • setup длинный
  • arrange длиннее самого тела теста
  • непонятно, что хотели проверить

обычно верно одно из двух:

  1. у класса слишком много обязанностей
  2. в юнит-тест затолкнули связку, которую на самом деле должен проверять интеграционный тест

Mock — инструмент, чтобы отсечь внешний мир, а не инструмент, который доказывает, что соединение с настоящим компонентом работает. Если это перепутать, легко получить «всё green, а в проде падает».

4.2. Минимальный пример юнит-теста

На одних словах это абстрактно, поэтому поставим по одному примеру кода на каждую сторону границы. Сначала юнит-тест. Переход состояния ViewModel завершается по смыслу и без UI, поэтому это территория юнит-теста.

// .NET 8 / xUnit
// Объект: ViewModel, который вообще не трогает внешние ресурсы
public sealed class OrderViewModel
{
    public decimal Subtotal { get; set; }
    public bool IsMember { get; set; }

    public bool CanCheckout => Subtotal > 0m;
    public decimal Total => IsMember ? Subtotal * 0.9m : Subtotal;
}

public class OrderViewModelTests
{
    [Fact]
    public void УЧленаПрименяетсяСкидка10Процентов()
    {
        var viewModel = new OrderViewModel { Subtotal = 1000m, IsMember = true };

        Assert.Equal(900m, viewModel.Total);
    }

    [Theory]
    [InlineData(0, false)]
    [InlineData(1, true)]
    public void ПриНулевомПодытогеНельзяОформитьЗаказ(int subtotal, bool expected)
    {
        var viewModel = new OrderViewModel { Subtotal = subtotal };

        Assert.Equal(expected, viewModel.CanCheckout);
    }
}

Здесь нет ни БД, ни файлов, ни HTTP. Поэтому тесты быстрые, их можно гонять параллельно, а [Theory] позволяет сколько угодно наращивать входные варианты. «Полный перебор ветвлений сдвигайте сюда» на практике выглядит именно так.

5. Четыре границы, которые стоит поднимать в интеграционные тесты

Места, которые стоит поднимать в интеграционные тесты, в целом складываются в четыре: формат, связка, среда, время.

5.1. Граница форматов

В формат здесь входят такие вещи:

  • JSON / XML / CSV
  • schema и mapping БД
  • nullable / precision / timezone
  • сериализация enum и дат
  • кодировки и BOM
  • символы конца строки

Мартин Фаулер тоже относит границы, где есть serialize / deserialize, к кандидатам на интеграционный тест. Подробнее — в The Practical Test Pyramid.

Например,

  • у DTO после сериализации в JSON оказались другие имена полей
  • в CSV сломались кавычки или переносы строк
  • decimal округлился
  • в БД поехало обращение с DateTimeOffset
  • null и пустая строка обработались не так, как ждали

такие дефекты юнит-тестами легко пропустить.

5.2. Граница связки

На границу связки попадают, например, такие части:

  • регистрация в DI
  • bind конфигурации
  • маршрутизация
  • привязка модели
  • фильтры
  • middleware
  • запуск host
  • связка событий
  • Binding и подключение command в WPF

Здесь предмет не «верна ли моя функция», а правильно ли соединены несколько реальных частей.

В ASP.NET Core официально: юнит-тест controller action сужают до решений самого action, а routing, model binding и filters смотрят интеграционными тестами. Вне веба ход тот же: в десктопном приложении переходы состояний ViewModel — юнит-тесты, поведение с настоящим XAML Binding или Dispatcher — уже ближе к интеграционным.

5.3. Граница среды

В разработке под Windows этот пункт особенно важен.

  • права на файлы
  • общие папки
  • блокировки файлов
  • rename из временного файла
  • права администратора
  • права на запуск службы
  • регистрация COM
  • 32-bit / 64-bit
  • STA / MTA
  • откуда грузится DLL

Здесь главные действующие лица — сами условия ОС и среды выполнения. На in-memory fake смысл сильно падает, поэтому безопаснее закрывать такие случаи интеграционными тестами.

Особенно в конфигурациях с существующим Windows-ПО или COM / ActiveX обычное дело — споткнуться раньше логики, на регистрации, bitness, модели потоков и правах доступа. Такие сбои подбирает не юнит-тест, а интеграционный тест, который включает среду.

5.4. Граница времени

Ещё одно, что легко упустить, — время и параллелизм.

  • timeout
  • cancellation
  • реальное поведение retry
  • обработка по timer
  • остановка фоновой работы
  • race condition
  • порядок завершения при shutdown

Здесь важно отделять решение от реального поведения.

Например,

  • сколько раз повторять попытку
  • какие исключения считать поводом для повтора

достаточно проверить юнит-тестами. А вот

  • действительно ли срабатывает timeout
  • распространяется ли cancellation
  • не ломается ли всё, когда timer сталкивается с асинхронной обработкой
  • аккуратно ли закрываются handle и задачи при завершении

ближе к интеграционным тестам.

5.5. Минимальный пример интеграционного теста

Тот же сюжет, что в 4.2, но с другой стороны границы. Здесь проверяем: «сохранённая сумма не ломается, даже если сходить в БД и вернуться». Проход идёт через настоящий файл SQLite.

// .NET 8 / xUnit / Microsoft.Data.Sqlite
using System.Globalization;
using Microsoft.Data.Sqlite;

public sealed class OrderRepository(SqliteConnection connection)
{
    public void Save(int id, decimal total)
    {
        using var command = connection.CreateCommand();
        command.CommandText = "INSERT INTO orders (id, total) VALUES ($id, $total);";
        command.Parameters.AddWithValue("$id", id);
        command.Parameters.AddWithValue("$total", total.ToString(CultureInfo.InvariantCulture));
        command.ExecuteNonQuery();
    }

    public decimal FindTotal(int id)
    {
        using var command = connection.CreateCommand();
        command.CommandText = "SELECT total FROM orders WHERE id = $id;";
        command.Parameters.AddWithValue("$id", id);
        var stored = (string)command.ExecuteScalar()!;
        return decimal.Parse(stored, CultureInfo.InvariantCulture);
    }
}

public sealed class OrderRepositoryTests : IDisposable
{
    private readonly string _databasePath =
        Path.Combine(Path.GetTempPath(), $"orders-{Guid.NewGuid():N}.db");
    private readonly SqliteConnection _connection;

    public OrderRepositoryTests()
    {
        // Без Pooling=False файл БД иногда не удаётся удалить при зачистке
        _connection = new SqliteConnection($"Data Source={_databasePath};Pooling=False");
        _connection.Open();

        using var create = _connection.CreateCommand();
        create.CommandText = "CREATE TABLE orders (id INTEGER PRIMARY KEY, total TEXT NOT NULL);";
        create.ExecuteNonQuery();
    }

    [Fact]
    public void СохраненнаяСуммаЧитаетсяОбратноБезОкругления()
    {
        var repository = new OrderRepository(_connection);

        repository.Save(id: 1, total: 1234.56m);

        Assert.Equal(1234.56m, repository.FindTotal(1));
    }

    public void Dispose()
    {
        _connection.Dispose();
        File.Delete(_databasePath);
    }
}

Этот тест проверяет не ветвления OrderRepository. Он проверяет соединение: проходит ли SQL, сходится ли тип столбца с decimal, сохраняется ли значение после круга туда-обратно. У SQLite нет типа decimal, поэтому «в каком типе сохранять, чтобы значение не ломалось» — уже не вопрос реализации, а вопрос проектирования соединения. На in-memory fake этого не видно.

В примере каждый тестовый класс поднимает один временный файл БД и удаляет его в Dispose. У интеграционного теста есть состояние, поэтому каждый раз явно решать, где создать и где выбросить, важнее, чем в юнит-тесте.

6. Частые ошибки в решении

6.1. Замокать Repository и на этом успокоиться

Даже если всё вокруг Repository проходит через mock, вы всё равно не знаете,

  • корректен ли SQL
  • срабатывает ли transaction
  • совпадает ли это со schema
  • не расходится ли mapping
  • не ломаются ли кодировка и precision

Repository чаще не объект тестирования логики, а точка соединения на границе. Тогда реальности лучше соответствует сместить вес с юнит-тестов на интеграционные.

6.2. В юнит-тесте controller / endpoint пытаться увидеть ещё и фреймворк

В юнит-тесте controller action смотреть стоит примерно на это:

  • условные ветвления
  • выбор возвращаемого значения
  • какой из зависимых сервисов вызывается

А вот

  • попадает ли route
  • проходит ли model binding
  • срабатывает ли filter
  • как выглядит результат после middleware

уже сторона интеграционных тестов. Если это смешать, становится трудно понять, что именно сломалось.

6.3. Полный перебор входных вариантов интеграционными тестами

Интеграционные тесты ближе к реальности и потому неизбежно медленнее. Поэтому выгоднее разделить: полный перебор ветвлений — юнит-тесты, представительные случаи на границе — интеграционные.

В разборе интеграционных тестов на Microsoft Learn тоже советуют для БД и файловой системы не гонять все шаблоны интеграционными тестами, а сужать до представительных сценариев вроде read / write / update / delete.

6.4. Из CI ходить напрямую в прод внешних сервисов

Этого лучше избегать.

В интеграционных тестах важна «похожесть на настоящее», но это не значит, что каждый раз нужно бить в прод SaaS или прод API. Фаулер тоже советует поднимать внешние сервисы локально, ставить fake или пользоваться выделенным test instance.

На практике удобно сочетать:

  • локальную БД
  • временные каталоги
  • test host
  • выделенную test environment
  • fake service с зафиксированным контрактом

7. Рекомендуемая структура на практике

Абсолютно правильного соотношения нет. Но достаточно универсально работает такая трёхслойная схема.

Слой Основной инструмент Что туда класть
Слой ядра Толстый набор юнит-тестов Бизнес-правила, переходы состояний, проверка ввода, классификация ошибок
Слой границ Узкие интеграционные тесты БД, файлы, HTTP, serializer, DI, конфигурация, COM, права доступа
Слой целого Небольшое число smoke / E2E Проверка запуска, основные потоки, чтобы серьёзный сбой не повторился

Интуитивно: плотнее числом становятся юнит-тесты, плотнее покрытием границ — интеграционные.

Рекомендуемый порядок такой.

  1. Сначала перечислить границы приложения
  2. Привести логику к виду, который можно отделить от внешнего мира
  3. На каждой границе поставить «минимум один happy path» и «типичный failure path»
  4. Сквозные прогоны целого сузить по числу
  5. Когда вылез баг, добавить тест на тот слой, где этот баг воспроизводится с наименьшими затратами

Пятый пункт — самый важный.

  • Ошибка правила — добавьте юнит-тест
  • Ошибка SQL / binding / конфигурации / прав доступа / регистрации — добавьте интеграционный тест
  • Сбой, в котором участвуют запуск или распространение приложения, — добавьте smoke или E2E

При таком росте обязанности тестов меньше плывут.

8. Пять вопросов на случай сомнений

В конце — пять вопросов для проверки, когда неясно.

  1. Если подставить in-memory fake, смысл, который хотели проверить, остаётся?
    • Если остаётся — это ближе к юнит-тесту.
  2. Если что-то сломается, подозревать будете скорее соединение или настройку, а не логику?
    • Если да — это ближе к интеграционному тесту.
  3. Предмет проверки — не БД / файлы / serializer / DI / route / model binding / ОС / права доступа / bitness / thread?
    • Если да — это ближе к интеграционному тесту.
  4. Нужно быстро прогнать много входных вариантов?
    • Если да — это ближе к юнит-тесту.
  5. Когда этот тест падает, сразу ли понятно, что чинить?
    • Если нет — слои тестов смешаны.

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

9. Итог

Границу между юнит-тестами и интеграционными тестами практичнее всего проводить не по месту кода, а по тому, какую неопределённость вы хотите снизить.

Суть сводится к этим пяти пунктам.

  • Юнит-тесты — тесты решений
  • Интеграционные тесты — тесты соединений
  • Полный перебор ветвлений — в юнит-тесты
  • Формат, связка, среда и время — в интеграционные тесты
  • Сквозную проверку целого закрывайте небольшим числом smoke / E2E

Больше всего стоит избегать трёх вещей:

  • считать, что mock доказал корректность соединения с настоящим компонентом
  • пытаться прогнать все ветвления интеграционными тестами
  • смешивать обязанности юнит-тестов и интеграционных тестов

Если сомневаетесь, сначала спросите: этот дефект ломает «решение» или ломает «соединение»? Одного этого вопроса хватает для значительной части случаев.

10. Похожие статьи

11. Справочные материалы

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Как разделить юнит-тесты и интеграционные тесты?
Одной фразой: юнит-тесты — это тесты решений, интеграционные тесты — тесты соединений. Расчёт суммы, переходы состояний, проверка ввода, классификация исключений — то, чей смысл завершается без внешних ресурсов, — сдвигайте в юнит-тесты. То, что «подводит в момент соединения» — выполнение SQL, сериализация JSON/CSV, маршрутизация, регистрация в DI, файловые блокировки, права доступа, регистрация COM, 32-bit/64-bit — держите на стороне интеграционных тестов. Если проверить можно и там, и там, начинайте с юнит-теста.
Что стоит поднимать в интеграционные тесты?
В целом это четыре границы: формат, связка, среда и время. Формат — JSON/CSV, mapping БД, кодировки. Связка — как реальные части соединены: регистрация в DI, маршрутизация, привязка модели. Среда — реальное поведение ОС: права на файлы, регистрация COM, 32-bit/64-bit, STA/MTA. Время — timeout, cancellation, race condition. На in-memory fake смысл этих проверок сильно падает, поэтому безопаснее закрывать их интеграционными тестами.
Почему плохо, если в юнит-тесте слишком много mock-ов?
Если нужно 7 mock-ов, setup длинный, непонятно, что вообще хотели проверить, — либо у класса слишком много обязанностей, либо в юнит-тест затолкнули связку, которую на самом деле должен проверять интеграционный тест. Mock — инструмент, чтобы отсечь внешний мир, а не доказательство, что соединение с настоящим компонентом работает. Если это перепутать, легко получить «всё green, а в проде падает».
Какую структуру и соотношение тестов держать?
Универсально работает трёхслойная схема. Слой ядра держит бизнес-правила и переходы состояний толстым набором юнит-тестов; слой границ — узкие интеграционные тесты на БД, файлы, serializer, DI; слой целого — запуск и основные потоки небольшим числом smoke/E2E. Полный перебор ветвлений гоняйте юнит-тестами; интеграционные тесты сужайте до минимум одного happy path и типичного failure path на каждой границе. Когда вылез баг, тест добавляйте на тот слой, где его дешевле всего воспроизвести.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог