Вебинары Разобраться в IT Реферальная программа
Программирование Аналитика Дизайн Маркетинг Управление проектами
07 Июн 2023
9 мин
353

Что такое модульное (юнит) тестирование и как его проводить

Узнайте о важности юнит-тестирования, его применении и советах по написанию тестов для обеспечения высокого качества кода.

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

Поэтому юнит-тестирование называют модульным. Его цель — проверка отдельной функциональности.

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

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

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

У юнит-тестов есть несколько преимуществ и недостатков:

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

Зачем проводится unit-тестирование

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

Когда можно обойтись без юнит-тестирования

Юнит-тесты помогают проверять код, но иногда их можно не писать:

  1. Если сроки очень сжатые и проект нужно закончить срочно.
  2. Для простых программ, где код не будет меняться в дальнейшем. Например, для формы обратной связи на сайте.

Виды и методы модульного тестирования

Два основных вида юнит-тестирования — ручное и автоматическое.
Ручное тестирование:

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

Ручные юнит-тесты можно делать только с очень небольшими и простыми компонентами — и даже в этом случае они будут отнимать много времени.

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

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

Методы модульного тестирования — это совокупность разных подходов к тестированию.

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

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

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

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

Анализ покрытия кода

Анализ покрытия кода — метод, который помогает оценить, насколько эффективно проходит тестирование программного обеспечения. Он показывает, какие части исходного кода были выполнены (или «покрыты») во время тестов.

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

Анализ дает следующую информацию:

Тип покрытия Описание Что измеряет Преимущества
Покрытие строк Показывает процент строк кода, выполненных во время тестирования Количество выполненных строк кода Простой в понимании метрики; быстрая оценка общего охвата
Покрытие условий Проверяет, охвачены ли все возможные логические условия Все возможные результаты в условных операторах (например, if) Помогает выявить пропущенные сценарии в логике программы
Покрытие ветвей Измеряет, охвачены ли все возможные ветви в коде Все пути выполнения в структурах ветвления (например, if-else) Глубокий анализ потока управления программы
Покрытие методов Показывает процент методов, вызванных во время тестирования Количество вызванных методов Помогает выявить неиспользуемый код

 

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

  • для Java подходят JaCoCo и Cobertura;
  • для Python — Coverage.py;
  • для JavaScript — Istanbul.

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

Эксперты Skypro учат работать с этими инструментами на курсах «Java-разработчик», «Python-разработчик» и «Веб-разработчик».

Мок-объекты

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

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

Рекомендации к unit-тестам

Чтобы модульное тестирование проходило правильно и ловило все ошибки, соблюдайте рекомендации по их написанию.

Выберите логическое расположение тестов в вашей VCS

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

/project-root
/src
/main
/python
/com
/example
/app
/tests
/python
/com
/example
/app

Выберите способ именования проектов с тестами

У проектов с тестами должны быть понятные и однозначные имена. Например, если основной проект называется MyApp, то проект с тестами можно назвать MyAppTests или MyAppTesting.

Используйте такой же способ именования для тестовых классов

Тестовые классы должны отражать тестируемый класс или функциональность. Например, если есть класс UserService, то тестовый класс можно назвать TestUserService.

Выберите «говорящий» способ именования методов тестирующих классов

Методы тестирующих классов должны иметь понятные названия, которые четко описывают что тестируется. Например, вместо testUserCreation лучше использовать should_create_user_with_valid_data — то есть буквально «должен_создавать_пользователя_с_корректными_данными».

Выберите тестовый фреймворк, который подходит вам

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

  • Python: unittest, pytest;
  • Java: JUnit, TestNG;
  • JavaScript: Jest, Mocha;
  • C#: NUnit, xUnit.

Придерживайтесь единого стиля написания тела теста

Один из популярных стилей — это AAA (Arrange, Act, Assert):

  1. Arrange: подготовка данных и объектов.
  2. Act: выполнение тестируемого действия.
  3. Assert: проверка результатов.

Пример на Python:

import unittest
from user_service import UserService, User

class TestUserService(unittest.TestCase):
# Тест проверяет создание пользователя с валидными данными
def test_should_create_user_with_valid_data(self):
# Arrange: создаем экземпляр сервиса пользователя и валидного пользователя
user_service = UserService()
valid_user = User(«Alice», «Smith»)

# Act: вызываем метод создания пользователя
created_user = user_service.create_user(valid_user)

# Assert: проверяем, что пользователь был создан
# Проверяем, что объект пользователя не None
self.assertIsNotNone(created_user)
# Проверяем, что имя пользователя совпадает с ожидаемым
self.assertEqual(«Alice», created_user.first_name)
# Проверяем, что фамилия пользователя совпадает с ожидаемым
self.assertEqual(«Smith», created_user.last_name)

Тестируйте одну вещь за один раз

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

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

Есть программа, которая проверяет, является ли число четным:

public class NumberUtils {
public static boolean isEven(int number) {
return number % 2 == 0;
}
}

Создадим юнит-тест для этой функции:

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import org.junit.Test;

public class NumberUtilsTest {

@Test
public void testIsEven() {
assertTrue(NumberUtils.isEven(2));
assertTrue(NumberUtils.isEven(0));
assertFalse(NumberUtils.isEven(1));
assertFalse(NumberUtils.isEven(-1));
}
}

Этот юнит-тест проверяет, что функция isEven правильно определяет четные и нечетные числа. Если все проверки проходят успешно, тест завершится без ошибок.

Что делаем в этом тесте:

  1. Импортируем необходимые классы из библиотеки JUnit:

    import static org.junit.Assert.assertTrue;
    import static org.junit.Assert.assertFalse;
    import org.junit.Test;

  2. Создаем класс тестов:

    public class NumberUtilsTest {

  3. На этапе выполнения вызываем метод, который тестирует приложение:

    @Test
    public void testIsEven() {


    Этап подготовки пропускаем — программа такая простая, что не требует создания объектов или дополнительной подготовки.
  4. На последнем этапе проверяем результаты функции isEven:

    assertTrue(NumberUtils.isEven(2));
    assertTrue(NumberUtils.isEven(0));
    assertFalse(NumberUtils.isEven(1));
    assertFalse(NumberUtils.isEven(-1));

Если вы хотите научиться писать автоматизированные юнит-тесты под программы на Java, Python и JavaScript — поступайте на «Java-разработчик», «Python-разработчик» или «Веб-разработчик» в онлайн-университет Skypro. Эксперты научат вас писать сами тесты и пользоваться инструментами для интеграции тестов.

Главное про юнит-тестирование

  1. Модульное и юнит-тестирование — это одно и то же. Называется так, потому что этот тип тестирования проверяет работу отдельных компонентов (юнитов/модулей).
  2. Юнит-тесты проверяют отдельные модули программы. Благодаря этому можно протестировать работу конкретной функциональности.
  3. Юнит-тесты могут быть ручными и автоматическими. Ручное тестирование более гибкое и подробное, но очень медленное и трудозатратное. Автоматические тесты быстрее и надежнее, но их создание требует ресурсов.
  4. Модульное тестирование помогает найти ошибки в модуле на ранних стадиях разработки. Но может не учитывать ошибки, которые появляются при взаимодействии модулей. Для этого уже есть интеграционное тестирование — тесты, которые проверяют, как модули взаимодействуют между собой.
  5. Если регулярно проводить юнит-тесты, то ошибки не будут накапливаться, а интеграционное тестирование будет проходить легче.
  6. Основные виды тестирования построены по принципам белого ящика и черного ящика. В тестировании белого ящика тот, кто тестирует модуль, знает, как он устроен внутри. Это помогает написать подробные тесты. В тестировании черного ящика тот, кто тестирует, не знает, как он устроен: это дает возможность проверить, работает ли модуль в соответствии со спецификациями.
  7. Анализ покрытия кода показывает, какие участки кода покрыты тестами, а какие нет. После такого анализа тестировщики и разработчики могут сосредоточиться на непроверенных участках кода.
  8. Чтобы проанализировать степень покрытия кода тестами, нужно интегрировать специальные инструменты в систему сборки. Примеры инструментов: JaCoCo для Java, Coverage.py для Python.
  9. Тесты должны быть логично организованы. Имя тестов и методов должно быть «говорящим». Названия должны ясно отражать функциональность, которая проверяется тестом.
  10. Выбор фреймворка для тестирования зависит от языка программирования. Популярные фреймворки: pytest для Python, JUnit для Java.
  11. Писать тесты следует в стиле Arrange, Act, Assert. Тогда они будут четкими и структурированными. Лучше в одном тесте проверять только одну вещь за один раз.
Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей

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