Изменение стандартных правил поиска тестов Python

Игнорирование путей при поиске тестов

Можно игнорировать отдельные директории и файлы при сборке тестов, используя опцию командной строки --ignore=path. pytest позволяет указывать несколько опций --ignore. Пример:

tests/
|-- example
|   |-- test_example_01.py
|   |-- test_example_02.py
|   '-- test_example_03.py
|-- foobar
|   |-- test_foobar_01.py
|   |-- test_foobar_02.py
|   '-- test_foobar_03.py
'-- hello
    '-- world
        |-- test_world_01.py
        |-- test_world_02.py
        '-- test_world_03.py

Теперь, если вызвать pytest с опциями --ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/, то он соберет только те тестовые модули, которые не удовлетворяют указанному шаблону:

=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 5 items

tests/example/test_example_01.py .                                   [ 20%]
tests/example/test_example_02.py .                                   [ 40%]
tests/example/test_example_03.py .                                   [ 60%]
tests/foobar/test_foobar_01.py .                                     [ 80%]
tests/foobar/test_foobar_02.py .                                     [100%]

========================= 5 passed in 0.02 seconds =========================

Опция --ignore-glob позволяет игнорировать пути в виде шаблонов Unix. К примеру, если вы хотите исключить тестовые модули, которые заканчиваются на _01.py, можно запустить pytest с опцией --ignore-glob='*_01.py'.

Отмена подбора теста

С помощью опции --deselect=item можно отменить подбор конкретного теста. Допустим, tests/foobar/test_foobar_01.py содержит test_a и test_b, и мы хотим выполнить все тесты из tests/ кроме tests/foobar/test_foobar_01.py::test_a. Это можно сделать, запустив pytest с опцией --deselect tests/foobar/test_foobar_01.py::test_a. pytest поддерживает и множественное применение опции.

Повторение путей в командной строке

По умолчанию pytest игнорирует задвоенные пути в командной строке:

pytest path_a path_a

...
collected 1 item
...

Тесты будут собраны только один раз.

Чтобы собрать тесты дважды, используйте опцию --keep-duplicates. Пример:

pytest --keep-duplicates path_a path_a

...
collected 2 items
...

Из-за особенностей поиска, если вы 2 раза укажете один и тот же тестовый файл, pytest подберет его дважды, даже если опция --keep-duplicates не применялась. Пример:

pytest test_a.py test_a.py

...
collected 2 items
...

Изменение правил рекурсивного обхода

Опцию norecursedirs можно задавать в ini-файле, например, в корневом pytest.ini вашего проекта:

# content of pytest.ini
[pytest]
norecursedirs = .svn _build tmp*

Такая запись указывает pytest не проводить рекурсивный поиск тестов в «build»-директориях «sphinx» и в директориях с префиксом tmp

Изменение соглашений по именам тестов для поиска

Вы можете настроить свои правила для поиска тестов по имени, установив опции python_files, python_classes and python_functions. Вот примеры:

# content of pytest.ini
# Пример 1: pytest  должен искать "check" вместо "test"
# можно также определить в tox.ini или setup.cfg file, при этом секция
# имен в setup.cfg files должна быть "tool:pytest"
[pytest]
python_files = check_*.py
python_classes = Check
python_functions = *_check

Такая настройка заставит pytest искать файлы по шаблону check_*.py, классы - по префиксу Check, а функции и методы по шаблону *_check. Допустим, у нас есть:

# content of check_myapp.py
class CheckMyApp:
    def simple_check(self):
        pass

    def complex_check(self):
        pass

Тогда собранные тесты будут выглядеть вот так:

$ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 2 items
<Module check_myapp.py>
  <Class CheckMyApp>
      <Function simple_check>
      <Function complex_check>

========================== no tests ran in 0.12s ===========================

Можно проверять на соответствие нескольким глобальным шаблонам - в этом случае при описании шаблоны нужно разделить пробелом:

# Пример 2: pytest должен искать файлы с "test" и "example"
# вставляется в pytest.ini, tox.ini, или setup.cfg  (заменив "pytest"
# на "tool:pytest" в setup.cfg)
[pytest]
python_files = test_*.py example_*.py

Примечание

параметры python_functions и python_classes не оказывают никакого действия на поиск unittest.TestCase, поскольку обнаружение таких тестов производится средствами unittest.

Аргументы командной строки как имена пакетов

Чтобы заставить pytest интерпретировать аргументы как имена пакетов, получать их системные пути и запускать тесты, можно использовать опцию --pyargs. Например, если у вас установлен unittest2, можно выполнить

pytest --pyargs unittest2.test.test_skipping -q

для запуска соответствующего тестового модуля. Как и со всеми остальными опциями, можно сделать ее использование постоянным с помощью addopts в ini-файле:

# content of pytest.ini
[pytest]
addopts = --pyargs

После этого простой вызов вида pytest NAME будет проверять существование NAME В качестве пригодного для импорта модуля и рассматривать его как путь в файловой системе.

Просмотр дерева найденных тестов

Всегда можно посмоетреть дерево собранных тестов без их запуска:

. $ pytest --collect-only pythoncollection.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 3 items
<Module CWD/pythoncollection.py>
  <Function test_function>
  <Class TestClass>
      <Function test_method>
      <Function test_anothermethod>

========================== no tests ran in 0.12s ===========================

Настройка поиска тестов

Можно легко заставить pytest искать тесты в любом python-файле:

# content of pytest.ini
[pytest]
python_files = *.py

Однако, во многих проектах есть файл setup.py, который не хотелось бы импортировать. Более того, там могут присутствовать файлы, которые можно импортировать только определенной версией Python. В таких случаях можно динамически определить игнорируемые файлы, перечислив их в conftest.py:

# content of conftest.py
import sys

collect_ignore = ["setup.py"]
if sys.version_info[0] > 2:
    collect_ignore.append("pkg/module_py2.py")

Пусть у вас есть такой модуль:

# content of pkg/module_py2.py
def test_only_on_python2():
    try:
        assert 0
    except Exception, e:
        pass

и макет файла setup.py:

# content of setup.py
0 / 0  # если будет импортирован - вызовет исключение

Тогда при запуске pytest в интерпретаторе `Python 2 мы соберем 1 тест, а файл``setup.py`` будет проигнорирован:

#$ pytest --collect-only
====== test session starts ======
platform linux2 -- Python 2.7.10, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 1 items
<Module 'pkg/module_py2.py'>
  <Function 'test_only_on_python2'>

====== no tests ran in 0.04 seconds ======

А при запуске на `Python 3 будут проигнорированы все файлы:

$ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 0 items

========================== no tests ran in 0.12s ===========================

Для определения файлов, которые должны быть пропущены, можно также добавлять в collect_ignore_glob шаблоны в стиле Unix

В следующем примере в conftest.py игнорируется файл setup.py и все файлы, которые оканчиваются на *_py2.py и запускаются с помощью python версии 3 и выше:

# content of conftest.py
import sys

collect_ignore = ["setup.py"]
if sys.version_info[0] > 2:
    collect_ignore_glob = ["*_py2.py"]