什么是 fixture?
fixture 是 pytest 的精髓,它是测试脚手架,有两个部分组成:
- 用例前置:用例前置的作用就是准备测试用例的前置条件和测试数据,相当于 unittest 中的 setup() 的功能;
- 用例后置:用例后置的作用就是在测试用例执行完毕之后进行前置条件和测试数据的清理、复原等工作,总之就是你想在测试用例执行完之后干的事都在这里干就行了,没有想干的事情也可以空着,相当于 unittest 中的 teardown() 的功能;
使用 @pytest.fixture() 编写 fixture:
- 创建一个 conftest.py 文件,可以创建在项目根目录或测试用例所在的目录中;
- 在 conftest.py 编写 fixture;
# conftest.py
# step 1. 先导入 pytest
import pytest
from selenium import webdriver
# step 2. 使用 @pytest.fixture() 装饰 fixture;
@pytest.fixture()
# step 3. 定义函数,函数名可以自定义,但是最好要有意义;
def fix_init_chrome():
"""启动/关闭 chrome浏览器"""
# step 4. 实现用例前置
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
driver.maximize_window()
# step 5. 返回数据
yield driver
# step 6. 实现用例后置
driver.close()
使用说明:
-
step 2 :必须要用 @pytest.fixture() 装饰函数,才会被注册为 pytest 的 fixture;
-
step 4 :yield 之前的是用例前置部分,用例前置条件在此处实现,例如:初始化chrome,打开百度网页,最大化窗口等;
-
step 5 :yield 是 python 中的迭代器,它的作用:一是将用例前置准备好的测试数据返回给调用方,二是让 python 跳出这个 fixture 函数,去执行测试用例的代码,在测试用例执行完后再回来继续执行 yield 之后的代码;
-
step 6 :yield 之后的是用例后置部分,用例前置条件的拆卸、数据清理在此处实现,例如:关闭chrome浏览器,此处执行完毕后整个用例才算执行完毕;
注意:yield 也可以替换为 return,但是 return 之后就视为此函数的代码执行完毕,return 之后就不能在写用例后置部分的代码,所以建议都使用 yield ,就算不需要用例后置也可以空着!
如何使用 fixture?
fixture 编写好之后如何使用呢?pytest 提供了两种方法来调用 fixture :
使用 @pytest.mark.usefixtures() 装饰器的方法调用:
- @pytest.mark.usefixtures() 既可以装饰测试类,也可以装饰测试方法;
- @pytest.mark.usefixtures() 的参数为 fixture 的名字,必须是 str 类型;
- 可以同时调用多个fixture;
- 注意:用此方法调用 fixture 无法接收 fixture 中 yield 返回的数据;
# test_moduleName.py
import pytest
# 将想要调用的 fixture 的名字作为参数传给装饰器
@pytest.mark.usefixtures('fix_1')
@pytest.mark.usefixtures('fix_2')
class TestClassName:
"""测试类"""
@pytest.mark.usefixtures('fix_init_chrome')
@pytest.mark.usefixtures('fix_init_chrome_new')
def test_func_name(self):
"""测试方法"""
# 测试用例部分
pass
将 fixture 的名字作为测试方法的实参直接调用:
- 将 fixture 的名字作为测试方法的参数,便可调用;
- 也可以同时调用多个fixture;
- 注意:此方法可以在用例中接收 fixture 返回的参数,但是此方法测试类不可使用;
- 两种方法可以结合使用;
# test_moduleName.py
import pytest
class TestClassName:
"""测试类"""
@pytest.mark.userfixtures('fix_1') # 两种调用方法可以同时使用
@pytest.mark.userfixtures('fix_2')
def test_func_name(self, fix_init_chrome, fix_3):
"""测试方法"""
# 测试用例部分
driver = fix_init_chrome
param = fix_3
pass
pytest 如何发现及共享 fixture?
pytest 中调用 fixture 时是遵循 就近查询 原则,上面我们讲到 fixture 是写在 conftest.py 中的,实际上 fixture 还可以写在测试模块中,和测试用例放在一起,但是除非是某个用例专用的 fixture ,否则建议都写在 conftest.py 中方便管理;另外,conftest.py 文件是可以有多个的,当项目需要时,我们可能会将测试用例分成多个 package 管理,那么我们可以在每个 package 下创建一个 conftest.py 文件,还有所有 package 共用的 conftest.py 文件,多个 conftest.py 中可能存在同名的 fixture,那么 pytest 是怎么找到我们想要调用的 fixture呢?
- pytest 真正执行用例之前有个加载配置、fixtures、测试用例的过程,在这个过程中 pytest 或获取到所有 conftest.py 中的 fixture;
- 执行测试用例时,pytest 会先在用例所在的模块(py文件)中检查是否有你要调用的 fixture(按 fixture 名字查询);
- 如果当前模块找不到,那么会去检查当前模块所在的 package 中是否有 conftest.py 文件,如果有就在这个 conftest.py 是否有你想要调用的 fixture;
- 如果第3步没有 conftest.py 文件,或者 conftest.py 中没有想要调用的fixture,那么会找上一级 package 中的 conftest.py,直到最上级的 conftest.py;
- pytest 不会平级跨 package 找别的 conftest.py ,只会向上级查找;只要找到第1个便会停止查找,最终找不到会抛出异常;
fixture 优先级:当前所在模块 → 当前所在 package 的 conftest.py → 上级 package 的 conftest.py → 最上级的 conftest.py
查询所有的 fixtures:
命令行:pytest --fixtures