Что такое модульное тестирование?

Суть

Разработка ИТ-продукта — поэтапный процесс; все начинается с идеи, создания концепта, далее этап дизайна, далее написание кода, затем тестирование, и передача готового приложения заказчикам/клиентам/пользователям. 

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

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

Как правило, юнит-тесты пишут разработчики; в идеале — создатель тестируемого юнита (модуля, компонента). В современной разработке, с непрерывным развертыванием и доставкой, этот процесс более или менее автоматизирован; система не примет юнит с багами (возвращает его разработчику). Ручное юнит-тестирование также распространено.

Юнит-тестирование по объему/количеству тестов составляет, в разных проектах, от 50% до 70% и более. Поэтому современный цикл разработки и тестирования обязательно включает юнит-тестирование; и тестировщик-джун должен обладать базовыми понятиями о юнит-тестах; в крупных компаниях в некоторых проектах случается, что и тестировщики пишут юнит-тесты чужого кода.

Что такое юнит-тестирование: определение

Юнит-, или модульное тестирование — «первый снизу», фундаментальный уровень (функционального) тестирования. Цель модульного тестирования состоит в проверке каждого отдельного юнита (модуля) продукта на ранней стадии разработки, чтобы не допустить проникновения ошибок («каскадом») на верхние уровни.

Чтобы правильно ответить на вопрос, что такое юнит-тест, нужно сначала правильно сформулировать, что такое юнит, с точки зрения тестировщика?

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

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

У джуна QA все же может возникнуть вопрос, зачем же нужно тестирование на таком низком уровне, и почему столько внимания. Почему бы не придумать какие-то сверхновые, суперсовременные, полностью автоматические методики разработки, вообще НЕ предполагающие тестирование на таком уровне? Как-то упрощающие процесс. Передающие эту механическую, объемную работу на уровне модулей — куда-то повыше, к Вершине Пирамиды, тем самым передавая и ответственность за тестирование продукта полностью в QA-департамент; освободить «разрабов» от рутинных задач; а мы, тестировщики, разберемся со всеми тестами, начиная с самых простых. Ответ в том, что если это было легко сделать, то так делали бы все и давно. Но с этим сложности. 

Во первых. Юнит-тестирование, как подход, пока что незаменимо, в той форме в какой оно существует сейчас, поскольку оно на фундаментальном уровне помогает ИТ-компаниям и отдельным разработчикам улучшать качество своих продуктов/модулей. Разработчик, который сам пишет юнит-тесты к своему коду, развивается как разработчик; и ему писать юнит-тест проще, удобнее, и быстрее, чем кому-либо другому.

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

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

Четвертая причина: юнит-тесты улучшают ситуацию с документацией продукта; документация становится более простой, понятной, как для разработчиков, так и для тестировщиков.

Пятая причина: если продукт на низком, unit-уровне хорошо покрыт тестами, то как правило эти модули лучше интегрируются с другими модулями и частями продукта, и внешними инструментами и технологиями.

Отдельный материал об этом — Юнит-тесты vs интеграционные тесты

Юнит-тестирование нужно потому, что:

  • Повышение скиллов у разработчиков
  • Фреймворки юнит-тестирования существуют уже десятилетиями, хорошо знакомы разработчикам, как правило надежные и хорошо документированные
  • Легче освоить техники статического тестирования (walkthrough/review/inspection), что будет полезно для повышения уровня в ЯП
  • Добросовестно выполненное юнит-тестирование экономит компании время и деньги, позволяя найти ошибки на очень ранней стадии разработки. Не тратить время и деньги, неделями пытаясь найти и устранить не всегда воспроизводящиеся баги, а релиз вот-вот. 
  • Такое тестирование позволяет команде принять и внедрить у себя методику разработки через тестирование (TDD), в которой тест-кейсы создаются до написания кода. (Традиционный подход: сначала пишем код, потом тестируем).

Как это работает

Юнит-тест — это программный код, который проверяет, что модуль работает корректно. Под «корректно» подразумевается, что модуль возвращает нужный результат (выполняет нужную функциональность, выводит ожидаемые данные).

Если так не происходит, если тест не выдает ожидаемый результат, он считается непрошедшим, то есть failed («красные тесты»). Если тест выдал ожидаемый результат, то есть прошел (passed, «зеленый»), можно переходить к следующим тестам/этапам.

Кто должен писать юнит-тесты

Отдельно уточним. Как говорилось выше, юнит-тесты — изначально и всегда была сфера ответственности разработчиков. Но их пишут и тестировщики; quality analysts на Западе могут, и, как считают некоторые PM-ы в не самых крупных ИТ-компаниях, даже должны писать юнит-тесты, если их об этом просят.

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

Также причины, почему разработчики пишут юнит-тесты:

  • Разработчикам лучше известно, какие части кода критически важные и их нужно проверить тщательнее
  • Они намного более компетентны, когда дело касается моков/заглушек
  • Они делают это быстрее, имея намного больше опыта в программировании, чем любой QA-автоматизатор
  • И это важнее всего — экономия времени, что критически важно в современных проектах «по эджайлу».

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

О важности юнит-тестирования в Agile

При создании продукта по Agile особенно важен поиск и устранение потенциальных дефектов на ранних стадиях разработки. Юнит-тестирование — проверка соответствия продукта требованиям на фундаментальном уровне, поэтому в каждом билде «по эджайлу» должно быть проведено юнит-тестирование, и сгенерирован репорт, описывающий функциональность (и проблемы с ней) на уровне юнитов; и QA-команду автоматически информируют о неудачных тест-кейсах.

Какие юнит-тесты можно считать добротными?

  • Быстрые. В больших корпоративных проектах, возможно, сотни тысяч отдельных модулей. Поэтому хороший юнит-тест выполняется быстро (миллисекунды). Часто тесты нужно повторять многократно. Поэтому юнит-тест по определению должен быть очень быстрым.
  • Легкий дебаг. Всякий юнит-тест должен быть достаточно простым и ясным, как бы «объяснять поведение модуля». Некоторые тесты выполняются успешно будучи изолированными, но падают после интеграции модулей, что указывает на ошибки в дизайне.
  • Независимые и изолированные. Хорошие тест не зависит от внешних факторов, например типа файловой системы или базы данных.
  • Самопроверка. Автоматизированные юнит-тесты в идеале должны «предвидеть», пройдут они или нет, без вмешательства человека.
  • Воспроизводимость и стабильность. Хороший тест должен выдавать стабильные результаты всякий раз.

Подходы юнит-тестирования

Тестирование белого ящика

Также известно как «тестирование стеклянного ящика» или «прозрачное». Разработчик (или тестировщик) знает код приложения и понимает его функциональность. Поэтому может верифицировать модуль лучше — понимая его код и связи с другими модулями. 

Черного ящика

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

Серый ящик

Так называемое «полупрозрачное тестирование», смешение описанных выше подходов. Разработчик имеет ограниченное понимание кода модуля. Применяется тестирование по паттернам, матричное тестирование, ортогональных паттернов, и регрессионное.

Процесс

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

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

В Python применяется фреймворк UnitTest или другие подобные для создания автоматизированных тест-кейсов.

Процесс:

  1. Разработчик пишет код проверки функции модуля. Далее, когда продакшн-код деплоится, он может закомментировать или вообще удалить код теста.
  2. Если нужно, разработчик может изолировать функцию, чтобы лучше ее проверить, особенно проблемные зависимости.
  3. Фреймворк записывает все неудачные результаты автотестов. В некоторых фреймворках есть удобные обобщенные репорты.

Недостатки юнит-тестирования

  • Сложность и стоимость, при имплементировании новых функций в приложение
  • Замедляет создание прототипов когда исходный код приложения часто обновляется
  • Тесты с взаимными зависимостями могут влиять на результаты

Сложности

  • Названия и метки. Присвоение тестам понятных названий и меток важно для команды.
  • Понимание всего кода. Юнит-тестов всегда очень много, соответственно объем кода. Чтобы писать хорошие тесты, желательно изучить весь код.
  • Низкий уровень опыта что касается моков. Случается, что моки сложнее, чем продакшен-код.
  • Дебаг требует много времени. Если тесты часто падают, юнит-тестирование задерживает разработку.

Лучшие практики

  • Как уже говорилось выше, юнит-тесты должны быть автономны. Любые зависимости будут влиять негативно, усложняя дебаг тест-кейсов. 
  • Тестировать много use-кейсов. Один юнит может быть связан с несколькими use-кейсами, поэтому нужно тестировать каждый use-кейс в разных тест-кейсах, что позволяет рефакторить код более эффективно.
  • Паттерн ААА, отделяющий объект тестирования от arrange-этапа, что делает тест-кейсы более читабельными.
  • Логичные и понятные названия переменных, тест-кейсов, сценариев.

Пример юнит-теста

Юнит-тест проверяет часть кода, класс, или просто один метод. Чем меньше тест, тем лучше, небольшие тесты скорее выполняются, и их легче запускать «пакетом».

Далее пример компактного юнита кода и юнит-теста. Это класс, суммирующий две цифры, и простая тестовая функция этого класса.

public class Adder{
public int addnum(int num1, int num2){
return num1 + num2;
}
}
import org.junit.Assert;
import org.junit.Test;
public class Addertest{
@Test
public void test addnum(){
Adder adder1 = new Adder();
int num1 = 3;
int num2 = 2;
int result = adder1.addnum(num1, num2);
Assert.assertEquals(5, result);
}
}

Инструменты

JTest

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

Сайт

JUnit

Бесплатный инструмент для Java. Поддерживает assertions. 

Сайт

NUnit

Бесплатный фреймворк для .NET с открытым кодом. Поддерживает DDT-методику и параллельное выполнение.

Сайт

JMockit

Инструмент покрытия кода на Java. Есть функции записи и проверки синтаксиса для моков API. Подсчитывает покрытие по строкам, путям, и данным. 

Сайт

EMMA

Открытый инструмент анализа и репортов Java-кода, аналогичный JMockit. Поддерживает больше типов покрытия кода.

Сайт

PHPUnit

Для разработчиков на PHP. Есть встроенные assertions.

Сайт

Бонус: что такое TDD

Разработка через тестирование (TDD), иначе называемое TFD, подход «сначала тесты, потом код к этим тестам». Итеративный метод разработки, когда разработчики пишут тест-кейсы до написания продакшен-кода. Тест-кейсы создаются по каждой функции до появления ее кода. Ставится цель изменить сам процесс разработки, и как утверждают адепты этой методики, она позволяет минимизировать количество багов в финальном продукте.

Тесты в TDD содержат условия, которым продакшн-код должен соответствовать. Каждый тест-кейс описывает и затем валидирует то, что должен делать продакшен-код. Затем пишется продакшен-код и проверяется этими тест-кейсами; если код падает, разработчики рефакторят его, пока не достигнут результата, то есть соблюдения требований в тест-кейсе.

Три этапа TDD: 

  1. Создание тест-кейсов, проверяющих какую-то функциональность.
  2. Коррекция кода, если тесты падают. Делаются корректировки, нужные чтобы тест прошел.
  3. Рефакторинг кода. Если код прошел все тесты, разработчики оптимизируют свой код, выбрасывают все избыточное и конфликтующее.

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

Источник

***

Более серьезный материал о модульном тестировании — Юнит-тесты. Очень глубокое погружение

Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас