Изменение стандартного (Python) обнаружения тестов

Игнорирование путей во время сбора тестов

Вы можете легко игнорировать определенные тестовые каталоги и модули во время сбора, передавая опцию --ignore=path на cli. 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/, вы увидите, что pytest собирает только те тест-модули, которые не соответствуют заданным шаблонам:

=========================== 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 shell. Если вы хотите исключить тестовые модули, которые заканчиваются на _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 допускает несколько вариантов --deselect.

Сохранение дубликатов путей, заданных из командной строки

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

pytest path_a path_a

...
collected 1 item
...

Достаточно собрать тесты один раз.

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

pytest --keep-duplicates path_a path_a

...
collected 2 items
...

Поскольку коллектор работает только с каталогами, если вы дважды укажете один тестовый файл, 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 не выполнять поиск в обычных каталогах subversion или sphinx-build или в любом каталоге с префиксом tmp.

Изменение соглашений об именовании

Вы можете настроить различные соглашения об именовании, установив python_files, python_classes и python_functions в вашем configuration file. Вот пример:

# content of pytest.ini
# Example 1: have pytest look for "check" instead of "test"
[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-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project, configfile: pytest.ini
collected 2 items

<Module check_myapp.py>
  <Class CheckMyApp>
    <Function simple_check>
    <Function complex_check>

======================== 2 tests collected in 0.12s ========================

Вы можете проверить наличие нескольких шаблонов glob, добавив пробел между шаблонами:

# Example 2: have pytest look for files with "test" and "example"
# content of pytest.ini
[pytest]
python_files = test_*.py example_*.py

Примечание

опции python_functions и python_classes не имеют эффекта для обнаружения тестов unittest.TestCase, поскольку pytest делегирует обнаружение методов тестового случая коду unittest.

Интерпретация аргументов cmdline как пакетов Python

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

pytest --pyargs unittest2.test.test_skipping -q

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

# 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-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project, configfile: pytest.ini
collected 3 items

<Module CWD/pythoncollection.py>
  <Function test_function>
  <Class TestClass>
    <Function test_method>
    <Function test_anothermethod>

======================== 3 tests collected 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  # will raise exception if imported

Если вы работаете с интерпретатором Python 2, то вы найдете один тест и оставите файл 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'>

====== 1 tests found in 0.04 seconds ======

Если вы запускаете с интерпретатором Python 3, то и один тест, и файл setup.py будут пропущены:

$ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project, configfile: pytest.ini
collected 0 items

======================= no tests collected in 0.12s ========================

Также можно игнорировать файлы на основе подстановочных знаков в стиле Unix shell, добавляя шаблоны к collect_ignore_glob.

Следующий пример 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"]

Начиная с Pytest 2.6, пользователи могут запретить pytest обнаруживать классы, начинающиеся с Test, установив булев атрибут __test__ в значение False.

# Will not be discovered as a test
class TestClass:
    __test__ = False
Вернуться на верх