Unit-тесты: проверка функций и модулей кода

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

Юнит-тестирование: проверка каждого юнита

Перед отправкой в продакшен следует протестировать каждый юнит, чтобы убедиться в его корректной работе. Это юнит-тестирование (или модульное тестирование) — автоматическая проверка каждого юнита на правильность результатов. Даже неизмененный юнит будет протестирован. Юнит-тесты интегрируются в CI/CD пайплайн проекта, запускаясь при заливке кода в репозиторий, предотвращая продвижение нерабочего кода. Разработчики пишут unit-тесты — небольшие программы, проверяющие работу части кода. Они запускаются вместе с программой, проверяя правильность возвращаемых функциями результатов.

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

Тот, кто писал код, лучше всего знает, как должна работать функция или метод, и может написать тест для проверки этого. Каждый тест проверяет только один сценарий использования функции или метода (например, для функции сложения двух чисел — проверка 2 + 2 = 4). Тест запускает функцию, передаёт аргументы, проверяет результат и сравнивает его с ожидаемым значением. Если результат отличается от ожидаемого (например, вместо 4 — «динозавр»), тест сообщит об ошибке.

Несмотря на преимущества, у юнит-тестов есть недостатки:

  1. Проверка ограниченного числа сценариев: Юнит-тесты проверяют только один сценарий, поэтому другие сценарии могут быть не учтены.
  2. Необходимость обновления: При изменении функций нужно обновлять тесты.
  3. Ограниченная область применения: Юнит-тесты не подходят для тестирования целых модулей, интеграций, интерфейса и сложных алгоритмов.
  4. Альтернативные методы тестирования: Можно запускать функции по отдельности и проверять результаты визуально. Однако в реальных приложениях функции могут быть связаны с формами на сайте, требующими заполнения и нажатия кнопок. При ошибке всё придётся повторять. Запуск функции отдельно от всего кода ещё сложнее из-за зависимостей. Написание юнит-теста быстрее, чем ручное тестирование новой фичи, но требует дополнительных затрат времени на создание самих тестов.

Инструменты для юнит-тестирования

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

  • Python: unittest
  • Ruby: Test::Unit
  • Java: JUnit
  • JavaScript: Mocha

Примеры юнит-тестов

Рассмотрим пример юнит-теста на Python для функции get_sum(), которая складывает два числа:

import unittest

class TestSum(unittest.TestCase):
    def test_get_sum(self):
        self.assertEqual(get_sum(2, 2), 4)

В коде используется фреймворк unittest. Создаётся класс TestSum, наследующий от TestCase, содержащий функцию test_get_sum(), которая использует функцию assertEqual() из unittest для сравнения результата get_sum(2, 2) с ожидаемым значением 4.

Пример в Java для тестирования класса Calculator с методами сложения, вычитания, умножения и деления:

import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class CalculatorTest {
    @Test
    public void testAddition() {
        assertEquals("Addition failed", 4, calculator.add(2, 2));
    }
    // аналогично для testSubtraction, testMultiplication, testDivision
}

В Java используется JUnit. Класс CalculatorTest содержит методы для тестирования арифметических операций. Обратите внимание на третий аргумент assertEquals() — сообщение об ошибке. Примеры в Python и Java практически одинаковы. Для тестирования нескольких методов повторяются похожие конструкции.

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

Что будем искать? Например,программа