将测试嵌入到与实现相同的模块中
Embedding tests in the same module with the implementation
我有一些独立且太小(~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
命令的特殊启动参数。
这是一种不好的做法,因为您的测试方法是生产代码的一部分。这是应该避免的事情。
恕我直言,正如@Guy 提到的那样,您可以拥有一个 tests
文件夹,而不是在测试文件夹中创建子文件夹或模块,您可以拥有一个名为 test_utils 的通用测试模块,您可以在其中添加测试所有实用程序模块。
是的,您正确地提到要将这些视为测试,您需要变通,这本身就是一种反模式。
如果您需要更多信息,请告诉我。
您只需要告诉 pytest
在您的包文件夹中发现测试并查看所有 *.py
文件而不只是 test_*.py
文件。这是 python_files option.
您可能还需要将 testpaths
提供给 pytest
,也可能不需要。如果你使用相对 ..
导入 pytest 可能会遇到困难,除非你给出明确的 --rootdir
.
运行 pytests
的示例命令
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
我有一些独立且太小(~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
命令的特殊启动参数。
这是一种不好的做法,因为您的测试方法是生产代码的一部分。这是应该避免的事情。
恕我直言,正如@Guy 提到的那样,您可以拥有一个 tests
文件夹,而不是在测试文件夹中创建子文件夹或模块,您可以拥有一个名为 test_utils 的通用测试模块,您可以在其中添加测试所有实用程序模块。
是的,您正确地提到要将这些视为测试,您需要变通,这本身就是一种反模式。 如果您需要更多信息,请告诉我。
您只需要告诉 pytest
在您的包文件夹中发现测试并查看所有 *.py
文件而不只是 test_*.py
文件。这是 python_files option.
您可能还需要将 testpaths
提供给 pytest
,也可能不需要。如果你使用相对 ..
导入 pytest 可能会遇到困难,除非你给出明确的 --rootdir
.
运行 pytests
的示例命令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