pytest–Cache管理跨测试的状态
目录
一、用法
件提供了两个命令行参数用于在测试失败的时候重新运行失败的用例:
1、–lf, --last-failed 只重新运行失败的用例
2、--ff,--failed-first 先重新运行失败的用例,然后运行其他的
3、pytest
会将本轮测试的执行状态写入到.pytest_cache
文件夹,这个行为是由自带的cacheprovider
插件来实现的,pytest
默认将测试执行的状态写入到根目录中的.pytest_cache
文件夹,我们也可以通过在pytest.ini
中配置cache_dir
选项来自定义缓存的目录,它可以是相对路径,也可以是绝对路径;
[pytest]
cache_dir = .pytest-cache
4、为了执行清理(通常情况下不需要),使用 --cache-clear 允许我们在测试运行之前删除所有的之前运行的cache。 其他的插件可以在pytest执行的时候访问 config.cache 对象来获取一个json格式的值。
二、重新执行失败用例或先执行失败用例
构造用例,执行5个测试,其中两个会失败
import pytest
@pytest.mark.parametrize("i",range(5))
def test_num(i):
if i >2:
pytest.fail("num too large")
执行测试:
=========================short test summary info =========================
FAILED f/test_cache.py::test_num[3] - Failed: num too large
FAILED f/test_cache.py::test_num[4] - Failed: num too large
2 failed, 3 passed in 0.10s
再用 pytest --lf 运行:
========================= short test summary info ==========================
FAILED f/test_cache.py::test_num[3] - Failed: num too large
FAILED f/test_cache.py::test_num[4] - Failed: num too large
========================= 2 failed, 3 deselected in 0.06s =====================
上一次运行中,你只运行了两个之前失败的测试,其余3个测试没有被运行。
现在使用 --ff 选项,所有的测试都会被运行,但是之前失败的用例会被先运行(从一连串的FF和点中可以看出):
======================= short test summary info ========================
FAILED f/test_cache.py::test_num[3] - Failed: num too large
FAILED f/test_cache.py::test_num[4] - Failed: num too large
======================= 2 failed, 3 passed in 0.07s ==============
三、--cache-clear
:先清除所有缓存,再执行用例
# _pytest/cacheprovider.py
class Cache:
...
@classmethod
def for_config(cls, config):
cachedir = cls.cache_dir_from_config(config)
if config.getoption("cacheclear") and cachedir.exists():
rm_rf(cachedir)
cachedir.mkdir()
可以看到,它会先把已有的缓存文件夹删除(rm_rf(cachedir)
),再创建一个空的同名文件夹(cachedir.mkdir()
),这样会导致上述的功能失效,所以一般不使用这个命令;
四、上次没有测试失败时候的行为
清除缓存,再执行测试
D:\pytest\learn01>pytest --cache-clear -q -s f/test_cache.py
.....
5 passed in 0.12s
我们再去看一下缓存目录
因为没有失败的用例,所以不会生成lastfailed
文件,那么这个时候在使用--lf
和--ff
D:\pytest\learn01>pytest --ff f/test_cache.py
f\test_cache.py ..... [100%] ========== 5 passed in 0.02s ===========
D:\pytest\learn01>pytest --lf f/test_cache.py
f\test_cache.py ..... [100%]
============ 5 passed in 0.03s ===============
可以看到,它们没有实施任何影响,查看源码:
# _pytest/cacheprovider.py
class LFPlugin:
def __init__(self, config):
...
self.lastfailed = config.cache.get("cache/lastfailed", {})
...
def pytest_collection_modifyitems(self, session, config, items):
...
if self.lastfailed:
...
else:
self._report_status = "no previously failed tests, "
if self.config.getoption("last_failed_no_failures") == "none":
self._report_status += "deselecting all items."
config.hook.pytest_deselected(items=items)
items[:] = []
else:
可以看到,当self.lastfailed判断失败时,如果我们指定了last_failed_no_failures选项为none,pytest会忽略所有的用例(items[:] = []),否则不做任何修改(和没加--lf或--ff一样),而判断self.lastfailed的依据是就是lastfailed文件;
#_pytest/cacheprovider.py
group.addoption(
"--lfnf",
"--last-failed-no-failures",
action="store",
dest="last_failed_no_failures",
choices=("all", "none"),
default="all",
help="which tests to run with no previously (known) failures.",
执行测试:
D:\pytest\learn01>pytest -q -s --ff --lfnf none f/test_cache.py
5 deselected in 0.01s
D:\pytest\learn01>pytest -q -s --ff --lfnf all f/test_cache.py
.....
5 passed in 0.02s
五、 config.cache 对象
def output():
print("just output")
@pytest.fixture
def mydata(request):
val = request.config.cache.get("example/value", None)
if val is None:
output()
val = 23
request.config.cache.set("example/value", val)
return val
def test_function(mydata):
assert mydata == 23
执行一次用例:
D:\pytest\learn01>pytest -q -s f/test_cache2.py
just output
.
1 passed in 0.05s
这个时候,缓存中没有example/value
,将val
的值写入缓存,终端打印just output,查看缓存,其中新加了一个文件:.pytest-cache/v/example/value
;
通过--cache-show
选项查看value值
D:\pytest\learn01>pytest -q -s --cache-show example/value
cachedir: D:\pytest\learn01\.pytest_cache
--------------------- cache values for 'example/value' ------------------------
example\value contains:
23
pytest --cache-show
查看所有cache信息,可以使用一个模式通配符来对于结果进行一个过滤
六、Stepwise
想要在遇到第一个失败的用例时退出执行,并且下次还是从这个用例开始执行;
def test_one():
assert 1
def test_two():
assert 0
def test_three():
assert 1
def test_four():
assert 0
def test_five():
assert 1
执行测试:
D:\pytest\learn01>pytest --cache-clear --sw -s -q f/test_cache3.py
.F
=========================== FAILURES ===========================
____________________________ test_two ________________________
def test_two():
> assert 0
E assert 0
f\test_cache3.py:6: AssertionError
=========================== short test summary info ============================
FAILED f/test_cache3.py::test_two - assert 0
!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: Test failed, continuing from this test next run. !!!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 1 passed in 0.14s
使用--cache-clear清除之前的缓存,使用--sw, --stepwise使其在第一个失败的用例处退出执行;
现在我们的缓存文件中lastfailed记录了这次执行失败的用例,即为test_two();nodeids记录了所有的测试用例;特殊的是,stepwise记录了最近一次失败的测试用例,这里也是test_two();
接下来,我们用--sw的方式再次执行:pytest首先会读取stepwise中的值,并将其作为第一个用例开始执行;
D:\pytest\learn01>pytest --sw -s -q f/test_cache3.py
F
====================== FAILURES =======================
_______________________ test_two _________________________
def test_two():
> assert 0
E assert 0
f\test_cache3.py:6: AssertionError
=====================short test summary info =====================
FAILED f/test_cache3.py::test_two - assert 0
!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: Test failed, continuing from this test next run. !!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 1 deselected in 0.09s
可以看到,test_two()
作为第一个用例开始执行,在第一个失败处退出;其实,pytest
还提供了一个--stepwise-skip
的命令行选项,它会忽略第一个失败的用例,在第二个失败处退出执行
D:\pytest\learn01>pytest --sw -s --stepwise-skip -q f/test_cache3.py
F.F
====== FAILURES =======
_______ test_two ________
def test_two():
> assert 0
E assert 0
f\test_cache3.py:6: AssertionError
______ test_four _______
def test_four():
> assert 0
E assert 0
f\test_cache3.py:14: AssertionError
==================== short test summary info =====================
FAILED f/test_cache3.py::test_two - assert 0
FAILED f/test_cache3.py::test_four - assert 0
!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: Test failed, continuing from this test next run. !!!!!!!!!!!!!!!!!!!!!!!!
2 failed, 1 passed, 1 deselected in 0.11s
这个时候,在第二个失败的用例test_four()
处退出执行,同时stepwise
文件的值也改成了""f/test_cache3.py::test_four""
;
参考文献:https://blog.csdn.net/weixin_44626980/article/details/103349675