Ответ
Для выявления ошибок в функциях используется модульное тестирование (unit testing), которое фокусируется на проверке отдельных компонентов (функций, методов) в изоляции.
Основные подходы и техники:
-
Тестирование на основе сценариев (Case-based testing) Создаются тесты для различных входных данных, чтобы проверить все ветви логики:
- Позитивные сценарии (Happy path): Проверка работы с корректными, ожидаемыми данными.
- Негативные сценарии: Проверка обработки невалидных данных (неправильный тип, формат).
- Граничные условия (Boundary cases): Тестирование крайних значений (например, 0, -1, пустые строки, максимальные значения).
-
Проверки утверждений (Assertions) Использование
assertдля сравнения фактического результата работы функции с ожидаемым. -
Проверка исключений (Exception testing) Тест должен подтвердить, что функция корректно выбрасывает ожидаемое исключение при определённых условиях (например, деление на ноль).
-
Изоляция с помощью моков (Mocking) Замена внешних зависимостей (базы данных, API, файловая система) на "заглушки" (mock-объекты). Это позволяет тестировать логику функции, не затрагивая другие части системы.
Пример с использованием pytest:
import pytest
def divide(a, b):
"""Функция для деления двух чисел."""
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Both arguments must be numbers")
if b == 0:
raise ZeroDivisionError("Cannot divide by zero")
return a / b
# 1. Позитивный сценарий
def test_divide_happy_path():
assert divide(10, 2) == 5
# 2. Тестирование граничного условия
def test_divide_by_negative():
assert divide(-10, 2) == -5
# 3. Проверка исключения ZeroDivisionError
def test_divide_by_zero_raises_exception():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
# 4. Проверка исключения TypeError для невалидных данных
def test_divide_with_invalid_type_raises_exception():
with pytest.raises(TypeError):
divide(10, "a")
Ключевые инструменты:
pytest: Мощный и популярный фреймворк для тестирования.unittest.mock: Встроенная библиотека для создания мок-объектов.coverage.py: Инструмент для измерения процента кода, покрытого тестами.
Ответ 18+ 🔞
А, ну вот, подъехали к самому интересному — как же мы, великие программисты, находим свои же косяки, чтобы потом не выглядеть полными мудаками перед заказчиком. Слушай сюда, сейчас разжую.
Вот есть у тебя функция, красивая такая, вроде работает. Но ты-то понимаешь, что внутри она может быть пиздец какой хитрожопой. Чтобы не гадать на кофейной гуще, используют модульное тестирование (unit testing). Суть проста, как три рубля — ты берёшь каждый мелкий кусочек кода (функцию, метод) и начинаешь его долбить со всех сторон в полной изоляции, как грешника в аду. От всего остального мира его отгораживаешь, чтобы если что-то пойдёт не так, ты точно знал — виновата вот эта конкретная сволочь, а не какой-то левый модуль.
Как это обычно делают, эти наши тестировщики-садисты:
-
Тестирование на основе сценариев (Case-based testing) Ты придумываешь кучу разных входных данных и смотришь, не обосрётся ли твоя функция.
- Позитивные сценарии (Happy path): Даёшь ей нормальную, ожидаемую еду. Всё должно перевариться и выйти ровным калом.
- Негативные сценарии: А вот тут начинается веселье. Суёшь ей в рот откровенную дичь — строки вместо чисел,
None, пустоту. Смотришь, выплюнет ли она с ошибкой, как и положено порядочной функции, или начнёт это жрать и потом срать исключениями на весь проект. - Граничные условия (Boundary cases): Это когда ты проверяешь её на прочность на самых краях. Ноль, минус один, максимально возможное число, пустая строка. Чаще всего все баги и сидят именно тут, эти подлые твари.
-
Проверки утверждений (Assertions) Это когда ты пишешь
assert, что результат работы функции должен быть вот таким. Если не таким — всё, пиздец, тест провален, иди чини. Проще некуда. -
Проверка исключений (Exception testing) Иногда правильное поведение функции — это не вернуть результат, а громко и по-матерински обложить тебя ошибкой. Например, если ты пытаешься делить на ноль. Тест должен удостовериться, что функция не стесняется в выражениях и кидает нужное исключение.
-
Изоляция с помощью моков (Mocking) О, это высший пилотаж, ёпта! Представь, твоя функция лезет в базу данных или дергает какой-то внешний API. Тебе же не нужно, чтобы каждый прогон тестов зависел от интернета или засирал реальную базу? Вот для этого и придумали мок-объекты — это такие хулиганистые заглушки, которые притворяются серьёзными зависимостями. Ты говоришь моку: "Слушай, когда тебя вызовут с такими-то аргументами, верни вот это". И функция, дура, думает, что пообщалась с настоящей базой, а на самом деле её просто наебали. Гениально же!
Смотри, как это выглядит в коде, на примере простой, но гордой функции деления:
import pytest
def divide(a, b):
"""Функция для деления двух чисел."""
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Both arguments must be numbers")
if b == 0:
raise ZeroDivisionError("Cannot divide by zero")
return a / b
# 1. Позитивный сценарий: всё хорошо, все довольны.
def test_divide_happy_path():
assert divide(10, 2) == 5
# 2. Граничное условие: работа с отрицалкой.
def test_divide_by_negative():
assert divide(-10, 2) == -5
# 3. Проверка, что на нуле будет БАБАХ!
def test_divide_by_zero_raises_exception():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
# 4. Проверка, что на буквы тоже будет скандал.
def test_divide_with_invalid_type_raises_exception():
with pytest.raises(TypeError):
divide(10, "a")
Чем всё это добро обычно вооружаются:
pytest— это наш главный каратель, фреймворк для тестов. Мощный, удобный, всем рекомендую.unittest.mock— встроенная в Питон библиотека для создания этих самых пиздатых заглушек-моков.coverage.py— инструмент для параноиков. Он показывает, какой процент твоего кода ты покрыл тестами. Полезная штука, чтобы понять, не осталось ли где тёмных, не проверенных уголков, где может затаиться очередной баг, эта мартышлюшка подколодная.