工具美眉在上一篇fixture(一)中简单说了说什么是fixture,如何自定义一个fixture方法来实现测试用例的前置和后置工作。这一篇将重点介绍一下几个常用的pytest自带的fixture。
pytestconfig
def pytest_addoption(parser):parser.addoption("--env",action="store",default="test_env1",help="env to setup before test functions",)@pytest.fixturedef env(pytestconfig):return pytestconfig.getoption("--env")
通过pytestconfig.getoption("--env")获取用户输入的命令行参数。
例如:获取pytest.ini配置文件中的配置信息
[pytest]markers=web_testapp_test...
获取ini配置文件中所有的marker信息
@pytest.fixturedef get_ini(pytestconfig):print(pytestconfig.getini('markers'))
request
request是一个内置的fixture用于向fixture传递参数使用。
举个例子,大部分的测试用例都会要求先登录,那就需要把登录单独抽出来写个fixture函数。其它用例全部的调用这个登陆函数就行。但是登录的账号,密码不能写死,所以需要对fixture函数通过request传参。
# 测试账号数据test_user_data = [{"user": "admin1", "passwd": "111111"},{"user": "admin1", "passwd": ""}]@pytest.fixture(scope="module")def login(request):user = request.param["user"]passwd = request.param["passwd"]print("登录账户:%s" % user)print("登录密码:%s" % passwd)if passwd:return Trueelse:return False# indirect=True 声明login是个函数@pytest.mark.parametrize("login", test_user_data, indirect=True)def test_login(login):"""登录用例"""islogin = loginprint("测试用例中login的返回值:%s" % islogin)assert islogin, "失败原因:密码为空"
除此之外,还可以通过request.addfinalizer,实现在case结束后运行代码,跟上一篇中提到的yield有点类似。
@pytest.fixture()def demo_fixture(request):print("这个fixture在每个case前执行一次")def demo_finalizer():print("在每个case完成后执行的teardown")#注册demo_finalizer为终结函数request.addfinalizer(demo_finalizer)def test_01(demo_fixture):print("\n===执行了case: test_01===")def test_02(demo_fixture):print("\n===执行了case: test_02===")def test_03(demo_fixture):print("\n===执行了case: test_03===")
执行效果就是在每个testcase结束之后,都会运行demo_finalizer方法。
yield和finalizer的区别是什么呢?
1)通过request.addfinalizer的方法可以注册多个cleanup函数,要注意的是执行顺序,与注册的顺序相反。
2)如果使用yield之前的setup的代码发生了任何异常,yield之后的代码将不再执行。但是通过request.addfinalizer注册的终结器,即使fixture的setup部分的代码发生了异常,终结器仍然会继续运行。
cache
cache 是一个可以在测试会话之间共享的缓存对象。pytest 运行完用例之后会生成一个 .pytest_cache 的缓存文件夹,用于记录用例的ids和上一次失败的用例。
| pytest --lf | --last failed | 重跑最后一轮执行的测试用例中失败的case,如果没有失败的,就重跑所有的 |
| pytest --ff | --failed first | 执行所有的测试用例,但是先跑上一轮失败的那些测试用例 |
| pytest --nf | --new-first | 执行所有的测试用例,但是先跑新文件里的测试用例 |
pytest --cache-show | 展示cache的内容 | |
pytest --cache-clear | 清空cache |
可以通过request.config拿到当前测试会话的cache
# content of test_caching.pyimport pytestimport timedef expensive_computation():print("running expensive computation...")@pytest.fixturedef mydata(request):val = request.config.cache.get("example/value", None)if val is None:expensive_computation()val = 42request.config.cache.set("example/value", val)return valdef test_function(mydata):assert mydata == 23
cache提供了两个方法,set和get。
cache.set(key, value):写入缓存
key 参数:缓存存储的路径,是相对于缓存文件 .pytest_cache/v/ 的相对路径,如无此路径会自动创建
value 参数:缓存的值
cache.get(key, default):返回给定键的缓存值,如果尚未缓存任何值或无法读取该值,则返回指定的默认值
key 参数:缓存存储的路径,也是相对于缓存文件 .pytest_cache/v/ 的相对路径;
default 参数:获取不到缓存值时的默认值
monkeypatch
monkeypatch主要是一种pytest的mock test方式,能够动态地在运行时替换一些属性。设想一下以下这种应用场景:你的测试用例中的某个调用特别耗时,且操作复杂。你希望可以mock这个方法,去测试其余的代码是否能够正确执行。比较常见的做法是直接修改这个复杂方法的代码,或者是修改所有调用这个方法的入口。其实你可以并不需要修改太多的代码,利用monkeypatch就可以在运行时,动态将复杂的方法偷梁换柱,变成你的mock方法,看下面这个例子:
class Calculator:@classmethoddef expensive_calculate(cls):# 这是一个执行特别耗时的方法return "response"def test_case1(monkeypatch):def mockreturn():response = "abc"return response# 通过monkeypatch将expensive_calculate方法动态替换成mockreturnmonkeypatch.setattr(Calculator, "expensive_calculate", mockreturn)x = Calculator.expensive_calculate()assert x == "abc"
也可以通过monkeypatch去mock http请求的返回
monkeypatch.setattr(requests, "get", mock_get)
除了以上这些,pytest还自带了tmp_path这样的创建临时文件的fixture方法,本篇就不展开介绍了。
总结一下:pytest自带的好用的fixture
cache ——pytest 缓存,testcase 可以在session级别共享一些信息
monkeypatch——pytest mock工具,可以动态替换object的属性
pytestconfig——可以获取命令行参数、选项、配置文件等
request——用于向fixture传递参数使用,通常与parametrize和indirect=True一起使用
今天的学习就到这里,如果这篇文章对你有帮助,请记得点赞和转发哦👍




