Есть хороший шаблон для скриптов: вы пишете их тесты прямо в скрипте. Это позволяет обойти любые проблемы с импортом и просто отправлять код и тесты вместе.

Этот подход отлично работает, если у вас есть только простые тесты:

def my_production_function(x):
  if x > 0:
    return x+1
  else:
    raise Exception("Negative")


### TESTS HERE


def test_1():
  assert my_production_function(1) == 2

Но что мы не можем импортировать что-либо связанное с тестами в верхний уровень модуля. Может быть, этот код будет работать в среде, где у нас нет pytest. С простыми тестами все просто: у вас просто есть неиспользуемая функция (test_1 и т. д.), которая игнорируется при обычном выполнении кода. Если вы пишете import pytest в начале файла, у вас должен быть запущен pytest, что странно.

Но, допустим, вы хотите проверить свою функцию на наличие исключений. Как? Мой старый подход заключался в том, чтобы импортировать pytest внутри теста:

def test_0():
   import pytest
   pytest.raises(Exception):
      my_production_function(0)

Все было в порядке… пока не пройдет один или два теста. Если у вас их больше, множественный импорт замедляет работу и выглядит некрасиво.

Сегодня я нашел красивое решение. Посмотри на это!

def setup_module():
  global pytest
  import pytest


def test_0():
  with pytest.raises(Exception):
    my_production_function(0)


def test_negative():
  with pytest.raises(Exception):
    my_production_function(-1)

Хитрость заключается в setup_module, где вы можете иметь глобальные переменные (верхнего уровня). А модуль - это переменная! Pytest запускает setup_module перед запуском тестов, поэтому он импортирует модуль pytest. Это аккуратный трюк в стиле pytest.