Тесты, как и любой другой код, можно писать по-разному, в том числе очень плохо. Помимо каких-то общих практик и стандартов кодирования у тестов есть свои особенности, о которых надо знать. В этом уроке мы пройдёмся по некоторым из них.
Одно из ключевых правил: тесты не должны влиять друг на друга. Это значит, что любой тест выполняется так, как будто других тестов не существует в природе.
Нарушить это правило очень просто. Один тест может создать файл, изменить переменную или записать что-то в базу. Если остальные тесты наткнутся на эти изменения, то они могут упасть там, где не должны падать, или наоборот — успешно пройти там, где не должны проходить. Кроме этого, в такой ситуации вводится неопределённость. Такие тесты могут падать эпизодически без видимых на то причин. Например, когда тест запускают изолированно, то он работает, а когда вместе с остальными — падает:
user = None
def test_first():
user = { 'name': 'Vasya' }
# Тут логика теста
def test_second():
# Используется пользователь, созданный другим тестом!
# Этот тест зависит от того, как работает предыдущий тест,
# и не может работать без последовательного запуска обоих тестов
user.name = 'Petya'
Особенно часто такая ситуация возникает в тестах, активно взаимодействующих с внешней средой: базой данных или файловой системой. Тестирование побочных эффектов имеет свои хитрости и рассматривается в курсе по продвинутому тестированию.
def test_something():
if (something):
# Выполняем код одним способом
# Проверка может быть тут
else:
# Выполняем код другим способом
# Проверка может быть тут
# Проверка может быть тут
Любое ветвление в тестах это, фактически, несколько тестов в рамках одного теста. Ветвления порождают код, который сам нуждается в тестировании, так как его порядок выполнения становится не очевидным и может быть случайно нарушен. Лучше создать независимые тесты для каждой ветки.
Задача фикстур — готовить данные и среду для тестирования, а задача тестовых функций — вызывать код, который тестируется, и проводить проверки. Но иногда разработчики переусердствуют:
# Тестируем функцию sum()
import pytest
@pytest.fixture
def result():
# Вызывается тестируемый код
return sum([5, 9])
def test_sum(result):
assert result == 14
В этом примере, тестируемый код вызывается в result()
. Такой подход усложняет анализ тестов, так как переворачивает всё с ног на голову.
Программисты под влиянием голосов из интернета стремятся максимально разносить код по файлам, модулям и функциям. То же самое наблюдается и в тестах. Вместо одного теста, в котором содержатся все необходимые проверки, программист создаёт 5 тестов, в каждом из которых ровно одна проверка:
def test_create_user():
user = { 'name': 'Mark', 'age': 28 }
# Тут, например, код, добавляющий пользователя в базу данных
assert user['age'] == 28
def test_create_user():
user = { 'name': 'Mark', 'age': 28 }
# Тут, например, код, добавляющий пользователя в базу данных
assert user['name'] == 'Mark'
Чаще всего единственным результатом такого разделения будет большее количество кода и усложнение рефакторинга в будущем, когда кода станет по-настоящему много.
Это очень интересный вопрос, по которому можно понять, насколько хорошо программист умеет писать тесты. Несмотря на то, что некоторые виды тестирования действительно сложны и требуют дополнительного времени, ежедневные тесты, которые пишутся вместе с кодом, должны приводить к ускорению разработки. И на это есть пять причин:
Вам ответят команда поддержки Хекслета или другие студенты.
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Загляните в раздел «Обсуждение»:
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт