三、fixture(测试 夹具)
1、pytest fixtures的目的是提供一个固定的基线,使测试可以在此基础上可靠地、重复地执行;对比xUnit经典的setup/teardown形式,它在以下方面有了明显的改进:
fixture拥有一个明确的名称,通过声明使其能够在函数、类、模块,甚至整个测试会话中被激活使用;fixture以一种模块化的方式实现。因为每一个fixture的名字都能触发一个fixture函数,而这个函数本身又能调用其它的fixture;fixture的管理从简单的单元测试扩展到复杂的功能测试,允许通过配置和组件选项参数化fixture和测试用例,或者跨功能、类、模块,甚至整个测试会话复用fixture;
2、使用
(1)作为 形参使用

执行过程
pytest收集到测试用例test_ehlo,其有一个形参smtp_connection,pytest查找到一个同名的已经注册的fixture;- 执行
smtp_connection()创建一个smtp_connection实例<smtplib.SMTP object at 0x00000244923A5F60>作为test_ehlo的实参; - 执行test_ehlo(<smtplib.SMTP object at 0x00000244923A5F60>)。
也可以使用命令行调用:pytest --fixtures [testpath]
2、fixture:是一个典型的注入实践
fixture允许测试用例可以轻松的接收和处理特定的需要预初始化操作的应用对象,而不用过分关心导入/设置/清理的细节;这是一个典型的依赖注入的实践,其中,fixture扮演者注入者(injector)的角色,而测试用例扮演者消费者(client)的角色;
3、conftest.py:共享fixture实例
如果想在多个测试模块中共享同一个fixture实例,那么可以把这个fixture移动到conftest.py文件中。在测试模块中不需要手动的导入它,pytest会自动发现,fixture的查找的顺序是:测试类、测试模块、conftest.py、最后是内置和第三方的插件;
还可以利用conftest.py文件的这个特性为每个目录实现一个本地化的插件;
4、共享测试数据
如果想多个测试共享同样的测试数据文件,有两个好方法实现这个:
- 把这些数据加载到
fixture中,测试中再使用这些fixture; - 把这些数据文件放到
tests文件夹中,一些第三方的插件能管理这方面的测试5、作用域:在跨类的、模块的和整个测试会话的用例中,共享fixture实例
在@pytest.fixture装饰器中添加scope='module'参数,使每个测试模块只调用一次smtp_connection(默认每个用例都会调用一次),这样模块中的所有测试用例将会共享同一个fixture实例;其中,scope参数可能的值都有:function(默认值)、class、module、package和session;
例子


注:pytest每次只缓存一个fixture实例,当使用参数化的fixture时,pytest可能会在声明的作用域内多次调用这个fixture;
5、fixture实例化顺序
多个fixture的实例化顺序,遵循以下原则:
- 高级别作用域的(例如:
session)先于低级别的作用域的(例如:class或者function)实例化; - 相同级别作用域的,其实例化顺序遵循它们在测试用例中被声明的顺序(也就是形参的顺序),或者
fixture之间的相互调用关系; - 使能
autouse的fixture,先于其同级别的其它fixture实例化;
其他注意:
-
除了
autouse的fixture,需要测试用例显示声明(形参),不声明的不会被实例化; -
多个相同作用域的
autouse fixture,其实例化顺序遵循fixture函数名的排序;

6、fixture清理操作
(1)yield代理return
将fixture函数中的return关键字替换成yield,则yield之后的代码,就是要做的清理操作;


(2)使用with

(3)使用addfinalizer方法
fixture函数能够接收一个request的参数,表示测试请求的上下文;可以使用request.addfinalizer方法为fixture添加清理函数;

注:在yield之前或者addfinalizer注册之前代码发生错误退出的,都不会再执行后续的清理操作。
7、fixture可以访问测试请求的上下文


8、fixture 返回工厂函数

9、fixture 参数化
不同用例执行同一个fixture,但使用不同的参数,通过指定params关键字参数创建两个fixture实例,每个实例供一轮测试使用,所有的测试用例执行两遍;在fixture的声明函数中,可以使用request.param获取当前使用的入参:


在参数化的fixture中,pytest为每个fixture实例自动指定一个测试ID,例如:上述示例中的test_parames[smtp.163.com]和test_parames[smtp.126.com];
可以使用-k指定用例

或用--collect-only显示测试ID

也可以使用ids关键字参数自定义ID

10、在参数化的fixture 中标记用例
在fixture的params参数中,可以使用pytest.param标记这一轮的所有用例,其用法和在pytest.mark.parametrize中的用法一样

也可以使用pytest.mark.parametrize实现相同的效果:

11、模块化:fixture使用其他的fixture
不仅仅可以在测试用例上使用fixture,还可以在fixture的声明函数中使用其它的fixture;这有助于模块化的设计fixture,可以在多个项目中重复使用框架级别的fixture;


app的作用域是模块级别的,它又调用了smtp_connection_params,也是模块级别的,如果smtp_connection_params是会话级别的作用域,这个例子还是一样可以正常工作的;这是因为低级别的作用域可以调用高级别的作用域,但是高级别的作用域调用低级别的作用域会返回一个ScopeMismatch的异常;
12、高效利用fixture实例
在测试期间,pytest只激活最少个数的fixture实例;如果拥有一个参数化的fixture,所有使用它的用例会在创建的第一个fixture实例并销毁后,才会去使用第二个实例;

mod1的TEARDOWN操作完成后,才开始mod2的SETUP操作;- 用例
test_0独立完成测试; - 用例
test_1和test_2都使用到了模块级别的modarg,同时test_2也使用到了用例级别的otherarg。它们执行的顺序是,test_1先使用mod1,接着test_2使用mod1和otherarg 1/otherarg 2,然后test_1使用mod2,最后test_2使用mod2和otherarg 1/otherarg 2;也就是说test_1和test_2共用相同的modarg实例,最少化的保留fixture的实例个数;
13、在类、模块和项目级别上使用fixture实例
(1)使用usefixtures
如创建临时目录

可以 指定多个 fixture
(2)为测试模块指定fixture
pytestmark = pytest.mark.usefixtures("cleandir")
(3)为整个 项目指定fixture

注:usefixtures标记不适用于fixture声明函数,如下方式声明并不会报错

14、自动使用fixture

autouse=True的fixture在其它级别作用域中的工作流程:
autouse fixture遵循scope关键字的定义:如果其含有scope='session',则不管它在哪里定义的,都将只执行一次;scope='class'表示每个测试类执行一次;- 如果在测试模块中定义
autouse fixture,那么这个测试模块所有的用例自动使用它; - 如果在
conftest.py中定义autouse fixture,那么它的相同文件夹和子文件夹中的所有测试模块中的用例都将自动使用它; - 如果在插件中定义
autouse fixture,那么所有安装这个插件的项目中的所有用例都将自动使用它;
上述的示例中,期望只有TestClass的用例自动调用fixture transact,这样就不希望transact一直处于激活的状态,所以更标准的做法是,将transact声明在conftest.py中,而不是使用autouse=True:


15、在不同的层级上覆写fixture
(1)在文件夹(conftest.py)层级覆写fixture

- 子文件夹
conftest.py中的fixture覆盖了上层文件夹中同名的fixture; - 子文件夹
conftest.py中的fixture可以轻松的访问上层文件夹中同名的fixture;
(2)在模块层级覆写fixture

- 模块中的
fixture覆盖了conftest.py中同名的fixture; - 模块中的
fixture可以轻松的访问conftest.py中同名的fixture;
(3)在用例参数中覆写fixture

fixture的值被用例的参数所覆盖;- 尽管用例
test_username_other没有使用username,但是other_username使用到了username,所以也同样受到了影响;
(4)参数化的fixture覆写非参数化的fixture,反之亦然

- 参数化的
fixture和非参数化的fixture同样可以相互覆盖; - 在模块层级上的覆盖不会影响其它模块;

浙公网安备 33010602011771号