- en
- Language: ru
- Documentation version: latest
Особенности использования pytest для проверки заданий
На курсе pytest используется, в первую очередь, для самопроверки заданий. Однако, эта проверка не является опциональной - задание считается сделанным, когда оно соблюдает все указанные пункты и проходит тесты. Со своей стороны, я тоже сначала проверяю задания автоматическими тестами, а затем уже смотрю код, пишу комментарии, если нужно и показываю вариант решения.
Поначалу тесты требуют усилий, но через пару разделов, они будут помогать в решении заданий.
Предупреждение
Тесты, которые написаны для заданий курса, не являются эталоном или best practice написания тестов. Тесты написаны с максимальным упором на понятность и многие вещи принято делать по-другому.
При решении заданий, особенно, когда есть сомнения по поводу итогового формата данных, которые должны быть получены, лучше посмтреть в тест. Например, если задание task_9_1.py, то соответствующий тест будет в файле test_task_9_1.py.
Пример теста test_task_9_1.py:
import pytest
import task_9_1
import sys
sys.path.append('..')
from common_functions import check_function_exists, check_function_params
# Проверяет создана ли функция generate_access_config в задании task_9_1
def test_function_created():
check_function_exists(task_9_1, 'generate_access_config')
# Проверяет параметры функции
def test_function_params():
check_function_params(function=task_9_1.generate_access_config,
param_count=2, param_names=['intf_vlan_mapping', 'access_template'])
def test_function_return_value():
access_vlans_mapping = {
'FastEthernet0/12': 10,
'FastEthernet0/14': 11,
'FastEthernet0/16': 17
}
template_access_mode = [
'switchport mode access', 'switchport access vlan',
'switchport nonegotiate', 'spanning-tree portfast',
'spanning-tree bpduguard enable'
]
correct_return_value = ['interface FastEthernet0/12',
'switchport mode access',
'switchport access vlan 10',
'switchport nonegotiate',
'spanning-tree portfast',
'spanning-tree bpduguard enable',
'interface FastEthernet0/14',
'switchport mode access',
'switchport access vlan 11',
'switchport nonegotiate',
'spanning-tree portfast',
'spanning-tree bpduguard enable',
'interface FastEthernet0/16',
'switchport mode access',
'switchport access vlan 17',
'switchport nonegotiate',
'spanning-tree portfast',
'spanning-tree bpduguard enable']
return_value = task_9_1.generate_access_config(access_vlans_mapping, template_access_mode)
assert return_value != None, "Функция ничего не возвращает"
assert type(return_value) == list, "Функция должна возвращать список"
assert return_value == correct_return_value, "Функция возвращает неправильное значение"
Обратите внимание на переменную correct_return_value - в этой переменной содержится итоговый список, который должна возвращать функция generate_access_config. Поэтому, если, к примеру, по мере выполнения задания, возник вопрос надо ли добавлять пробелы перед командами или перевод строки в конце, можно посмотреть в тесте, что именно требуется в результате. А также сверить свой вывод, с выводом в переменной correct_return_value.
Как запускать тесты для проверки заданий
Самое главное, это откуда надо запускать тесты: все тесты надо запускать из каталога с заданиями раздела. Например, в разделе 09_functions, такая структура каталога с заданиями:
[~/repos/pyneng-7/pyneng-online-may-aug-2019/exercises/09_functions]
vagrant: [master|✔]
$ tree
.
├── config_r1.txt
├── config_sw1.txt
├── config_sw2.txt
├── conftest.py
├── task_9_1a.py
├── task_9_1.py
├── task_9_2a.py
├── task_9_2.py
├── task_9_3a.py
├── task_9_3.py
├── task_9_4.py
├── test_task_9_1a.py
├── test_task_9_1.py
├── test_task_9_2a.py
├── test_task_9_2.py
├── test_task_9_3a.py
├── test_task_9_3.py
└── test_task_9_4.py
Запускать тесты, в этом случае, надо из каталога 09_functions:
[~/repos/pyneng-7/pyneng-online-may-aug-2019/exercises/09_functions]
vagrant: [master|✔]
$ pytest test_task_9_1.py
========================= test session starts ==========================
platform linux -- Python 3.7.3, pytest-4.6.2, py-1.5.2, pluggy-0.12.0
rootdir: /home/vagrant/repos/pyneng-7/pyneng-online-may-aug-2019/exercises/09_functions
collected 3 items
test_task_9_1.py ... [100%]
...
conftest.py
К тестами относится и файл conftest.py - это специальный файл, в котором можно писать функции (а точнее фикстуры) общие для раных тестов. Например, в этот файл вынесены функции, которые подключаются по SSH/Telnet к оборудованию.
pytest.ini
Это конфигурационный файл pytest. В нем можно настроить аргументы вызова pytest. Например, если вы хотите всегда вызывать pytest с -vv, надо написать в pytest.ini:
[pytest]
addopts = -vv
В подготовленном файле pytest.ini находится такая строка:
addopts = -vv --no-hints
Это параметр, который нужен модулю pytest-clarity, он описывается ниже.
Полезные команды
Запуск одного теста:
$ pytest test_task_9_1.py
Запуск одного теста с более подробным выводом информации (показывает diff между данными в тесте и тем, что получено из функции):
$ pytest test_task_9_1.py -vv
Запуск всех тестов одного раздела:
[~/repos/pyneng-7/pyneng-online-may-aug-2019/exercises/09_functions]
vagrant: [master|✔]
$ pytest
======================= test session starts ========================
platform linux -- Python 3.6.3, pytest-4.6.2, py-1.5.2, pluggy-0.12.0
rootdir: /home/vagrant/repos/pyneng-7/pyneng-online-may-aug-2019/exercises/09_functions
collected 21 items
test_task_9_1.py ..F [ 14%]
test_task_9_1a.py FFF [ 28%]
test_task_9_2.py FFF [ 42%]
test_task_9_2a.py FFF [ 57%]
test_task_9_3.py FFF [ 71%]
test_task_9_3a.py FFF [ 85%]
test_task_9_4.py FFF [100%]
...
Запуск всех тестов одного раздела с отображением сообщений об ошибках в одну строку:
$ pytest --tb=line
pytest-clarity
Плагин pytest-clarity улучшает отображение отличий необходимого результата с решением задания.
Установка:
pip install pytest-clarity
Плагин pytest-clarity отрабатывает только в том случае, когда тест
вызывается с флагом -vv
. Также по умолчанию у него довольно объемный
вывод, поэтому лучше вызывать его с аргументом --no-hints
(эта опция прописана в подготовленном репозитории в файле pytest.ini):
$ pytest test_task_9_3.py -vv --no-hints
======================= test session starts ========================
test_task_9_3.py::test_function_created PASSED [ 33%]
test_task_9_3.py::test_function_params PASSED [ 66%]
test_task_9_3.py::test_function_return_value FAILED [100%]
======================== FAILURES ==================================
__________ test_function_return_value ______________________________
...
access, trunk = return_value
> assert (
return_value == correct_return_value
), "Функция возвращает неправильное значение"
E AssertionError: Функция возвращает неправильное значение
E assert left == right failed.
E Showing unified diff (L=left, R=right):
E
E L ({'FastEthernet0/0': '10',
E R ({'FastEthernet0/0': 10,
E L 'FastEthernet0/2': '20',
E R 'FastEthernet0/2': 20,
E L 'FastEthernet1/0': '20',
E R 'FastEthernet1/0': 20,
E L 'FastEthernet1/1': '30'},
E R 'FastEthernet1/1': 30},
E {'FastEthernet0/1': [100, 200],
E 'FastEthernet0/3': [100, 300, 400, 500, 600],
E 'FastEthernet1/2': [400, 500, 600]})
test_task_9_3.py:59: AssertionError
Так как агументы -vv
и --no-hints
надо постоянно передавать,
можно записать их в pytest.ini:
[pytest]
addopts = -vv --no-hints