目的是在pytest所有的测试脚本都执行完了后,需要执行一个特定的清理工作,要求就是在最终执行,无论是单个还是多个脚本。
有三种方式,在conftest里。
第一种是添加一个finalizer,这种方式是比较老的一种用法,不过仍然有效。
pytest在开始运行时便会将clean_services给添加进去,但只会在结束的时候执行。
@pytest.fixture(autouse=True, scope="session")
def final_step(request):
def clean_services():
# code
request.addfinalizer(clean_services)
第二种是一种主流用法,定义个autouse和session scope,在yield后加上清理代码。
@pytest.fixture(scope="session", autouse=True)
def regression():
# ─── 这里是所有测试开始前 ───
yield
# ─── 这里是所有测试结束后 ───
# code
第三种是用pytest自带流程中的pytest_sessionfinish。不过需要注意的是,conftest有时不是仅仅一份,而是多份,甚至于从第三方的import进来的,可能已经有其他的conftest定义过pytest_sessionfinish了。
比如在当前项目中,根目录有一个conftest,当作公共配置,在子目录里也有conftest,一般来说会先执行上一级的pytest_sessionfinish,然后执行小目录的pytest_sessionfinish,不一定,这取决于注册顺序,但两者都会执行,不会覆盖。
对于第三方导入的conftest,与自定义的conftest里执行的顺序不明确,可以标明一下tryfirst或者trylast,保证正确的执行顺序。
@pytest.hookimpl(trylast=True) # 确保在公共的finish执行完之后,再执行自定义的
def pytest_sessionfinish(session, exitstatus):
print("\n[Local Cleanup] 公共的跑完了,现在执行我自定义的清理")
# code
另外需要注意一下这个覆盖的情况。
如果是pytest_开头的,比如pytest_sessionfinish、pytest_runtest_setup等这种标准生命周期中的Hook,即使我们定义了一个同名的也不会覆盖,但是顺序我们得注意。另外还有一个需要注意的,如果对于其他conftest是以import *这样的方式导入进来的,那么就被覆盖掉了,如果此时又需要用到其他conftest里的各种hook和fixture,那么就不要import *,而是在当前conftest里这样写来保证两个conftest里的都加载进来,同名的hook也不会互相覆盖。(不能放pyproject.toml里)
import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) # 3rd path
pytest_plugins = "3rd_path.conftest"
如果是第三方、不同目录下的conftest里的同名fixture,这是会被覆盖的。如果想保留同名同时也想执行到顺序之前的同名fixture,是可以将上层的同名fixture以参数的方式注入进来,其实这样也顾及到了顺序。
比如上层有一个user_login,现在子目录也有一个user_login,可以这样来写:
@pytest.fixture()
def user_login(user_login): # 传入同名参数,此时参数user_login代表全局那个fixture的返回值
print("本地同名 fixture 把全局的覆盖了!")
# 但我可以通过参数拿到全局的结果
current_token = user_login["token"]
print(f"拿到全局 token: {current_token},在这里做我本地特有的额外设置")
return user_login
文章评论 (0)