自动化

pytest定义结束后的清理行为的几种方式

目的是在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)