Программы состоят из множества файлов, содержащих фрагменты кода, называемые функциями. Функции вызываются из разных частей программы, принимают аргументы (входные данные) и возвращают результат. Программа логически делится на части — юниты (элементы, блоки или модули), каждый из которых выполняет свою задачу (например, сложение чисел, вычисление средней оценки). Если написанные функции необходимы, то каждая должна работать исправно. Непроверенная программа может упасть в любой момент, особенно обидно, если изменения сломали функционал, который вроде бы и не трогали.
Юнит-тестирование: проверка каждого юнита
Перед отправкой в продакшен следует протестировать каждый юнит, чтобы убедиться в его корректной работе. Это юнит-тестирование (или модульное тестирование) — автоматическая проверка каждого юнита на правильность результатов. Даже неизмененный юнит будет протестирован. Юнит-тесты интегрируются в CI/CD пайплайн проекта, запускаясь при заливке кода в репозиторий, предотвращая продвижение нерабочего кода. Разработчики пишут unit-тесты — небольшие программы, проверяющие работу части кода. Они запускаются вместе с программой, проверяя правильность возвращаемых функциями результатов.
Преимущества и недостатки юнит-тестов
Тот, кто писал код, лучше всего знает, как должна работать функция или метод, и может написать тест для проверки этого. Каждый тест проверяет только один сценарий использования функции или метода (например, для функции сложения двух чисел — проверка 2 + 2 = 4). Тест запускает функцию, передаёт аргументы, проверяет результат и сравнивает его с ожидаемым значением. Если результат отличается от ожидаемого (например, вместо 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 практически одинаковы. Для тестирования нескольких методов повторяются похожие конструкции.
Юнит-тестирование — эффективный способ автоматической проверки отдельных частей программы. Знание принципов юнит-тестирования в одном языке программирования позволяет легко применять его в других. Несмотря на ограничения, юнит-тесты ускоряют разработку и внедрение новых функций.