我正在使用pytest来测试将数据推送到数据库的网络抓取工具。该类仅提取html并将html推送到要稍后解析的数据库。我的大多数测试都使用伪数据表示html。
我想做一个测试,从网站上刮掉一个网页,但是我希望测试自动关闭,除非指定。如果您有一个昂贵或耗时的测试不想一直运行,那么可能会发生类似的情况。
我期望某种标记可以抑制测试,除非我让pytest运行所有抑制的测试,但是我没有在文档中看到它。
试图使用skipif标记,并使用命令提示符pytest test_file.py 1
和以下测试文件中的以下代码使用此命令为python脚本提供参数。问题是,当我尝试为test_file提供参数时,pytest期望它是另一个文件名,因此我收到一条错误消息:“在0.00秒内没有运行测试,错误:找不到文件:1”
if len(sys.argv) == 1:
RUN_ALL_TESTS = False
else:
RUN_ALL_TESTS = True
...
# other tests
...
@pytest.mark.skipif(RUN_ALL_TESTS)
def test_scrape_website():
...
我也许可以将测试视为一种夹具并使用@pytest.fixture(autouse=False)
,但不确定如何覆盖autouse变量
How to skip a pytest using an external fixture?中提出了类似的解决方案,但是这种解决方案似乎比我需要的解决方案更为复杂。
答案 0 :(得分:3)
文档准确描述了您的问题:https://docs.pytest.org/en/latest/example/simple.html#control-skipping-of-tests-according-to-command-line-option。从那里复制:
这是一个conftest.py文件,在其中添加了--runslow命令行选项 控制跳过pytest.mark.slow标记测试:
# content of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption(
"--runslow", action="store_true", default=False, help="run slow tests"
)
def pytest_collection_modifyitems(config, items):
if config.getoption("--runslow"):
# --runslow given in cli: do not skip slow tests
return
skip_slow = pytest.mark.skip(reason="need --runslow option to run")
for item in items:
if "slow" in item.keywords:
item.add_marker(skip_slow)
我们现在可以编写这样的测试模块:
# content of test_module.py
import pytest
def test_func_fast():
pass
@pytest.mark.slow
def test_func_slow():
pass
答案 1 :(得分:3)
有两种方法可以解决此问题,但我将介绍在Python基准中见过的两种常见方法。
1)通过将“可选”测试放在另一个目录中来分离测试。
不确定项目的布局是什么样,但是您可以执行以下操作(仅测试目录很重要,其余只是玩具示例布局):
README.md
setup.py
requirements.txt
test/
unit/
test_something.py
test_something_else.py
integration/
test_optional.py
application/
__init__.py
some_module.py
然后,在调用pytest时,如果要运行 just 单元测试(即仅pytest test/unit
文件)或{{ 1}}如果要运行 just 集成测试(即仅test_something*.py
),或者pytest test/integration
如果要运行 all 测试。因此,默认情况下,您可以只运行test_optional.py
。
我建议将这些调用包装在某种脚本中。我更喜欢pytest test
,因为它对于这种包装非常有用。然后,您可以说pytest unit/test
,它只运行您的默认(快速)测试套件或make
,它将运行所有测试(可能很慢,也可能不会很慢)。
示例包装文件,您可以使用:
make test
2)使用make test_all
装饰器明智地标记测试,但使用环境变量作为触发器
我不太喜欢这种解决方案,这让我感到有些无所事事(很难确定在任何给定.PHONY: all clean install test test_int test_all uninstall
all: install
clean:
rm -rf build
rm -rf dist
rm -rf *.egg-info
install:
python setup.py install
test: install
pytest -v -s test/unit
test_int: install
pytest -v -s test/integration
test_all: install
pytest -v -s test
uninstall:
pip uninstall app_name
运行中正在运行哪组测试)。但是,您可以做的是定义一个环境变量,然后将该环境变量连接到模块中以检测是否要运行所有测试。环境变量取决于外壳,但是我假装您有一个bash环境,因为它是流行的外壳。
您可以仅针对快速的单元测试执行@pytest.mark.skipif
(因此这将是您的默认设置),或者针对所有测试执行pytest
。然后,在测试文件中,您可以执行以下操作:
export TEST_LEVEL="unit"
注意:将测试级别命名为“单位”和“集成”无关。您可以随意命名。您还可以具有许多级别(例如每晚测试或性能测试)。
此外,我认为选项1是最好的选择,因为它不仅清楚地允许测试分离,而且还可以为测试的含义和表示增加语义和清晰度。但是软件中没有“一刀切”的解决方案,您必须根据自己的具体情况来决定采用哪种方法。
HTH!
答案 2 :(得分:3)
一个非常简单的解决方案是使用-k
参数。您可以使用-k
参数取消选择某些测试。 -k
尝试将其参数与测试名称或标记的任何部分匹配。您可以使用not
反转匹配(也可以使用布尔运算符and
和or
)。因此,-k 'not slow'
跳过名称中具有“ slow”,名称中具有“ slow”的标记或其类/模块名称包含“ slow”的测试。
例如,给定此文件:
import pytest
def test_true():
assert True
@pytest.mark.slow
def test_long():
assert False
def test_slow():
assert False
运行时:
pytest -k 'not slow'
输出类似:(请注意,两个失败的测试都与过滤器匹配,因此被跳过了)
============================= test session starts =============================
platform win32 -- Python 3.5.1, pytest-3.4.0, py-1.5.2, pluggy-0.6.0
rootdir: c:\Users\User\Documents\python, inifile:
collected 3 items
test_thing.py . [100%]
============================= 2 tests deselected ==============================
=================== 1 passed, 2 deselected in 0.02 seconds ====================
由于热切的匹配,您可能需要执行一些操作,例如将所有单元测试放在名为unittest
的目录中,然后将慢速单元测试标记为slow_unittest
(以便意外地匹配碰巧名字有点慢)。然后,您可以使用-k 'unittest and not slow_unittest'
来匹配所有快速单元测试。
答案 3 :(得分:1)
形成一个小类,以便在多个mark / cli选项上重复使用@xverges代码;
@dataclass
class TestsWithMarkSkipper:
''' Util to skip tests with mark, unless cli option provided. '''
test_mark: str
cli_option_name: str
cli_option_help: str
def pytest_addoption_hook(self, parser):
parser.addoption(
self.cli_option_name,
action="store_true",
default=False,
help=self.cli_option_help,
)
def pytest_collection_modifyitems_hook(self, config, items):
if not config.getoption(self.cli_option_name):
self._skip_items_with_mark(items)
def _skip_items_with_mark(self, items):
reason = "need {} option to run".format(self.cli_option_name)
skip_marker = pytest.mark.skip(reason=reason)
for item in items:
if self.test_mark in item.keywords:
item.add_marker(skip_marker)
用法示例(必须放在conftest.py
中):
slow_skipper = TestsWithMarkSkipper(
test_mark='slow',
cli_option_name="--runslow",
cli_option_help="run slow tests",
)
pytest_addoption = slow_skipper.pytest_addoption_hook
pytest_collection_modifyitems = slow_skipper.pytest_collection_modifyitems_hook