Встраивание тестов в один модуль с реализацией

#pytest

Вопрос:

У меня есть несколько служебных модулей, которые независимы и слишком малы (~20 строк), чтобы гарантировать наличие собственной tests папки. Существует ли какое-либо соглашение о встраивании тестов pytest в один модуль с кодом, чтобы все это было компактно? Тестам не потребуется использовать какие-либо расширенные функции pytest (приспособления, области и т.д.).

Напр.

 """My beautiful sum helper"""

def sum_helper(a, b):
    return a b


def test_sum_helper():
   assert sum_helper(1, 2) == 3

 

Я понимаю, что для этого соглашения может потребоваться специальный аргумент запуска для pytest команды.

Комментарии:

1. docs.pytest.org/en/6.2.x/… не совсем то, что вы описали, но это самое близкое.

Ответ №1:

Это плохая практика, так как ваш метод тестирования является частью вашего производственного кода. Этого следует избегать.

ИМХО, вместо этого, как упоминал @Guy, у вас может быть tests папка, и вместо создания вложенных папок или модулей внутри папки тестов у вас может быть общий тестовый модуль под названием test_utils, и там вы можете добавлять тесты для всех ваших служебных модулей.

Да, вы правильно упомянули, что для того, чтобы они рассматривались как тест, вам нужно обойти, что само по себе показывает, что это анти-шаблон. Пожалуйста, дайте мне знать, если вам понадобится дополнительная информация.

Комментарии:

1. Спасибо Феликсу. Я знаю, что это плохая практика, так как я разрабатываю Python уже 20 лет. Но иногда вам нужно сделать что-то практичное, а не теоретически обоснованное. Я сам во всем разберусь.

2. Пожалуйста, посмотрите мой ответ выше.

3. @MikkoOhtamaa, если это действительно насущное требование/необходимость, определенно. Я думаю, что вы можете выполнять тесты, встроенные в ваш основной модуль(ы), следующим образом: pytest src/<utilities package>/* -v например: pytest src/utils/* -v я протестировал, и pytest может распознать тест(ы)

Ответ №2:

Вам просто нужно сказать pytest , чтобы вы обнаружили тесты в папке пакета и заглянули во все *.py файлы, а не только test_*.py в файлы. Это опция python_files.

Вам может понадобиться, а может и не понадобиться давать testpaths pytest кому-то . Если вы используете относительный .. импорт, pytest может столкнуться с трудностями, если вы не дадите явных --rootdir указаний .

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

 pytest -o "python_files='*.py'" -o "testpaths=src tests" 
 

Похоже, нет заметного наказания за поиск тестов в пути реализации — pytest, как правило, проверяет файлы очень быстро. Если вы думаете, что это безобразно, вы можете просто пропустить import pytest проверку.

Вот пример реализации с прилагаемыми тестами, с нулевыми штрафами за время выполнения. Тесты находятся в конце модуля и включаются только в том случае, если pytest он установлен.

 """CAIP handling.

https://github.com/ChainAgnostic/CAIPs
"""
from dataclasses import dataclass

from eth_utils import is_checksum_address


class BadChainAddressTuple(Exception):
    pass


class InvalidChainId(BadChainAddressTuple):
    pass


class InvalidChecksum(BadChainAddressTuple):
    pass


@dataclass
class ChainAddressTuple:
    """Present one chain-agnostic address"""
    chain_id: int
    address: str

    @staticmethod
    def parse_naive(v: str):
        """
        Example tuple: `1:0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc` - ETH-USDC on Uniswap v2
        """
        assert type(v) == str

        if not v:
            raise BadChainAddressTuple("Empty string passed")

        parts = v.split(":")

        if len(parts) != 2:
            raise BadChainAddressTuple(f"Cannot split chain id in address {v}")

        address = parts[1]
        if not is_checksum_address(address):
            raise InvalidChecksum("Address checksum or format invalid")

        try:
            chain_id = int(parts[0])
        except ValueError:
            raise InvalidChainId(f"Invalid chain_id on {v}")

        if chain_id <= 0:
            raise InvalidChainId("Invalid chain_id")

        return ChainAddressTuple(chain_id, address)


# Run tests with pytest -o "python_files='*.py'"
try:
    # We assume pytest is not installed in the production environment,
    # thus including the test code as part of the module does not incur any extra penalty
    import pytest

    def test_caip_parse_naive():
        tuple = ChainAddressTuple.parse_naive("1:0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc")
        assert tuple.chain_id == 1
        assert tuple.address == "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"


    def test_caip_bad_checksum():
        # Notive lower b, as Ethereum encodes the checksum in the hex capitalisation
        with pytest.raises(InvalidChecksum):
            ChainAddressTuple.parse_naive("1:0xb4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc")

except ImportError:
    # pytest not installed, tests not available
    pass