unit test что это такое

Что такое юнит-тесты и почему они так важны

Бывает, кодишь 10 минут, а дебажишь 2 часа. Чтобы такого не случилось, пилите юнит-тесты. Михаил Фесенко рассказал, как их правильно готовить.

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

Фесенко Михаил, можно просто Фес. Разработчик, раньше работал системным администратором, пишет на чём скажут, но пока писал на PHP, Go, Python, Bash. Сейчас работает в «Яндекс.Облаке», до этого работал во «ВКонтакте». Любит жену, кино и снимать видео =)

Юнит-тест (unit test), или модульный тест, — это программа, которая проверяет работу небольшой части кода. Разработчики регулярно обновляют сайты и приложения, добавляют фичи, рефакторят код и вносят правки, а затем проверяют, как всё работает.

Тестировать систему целиком после каждого обновления — довольно муторно и неэффективно. Поэтому обновлённые или исправленные части кода прогоняют через юнит-тесты.

Особенности юнит-тестов

На практике используют разные тесты — их разделяют по уровню абстракции с помощью пирамиды Майка Кона :

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

Чем выше тест в пирамиде, тем больше частей программы он затрагивает. Высокоуровневые тесты «ближе к бизнесу»: они проверяют бизнес-логику и пользовательские процессы. А те, что внизу пирамиды, помогают найти проблемы в отдельных частях кода. Например, какую-нибудь функцию, которая генерирует имя файла.

В отличие от них, юнит-тесты нужны в следующих случаях:

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

Процесс юнит-тестирования

Для юнит-тестирования подключают тестовые фреймворки — они позволяют «мокать», то есть имитировать функции. В коде больших проектов много зависимостей: одна функция вызывает другую и влияет на разные части программы. Но, как правило, достаточно проверить функции «в вакууме», отдельно от остального кода. Для этого и нужен тестовый фреймворк — он моделирует условия, в которых функция А вызывает функцию Б изолированно от других функций.

Простой пример: у нас есть функция на Go, которая получает id бэкапа и возвращает имя бэкап-файла:

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

Протестируем её с помощью набора входных и выходных данных. Они должны учитывать все ситуации, поэтому не забываем про негативные кейсы — когда программа возвращает ошибку. Вот набор тестовых данных:

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

В первую очередь я прописал запрещённые данные (-1 и 0) и слишком большое значение (10200300). Когда пользователь их вводит, функция не должна возвращать результат. Вместо этого мы ждём сообщения об ошибке: BAD_ID или BACKUP_ID_TOO_BIG. Когда же функция получает валидный id, она выводит отформатированное имя файла, например Backup#000010.

А вот и код самого теста:

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

Порой код для тестирования даже больше основного — и это норма. Но иногда всё-таки стоит задуматься, на самом ли деле тест должен быть таким объёмным. Я бы посоветовал покрывать тестами только те фрагменты кода, которые вы планируете менять. Или сложные части, которые, скорее всего, придётся чинить или поддерживать.

Некоторые разработчики мокают всё подряд. Из-за этого тесты становятся хрупкими, а код — сложным и непонятным. На самом деле для юнит-тестирования достаточно лишь немного переписать код, а огромные функции лучше разбить на более мелкие.

В старой хорошей книге «Экстремальное программирование» есть классная мысль: сначала пишите тест, а только потом программу. Это клёвый подход, но не все могут так делать (а кто-то просто не хочет тратить время).

Как покрыть код юнит-тестами

Есть разработчики, которые не проводят модульное тестирование: «Ой, у нас большой проект, и переписать 1000 строк под тесты или замокать их — слишком запарно». На самом деле покрыть код тестами несложно. Вот несколько советов.

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

Есть более жёсткий принцип: новый код без тестов на ревью не принимается. Конечно, он работает, если сроки не горят, — иначе программист рефакторит или покрывает его тестами позже.

Используйте тестовый фреймворк. В тестировании не нужно изобретать велосипед. Для популярных языков уже есть готовые решения, поэтому достаточно вбить в поиске test frameworks, и вы получите целый список. Вот, например, результат для Python:

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

Пишите простые тесты. Надо понимать, что происходит с входными данными и какой результат должна вернуть функция. Если непонятно — меняем нейминг и разбиваем функции на более мелкие, избавляемся от зависимостей. Пусть одна функция принимает результат, а другая возвращает. Так проще тестировать.

Допустим, у нас есть такая функция:

Её не нужно прогонять через юнит-тест, потому что тогда придётся мокать process_a, process_b и prepare_output. Тут нужен интеграционный тест, который проверит, как эти компоненты взаимодействуют между собой. Вообще, если код сложно покрывать юнит-тестами, используйте интеграционные — они проверяют общую работу системы, модуля или библиотеки.

Не забывайте про негативные тесты. Это the best practice. Что произойдёт, если передать в программу неправильные данные? Какую ошибку она выведет и выведет ли?

Покрывайте тестами все циклы и if-else. Этот совет касается кода, который нужно поддерживать. Если ему не следовать, на одной из итераций правок вы или ваш коллега просто всё сломаете.

Проверяйте качество тестов. Сделать это поможет мутационное тестирование. Мутационный фреймворк случайно меняет константы и значения в условных операторах и циклах, создаёт копию кода, в которой поочерёдно меняет условия. Например, было >= или было COUNT=3, а стало COUNT=10. Каждая замена тестируется: если код поменялся, а тесты не упали, значит, код не покрыт тестами.

На мутационное тестирование уходит много времени. Можно подключить плагин, который считает code coverage по тесту и выдаёт отчёт. Например, у нас покрыто тестами 43 тысячи строк кода, а 10 тысяч — нет. Значит, code coverage 81%. Но тут важен не только сам процент, но и качество — какие именно фрагменты кода и какими именно тестами покрыты. Например, не всё может быть под юнит-тестами — часть может перекрываться интеграционными.

Обеспечьте достаточный процент покрытия кода. Года три-четыре назад я был фанатиком стопроцентного покрытия. Конечно, безумно круто, когда ты всегда знаешь, что именно сломалось. Но в продакшне этого добиться сложно — да и не нужно. Исключение — маленькие проекты или «жёсткие» команды, для которых полное покрытие в приоритете.

На самом деле, code coverage в 70–90% — уже крутой показатель, но и меньше 70% — тоже плохо. И ещё важный момент: новый код не должен понижать уровень code coverage.

Проверить code coverage можно с помощью coveralls.io:

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

Coveralls принимает результаты тестов и выдаёт отчёт: показывает процент покрытия и как он изменился с последнего теста.

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такое

Не делайте хрупкие тесты. Если тест нестабильный и регулярно падает, его называют хрупким. Его результат может зависеть от дня недели, времени суток, чётности или нечётности запуска. Бывает, две функции работают параллельно и на итоговый результат влияет то, какая из них закончит выполняться первой. Такие функции лучше разбивать на несколько простых и тестировать по отдельности. Мокайте всё что нужно, чтобы сделать тест управляемым, но не переборщите — иначе код будет сложно поддерживать.

Допустим, мы написали юнит-тесты для двух функций. Но не учли, что первая функция сохраняет данные в глобалке, а вторая из-за этого меняет своё поведение. В результате первый тест проходит нормально, а второй падает или ведёт себя странно. А всё потому, что мы не сбросили состояние глобальной переменной.

Следите за скоростью тестов. Тесты должны работать быстро. Если они проверяют кусок кода 10–15 минут — разработчики устанут ждать и отключат их нафиг. Поэтому регулярно проверяйте скорость, ищите узкие места и оптимизируйте тесты. Если есть проблемы, подключитесь через дебаггер — возможно, основной код плохо оптимизирован и искать проблему нужно в продакшне.

Преимущества юнит-тестов

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

Упрощают работу — находят ошибки, которые вы можете не заметить (меня это много раз спасало). Например, меняешь одну строчку, чтобы поправить логи, а ломается весь код. Благодаря тестам я узнавал об этом ещё до продакшна.

Понятно документируют код. Если вам неочевидно, как работает та или иная функция, можно пройти дальше по коду или открыть юнит-тест. По нему сразу видно, какие параметры принимает функция и что отдаёт после выполнения. Это упрощает жизнь тем, кто работает с чужим кодом.

Помогают ничего не сломать при рефакторинге. Бывает, что код написан непонятно и ты не можешь его отрефакторить, потому что наверняка что-то сломаешь в продакшне. А с тестами код можно смело рефакторить.

Упрощают разработку. Кажется, что юнит-тесты всё усложняют, ведь нужно написать в два раз больше кода — не только функцию, но и тест к ней. Но я много раз убеждался: когда пишешь код без тестов, потом тратишь гораздо больше времени на поиск и исправление ошибок.

Бывает, бац-бац — и в продакшн, а потом понеслось: исправляешь код первый, второй, третий раз. И постоянно вспоминаешь, как тестировать его вручную. У меня даже были файлики с входными данными для таких проверок. Тогда я тестировал программы вручную, по бумажке, и тратил на это уйму времени. А если бы написал юнит-тест, нашёл бы эти баги сразу и не переписывал код по несколько раз.

В коммерческой разработке без юнит-тестов никуда

Сейчас в коммерческой разработке без тестов почти не работают — а в большинстве компаний от разработчиков даже требуют покрывать код юнит-тестами. Везде, где я работал в последние несколько лет, тоже было такое правило. Ведь если в команде кто-то факапит, то может развалиться вся работа — а тестирование как раз защищает от краха.

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

Чтобы лучше понять юнит-тесты, изучите тестовые фреймворки вашего языка. А потом найдите крупные open-source-проекты, которые их используют, и посмотрите, как они работают. Можно даже скачать проект и поиграть с тестами, чтобы глубже погрузиться в тему.

Чтобы познать тонкости разработки и тестирования приложений, лучше сразу учиться у практикующих профессионалов. Приходите в университет Skillbox, выбирайте курс и осваивайте программирование под присмотром экспертов.

обложка: Oli Scarff / Staff / GettyImages

Источник

Анатомия юнит-теста

Эта статья является конспектом книги «Принципы юнит-тестирования». Материал статьи посвящен структуре юнит-теста.

В этой статье рассмотрим структуру типичного юнит-теста, которая обычно описывается паттерном AAA (arrange, act, assert — подготовка, действие и проверка). Затронем именование юнит-тестов. Автор книги описал распространенные советы по именованию и показал, почему он несогласен с ними и привел альтернативы.

Структура юнит-теста

Согласно паттерну AAA (или 3А) каждый тест разбивается на три части: arrange (подготовка), act (действие) и assert (проверка). Возьмем для примера класс Calculator с методом для вычисления суммы двух чисел:

unit test что это такое. Смотреть фото unit test что это такое. Смотреть картинку unit test что это такое. Картинка про unit test что это такое. Фото unit test что это такоеРис. 1 – Листинг теста для проверки поведения класса по схеме ААА

Паттерн AAA предоставляет простую единообразную структуру для всех тестов в проекте. Это единообразие дает большое преимущество: привыкнув к нему, вы сможете легко прочитать и понять любой тест. Структура теста выглядит так:

в секции подготовки тестируемая система (system under test, SUT) и ее зависимости приводятся в нужное состояние;

в секции действия вызываются методы SUT, передаются подготовленные зависимости и сохраняется выходное значение (если оно есть);

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

Как правило, написание теста начинается с секции подготовки (arrange), так как она предшествует двум другим. Но начинать писать тесты можно также и с секции проверки (assert). Если практиковать разработку через тестирование (TDD), то вы еще не знаете всего о поведении этой функциональности. Возможно, будет полезно сначала описать, чего вы ожидаете от поведения, а уже затем разбираться, как создать систему для удовлетворения этих ожиданий. В этом случае сначала мы думаем о цели: что разрабатываемая функциональность должна делать для нас. А затем начинаем решать задачу. Но следует еще раз подчеркнуть, что эта рекомендация применима только в том случае, когда практикуется TDD. Если вы пишете основной код до кода тестов, то к тому моменту, когда вы доберетесь до теста, вы уже знаете, чего ожидать от поведения, и лучше будет начать с секции подготовки.

Есть еще паттерн «Given-When-Then», похожем на AAA. Этот паттерн также рекомендует разбить тест на три части:

Given — соответствует секции подготовки (arrange);

When — соответствует секции действия (act);

Then — соответствует секции проверки (assert)

В отношении построения теста эти два паттерна ничем не отличаются. Единственное отличие заключается в том, что структура «Given-When-Then» более понятна для непрограммиста. Таким образом, она лучше подойдет для тестов, которые вы собираетесь показывать людям, не имеющим технической подготовки.

Избегайте множественных секций arrange, act и assert

Иногда встречаются тесты с несколькими секциями arrange (подготовка), act (действие) или assert (проверка).

Когда присутствует несколько секций действий, разделенных секциями проверки и, возможно, секциями подготовки, это означает, что тест проверяет несколько единиц поведения. Такой тест уже не является юнит-тестом — это интеграционный тест. Такой структуры тестов лучше избегать. Если вы видите тест, содержащий серию действий и проверок, отрефакторите его: выделите каждое действие в отдельный тест.

Интеграционным тестом называется тест, который не удовлетворяет хотя бы одному из следующих критериев: проверяет одну единицу поведения, делает это быстро и в изоляции от других тестов.

Например, тест, который обращается к совместной зависимости — скажем, базе данных, — не может выполняться в изоляции от других тестов. Изменение состояния базы данных одним тестом приведет к изменению результатов всех остальных тестов, зависящих от той же базы и выполняемых параллельно.

Иногда допустимо иметь несколько секций действий в интеграционных тестах. Интеграционные тесты могут быть медленными. Один из способов ускорить их заключается в том, чтобы сгруппировать несколько интеграционных тестов в один с несколькими секциями действий и проверок. Это особенно хорошо ложится на конечные автоматы (state machines), где состояния системы перетекают из одного в другое и когда одна секция действий одновременно служит секцией подготовки для следующей секции действий.

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

Избегайте команд if в тестах

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

Присутствие команды if означает, что тест проверяет слишком много всего. Следовательно, такой тест должен быть разбит на несколько тестов. Но в отличие от ситуации с множественными секциями AAA, здесь нет исключений для интеграционных тестов. Ветвление в тестах не дает ничего, кроме дополнительных затрат на сопровождение: команды if затрудняют чтение и понимание тестов.

Насколько большой должна быть каждая секция?

Насколько большой должна быть каждая секция? И как насчет завершающей (teardown) секции — той, что должна «прибирать» после каждого теста?

Секция подготовки обычно является самой большой из трех. Если она становится слишком большой, лучше выделить отдельные операции подготовки либо в приватные методы того же класса теста, либо в отдельный класс-фабрику. Есть несколько популярных паттернов, которые помогут организовать переиспользование кода в секциях подготовки: «Мать объектов» (Object Mother) и «Построитель тестовых данных» (Test Data Builder).

Секция действия обычно состоит всего из одной строки кода. Если действие состоит из двух и более строк, это может указывать на проблемы с API тестируемой системы. Этот пункт лучше продемонстрировать на примере, в котором клиент совершает покупку в интернет-магазине.

Обратите внимание: секция действия (act) в этом тесте состоит из вызова одного метода, что является признаком хорошо спроектированного API класса. Теперь сравним ее с версией, в которой секция действия состоит из двух строк. Это признак проблемы с API тестируемой системы: он требует, чтобы клиент помнил о необходимости второго вызова метода для завершения покупки, следовательно, тестируемая система недостаточно инкапсулирована.

В новой версии клиент сначала пытается приобрести пять единиц товара в магазине. Затем товар удаляется со склада. Удаление происходит только в том случае, если предшествующий вызов Purchase() завершился успехом.

Недостаток новой версии заключается в том, что она требует двух вызовов для выполнения одной операции. Следует заметить, что это не является проблемой самого теста. Тест проверяет ту же единицу поведения: процесс покупки. Проблема кроется в API класса Customer. Он не должен требовать от клиента дополнительного вызова. Если клиентский код вызывает первый метод, но не вызывает второй; в этом случае клиент получит товар, но количество товара на складе при этом не уменьшится.

Такое нарушение логической целостности называется нарушением инварианта (invariant violation). Защита кода от потенциальных нарушений инвариантов называется инкапсуляцией (encapsulation). Когда нарушение логической целостности проникает в базу данных, оно становится серьезной проблемой; теперь не удастся сбросить состояние приложения простым перезапуском. Придется разбираться с поврежденными данными в базе и, возможно, связываться с клиентами и решать проблему с каждым из них по отдельности.

Проблема решается поддержанием инкапсуляции кода. В предыдущих примерах удаление запрашиваемого товара со склада должно быть частью метода Purchase. Сustomer не должен полагаться на то, что клиентский код сделает это сам, вызвав метод store.RemoveInventory. Когда речь заходит о поддержании инвариантов в системе, вы должны устранить любую потенциальную возможность нарушить эти инварианты.

Эти рекомендации применимы к большинству кода, содержащего бизнес-логику. Иногда это правило можно нарушить в служебном или инфраструктурном коде. Тем не менее следует анализировать каждый такой случай на возможные нарушения инкапсуляции.

Сколько проверок должна содержать секция проверки?

Так как под «юнитом» в юнит-тестировании понимается единица поведения, а не единица кода, то одна единица поведения может приводить к нескольким результатам. Проверять все эти результаты в одном тесте вполне нормально.

Тем не менее, если секция проверки получается слишком большой: это может быть признаком того, что в коде недостает какой-то абстракции. Например, вместо того чтобы по отдельности проверять все свойства объекта, возвращенного тестируемой системой, возможно, будет лучше добавить методы проверки равенства (equality members) в класс такого объекта. После этого объект можно будет сравнивать с ожидаемым значением всего одной командой.

Нужна ли завершающая (teardown) фаза?

Иногда еще выделяют четвертую — завершающую (teardown) — секцию, которая следует после остальных. Например, в завершающей секции можно удалить любые файлы, созданные в ходе теста, закрыть подключение к базе данных и т. д. Завершение обычно представляется отдельным методом, который переиспользуется всеми тестами в классе. По этой причине автор книги не включил эту фазу в паттерн AAA.

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

Переиспользование тестовых данных между тестами

Важно понимать, как и когда переиспользовать код между тестами. Переиспользование кода между секциями подготовки — хороший способ сокращения и упрощения ваших тестов.

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

Первый (неправильный) способ переиспользования тестовых данных — инициализация их в конструкторе теста. Такой подход позволяет значительно сократить объем кода в тестах — вы можете избавиться от большинства (или даже от всех) конфигураций в тестах. Однако у этого подхода есть два серьезных недостатка.

Он создает сильную связность (high coupling) между тестами. Изменение логики подготовки одного теста повлияет на все тесты в классе. Тем самым нарушается важное правило: изменение одного теста не должно влиять на другие тесты. Чтобы следовать этому правилу, необходимо избегать совместного состояния (shared state) в классах тестов.

Другой недостаток выделения кода подготовки в конструктор — ухудшение читаемости теста. С таким конструктором просмотр самого теста больше не дает полной картины. Чтобы понять, что делает тест, приходится смотреть в два места: сам тест и конструктор тест-класса.

Второй (правильный) способ — написать фабричные методы, как показано ниже.

Выделяя общий код инициализации в приватные фабричные методы, можно сократить код теста с сохранением полного контекста того, что происходит в этом тесте. Более того, приватные методы не связывают тесты друг с другом, при условии, что они достаточно гибкие (то есть позволите тестам указать, как должны создаваться тестовые данные).

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

Именование юнит-тестов

Правильное именование помогает понять, что проверяет тест и как работает система. Как же выбрать имя для юнит-теста? Есть много рекомендаций на эту тему. Одна из самых распространенных (и, пожалуй, одна из наименее полезных по мнению автора книги) рекомендаций выглядит так:

Такая схема неоптимальная, так как она заставляет сосредоточиться на деталях имплементации вместо поведения системы. Простые фразы гораздо лучше подходят для этой задачи. Простыми фразами можно описать поведение системы в формулировках, которые будут понятны всем, в том числе заказчику или аналитику:

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

Это верно, но только до определенной степени. Непонятные названия создают дополнительную когнитивную нагрузку для всех, независимо от того, программисты они или нет. На первый взгляд это не кажется серьезной проблемой, но эта когнитивная нагрузка дает о себе знать в долгосрочной перспективе. Все это медленно, но верно увеличивает затраты на сопровождение тестов.

Рекомендации по именованию юнит-тестов

Не следуйте жесткой структуре именования тестов. Высокоуровневое описание сложного поведения не удастся втиснуть в узкие рамки такой структуры. Сохраняйте свободу самовыражения.

Выбирайте имя теста так, словно вы описываете сценарий непрограммисту, знакомому с предметной областью задачи (например, бизнес-аналитику).

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

Также обратите внимание на то, что, хотя автор использует паттерн [ИмяКласса]Tests при выборе имен классов тестов, это не означает, что тесты ограничиваются проверкой только этого класса. Юнитом в юнит-тестировании является единица поведения, а не класс. Единица поведения может охватывать один или несколько классов. Рассматривайте класс в [ИмяКласса]Tests как точку входа — API, при помощи которого можно проверить единицу поведения.

Вывод

Все юнит-тесты должны строиться по схеме AAA: подготовка (Arrange), действие (Act), проверка (Assert). Если тест состоит из нескольких секций подготовки, действий или проверки, это указывает на то, что тест проверяет сразу несколько единиц поведения. Если этот тест — юнит-тест, разбейте его на несколько тестов: по одному для каждого действия.

Секция действия, содержащая более одной строки, — признак проблем с API тестируемой системы. Клиент должен не забывать выполнять эти действия совместно, чтобы не привести к нарушению логической целостности. Такие нарушения называются нарушениями инвариантов.

Переиспользование кода инициализации тестовых данных должно осуществляться с помощью фабричных методов (вместо конструктора тест-класса). Такой подход поддерживает изоляцию между тестами и улучшает читаемость.

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *