Перетяжка, Премия ТПрогер, 13.11
Перетяжка, Премия ТПрогер, 13.11
Перетяжка, Премия ТПрогер, 13.11

Как писать тесты функций Python, если вы никогда этого не делали

Разобрали на примере, как начинающим писать тесты для Python-кода и проверять вводимые почты на валидность

2К открытий9К показов
Как писать тесты функций Python, если вы никогда этого не делали

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

Проверка адресов email

Тест кода Python — это временная надстройка над основной частью программы. По мнению softwaretestinghelp.com, эта библиотека входит в тройку самых популярных инструментов. Давайте посмотрим на примере, как проверять вводимые адреса электронной почты.

Напишем функцию, проверяющую присутствие знака @, латиницы, и отсутствие некоторых спецсимволов:

			import string
def is_valid_email_address(s):
    s = s.lower()
    parts = s.split('@')
    if len(parts) != 2:
      # Если собачки нет
      return False
    allowed = set(string.ascii_lowercase + string.digits + '.-_')
    for part in parts:
        if not set(part) <= allowed:
          # Найдены неразрешенные символы
          return False
    return True
		

Теперь у нас есть система проверки email. Адреса вроде test@example.org, user123@tproger.ru действительны. А вот ‘not valid@example.org’ и ‘ivan ivanov’ — нет:

			print(is_valid_email_address('test@example.org')) # True
print(is_valid_email_address('user123@tproger.ru')) # True
print(is_valid_email_address('not valid@example.org')) # False
print(is_valid_email_address('ivan ivanov')) # False
		

pytest с легкостью автоматизирует такую проверку. Проверим валидность ящиков в три этапа:

			# проверяем верный адрес
def test_regular_email_validates():
    assert is_valid_email_address('test@example.org') # True

# проверяем на наличие одного знака @
def test_valid_email_has_one_at_sign():
    assert not is_valid_email_address('john.doe') # False

# проверяем на разрешенные символы
def test_valid_email_has_only_allowed_chars():
    assert not is_valid_email_address('john,doe@example.org') # True
    assert not is_valid_email_address('not valid@example.org') # True
		

Инструкция assert («допущение, утверждение») проверяет истинность выражения, not – инвертирует проверку

Важно понимать, что тестируем мы конкретный участок кода, а не сами почты. На проде is_valid_email_address() будет являться рабочим, а не тестирующим компонентом.

Запуск проверки

Сохраним test_regular_email_validates() в файл validator.py и запустим тест:

			pytest validator.py
		

Тест «обопрется» на assert и потому «соберет» 3 объекта (‘collected 3 items’):

			======================== test session starts ========================
platform darwin -- Python 3.9.6, pytest-7.0.1, pluggy-1.0.0
rootdir: /path/to/file
collected 3 items

validator.py ...                                                    [100%]

========================= 3 passed in 0.01s ==========================
		

Индикатор 100% показывает что наш валидатор работает штатно. Однако он далек от совершенства. john.doe@example прошел бы тест, как и john.doe+abc@gmail.com, хотя оба адреса недействительны.

Давайте добавим эти примеры в наш validator.py:

			def test_valid_email_can_have_plus_sign():
    assert is_valid_email_address('john.doe+abc@gmail.com')

def test_valid_email_must_have_a_tld():
    assert not is_valid_email_address('john.doe@example')
		

Теперь при перезапуске мы получим два «фэйла»:

			======================== test session starts =========================
platform darwin -- Python 3.9.6, pytest-7.0.1, pluggy-1.0.0
rootdir: /path/to/file
collected 5 items

test_validator.py ...FF                                                  [100%]

============================== FAILURES ==============================
________________ test_valid_email_can_have_plus_sign _________________

    def test_valid_email_can_have_plus_sign():
>       assert is_valid_email_address('john.doe+abc@gmail.com')
E       AssertionError: assert False
E        +  where False = is_valid_email_address('john.doe+abc@gmail.com')

test_validator.py:17: AssertionError
__________________ test_valid_email_must_have_a_tld __________________

    def test_valid_email_must_have_a_tld():
>       assert not is_valid_email_address('john.doe@example')
E       AssertionError: assert not True
E        +  where True = is_valid_email_address('john.doe@example')

test_validator.py:20: AssertionError
====================== short test summary info ======================
FAILED test_validator.py::test_valid_email_can_have_plus_sign - AssertionErro...
FAILED test_validator.py::test_valid_email_must_have_a_tld - AssertionError: ...
==================== 2 failed, 3 passed in 0.05s =====================
		

В выводе pytest появится новый раздел Failures, в котором подробно объясняется, в какой момент тест кода Python не удался. Это позволит отладить ошибки.

Фикстуры (Fixtures)

Обычно этим термином обозначают небольшой набор данных, которым наполняют хранилище приложения. В контексте тестирования фикстурами («арматурой») называют функции pytest.

Мы можем создать их с помощью декоратора pytest.fixture():

			import​ pytest

@pytest.fixture()
def database_environment():
    setup_database()
    yield
    teardown_database()
		

Обратите внимание, что настройка базы данных и удаление происходят в одном месте. Ключевое слово yield указывает pytest, где запустить тесты.

Чтобы фикстура действительно задейстовалась, добавим ее имя в качестве аргумента:

			def test_world(database_environment):
    assert 1 == 1
		

Полезные параметры командной строки

  • Запустить одну конкретную функцию:
			pytest test_validator.py::test_regular_email_validates
		
  • Перечислить функции в файле:
			pytest --collect-only
		
			======================== test session starts ========================
platform darwin -- Python 3.9.6, pytest-7.0.1, pluggy-1.0.0
rootdir: /path/to/file
collected 5 items

<Module test_validator.py>
  <Function test_regular_email_validates>
  <Function test_valid_email_has_one_at_sign>
  <Function test_valid_email_has_only_allowed_chars>
  <Function test_valid_email_can_have_plus_sign>
  <Function test_valid_email_must_have_a_tld>

===================== 5 tests collected in 0.01s =====================
		
  • Остановить при первой ошибке:
			pytest -x
		
  • Запустить только последний неудачный тест:
			pytest --lf
		
  • Запустите все тесты, но сначала последние неудачные:
			pytest --ff
		

Заключение

Теперь в нашем распоряжении простейший кейс проверки электронной почты. И мы знаем теперь, как использовать pytest и читать результаты. Если вы созрели для чего-то более масштабного, рекомендую статью Дениса Исангулова:

Следите за новыми постами
Следите за новыми постами по любимым темам
2К открытий9К показов