一、固件使用背景

在执行测试用例时,我们常常需要在测试用例执行的前后去完成一些额外的操作。例如针对于 Web 测试,在用例执行前需要打开浏览器,完成用户登录等一系列前置操作;在用例执行完成后,要清除浏览器缓存,关闭浏览器...... Pytest 框架提供的固件机制(又称为夹具)可以帮我们实现一系列的前后置操作。

我们先创建一套测试用例:

二、前后置函数

1. 测试方法级别

setupteardown 方法作用于每一个测试方法,每个测试方法执行之前都会先去执行 setup 方法,执行之后都会再去执行 teardown 方法。

 1 # cases/test_cases.py
2 class TestCase:
3 ​
4 def setup(self):
5 print("\n测试方法执行前做对应的操作!!!")
6 ​
7 def teardown(self):
8 print("\n测试方法执行后做对应的操作!!!")
9 ​
10 def test_case_001(self):
11 print("模拟测试案例001")
12 ​
13 def test_case_002(self):
14 print("模拟测试案例002")
15 ​
16 def test_case_003(self):
17 print("模拟测试案例003")

需要注意的是:

  • 如果前后置方法是定义在测试类内部的,那么作用的对象是当前测试类中的每一个测试方法,其他测试类和外部的测试方法将不会被影响。

 1 # cases/test_cases.py
2 class TestCase:
3 ​
4 def setup(self):
5 print("\n测试方法执行前做对应的操作!!!")
6 ​
7 def teardown(self):
8 print("\n测试方法执行后做对应的操作!!!")
9 ​
10 def test_case_001(self):
11 print("模拟测试案例001")
12 ​
13 def test_case_002(self):
14 print("模拟测试案例002")
15 ​
16 def test_case_003(self):
17 print("模拟测试案例003")
18 ​
19 ​
20 class TestCase2:
21 def test_case_004(self):
22 print("模拟测试案例004")
23 ​
24 def test_case_005(self):
25 print("模拟测试案例005")
26 ​
27 ​
28 def test_outer_case():
29 print("模拟测试外部的测试方法")

  • 如果前后置方法是定义在测试类外部的,那么作用的对象是定义在外部的测试方法,测试类中的测试方法不会被影响。

 1 # cases/test_cases.py
2 def setup():
3 print("\n测试方法执行前做对应的操作!!!")
4 ​
5 ​
6 def teardown():
7 print("\n测试方法执行后做对应的操作!!!")
8 ​
9 class TestCase:
10 ​
11 def test_case_001(self):
12 print("模拟测试案例001")
13 ​
14 def test_case_002(self):
15 print("模拟测试案例002")
16 ​
17 def test_case_003(self):
18 print("模拟测试案例003")
19 ​
20 ​
21 class TestCase2:
22 def test_case_004(self):
23 print("模拟测试案例004")
24 ​
25 def test_case_005(self):
26 print("模拟测试案例005")
27 ​
28 ​
29 def test_outer_case():
30 print("模拟测试外部的测试方法")

2. 测试类级别

setup_classteardown_class 方法作用于当前的测试类,每个测试类执行之前都会先去执行 setup_class 方法,执行之后都会再去执行 teardown_class 方法。测试方法并不会受到这两个方法的影响。

 1 # cases/test_cases.py
2 class TestCase:
3 ​
4 def test_case_001(self):
5 print("模拟测试案例001")
6 ​
7 def test_case_002(self):
8 print("模拟测试案例002")
9 ​
10 def test_case_003(self):
11 print("模拟测试案例003")
12 ​
13 ​
14 class TestCase2:
15 def setup_class(self):
16 print("\n测试方法执行前做对应的操作!!!")
17 ​
18 def teardown_class(self):
19 print("\n测试方法执行后做对应的操作!!!")
20 ​
21 def test_case_004(self):
22 print("模拟测试案例004")
23 ​
24 def test_case_005(self):
25 print("模拟测试案例005")

三、装饰器实现

使用前后置函数的方式作用的对象都是一个模块内或者是一个测试类中的全体对象,没有办法做到只作用于部分对象。Pytest 提供了 @pytest.fixture() 方法来实现部分用例的前后置操作。

1. 简单使用

  • 第一步,先自定义一个生成器方法

1 def my_fixture():
2 print("前置操作")
3 yield
4 print("后置操作")
  • 第二步,添加装饰器方法

1 import pytest
2 ​
3 @pytest.fixture()
4 def my_fixture():
5 print("前置操作")
6 yield
7 print("后置操作")
  • 第三步,将函数名作为参数,传递给需要做前后置操作的测试方法。测试方法在执行前会先去执行生成器函数中 yield 的前半部分代码;测试方法执行完成后,会去执行生成器函数中 yield 的后半部分代码。

 1 # cases/test_cases.py
2 import pytest
3 ​
4 @pytest.fixture()
5 def my_fixture():
6 print("前置操作")
7 yield
8 print("后置操作")
9 ​
10 class TestCase:
11 ​
12 def test_case_001(self, my_fixture):
13 print("模拟测试案例001")
14 ​
15 def test_case_002(self):
16 print("模拟测试案例002")
17 ​
18 def test_case_003(self):
19 print("模拟测试案例003")

2. 相关参数详解

2.1 autouse

值为 True 时,表示固件自动执行,即不需要在对应的测试方法中引用也会触发,默认值为 False

 1 # cases/test_cases.py
2 import pytest
3 ​
4 @pytest.fixture(autouse=True)
5 def my_fixture():
6 print("\n前置操作")
7 yield
8 print("\n后置操作")
9 ​
10 ​
11 class TestCase:
12 # 并未传入固件使用
13 def test_case_001(self):
14 print("模拟测试案例001")
15 ​
16 def test_case_002(self):
17 print("模拟测试案例002")
18 ​
19 def test_case_003(self):
20 print("模拟测试案例003")

2.2 scope

表示的是被 @pytest.fixture 标记的方法的作用域,有以下几个值:

  • function:作用于测试方法级别,每个函数或方法都会调用

 1 # cases/test_cases.py
2 import pytest
3
4 @pytest.fixture(scope="function", autouse=True)
5 def my_fixture():
6 print("\n前置操作")
7 yield
8 print("\n后置操作")
9
10
11 class TestCase:
12
13 def test_case_001(self):
14 print("模拟测试案例001")
15
16 def test_case_002(self):
17 print("模拟测试案例002")
18
19 def test_case_003(self):
20 print("模拟测试案例003")

  • class:作用于测试类级别,测试类执行时会执行一次固件

 1 # cases/test_cases.py
2 import pytest
3
4 @pytest.fixture(scope="class", autouse=True)
5 def my_fixture():
6 print("\n前置操作")
7 yield
8 print("\n后置操作")
9
10 class TestCase:
11
12 def test_case_001(self):
13 print("模拟测试案例001")
14
15 def test_case_002(self):
16 print("模拟测试案例002")
17
18 def test_case_003(self):
19 print("模拟测试案例003")
20
21 class TestCase2:
22
23 def test_case_004(self):
24 print("模拟测试案例004")
25
26 def test_case_005(self):
27 print("模拟测试案例005")

  • module:作用于测试模块,测试模块(即 py 文件)执行时会执行一次固件

  • session:作用于会话,可以跨多个.py 文件,若多个模块中的用例都调用了 fixture,只会运行一次

 1 ####################### cases/test_cases.py ########################
2 import pytest
3
4 @pytest.fixture(scope="session", autouse=True)
5 def my_fixture():
6 print("\n前置操作")
7 yield
8 print("\n后置操作")
9
10 class TestCase:
11
12 def test_case_001(self):
13 print("模拟测试案例001")
14
15 def test_case_002(self):
16 print("模拟测试案例002")
17
18 def test_case_003(self):
19 print("模拟测试案例003")
20
21 ####################### cases/test_cases_2.py ##############################
22 class TestCase2:
23
24 def test_case_004(self):
25 print("模拟测试案例004")
26
27 def test_case_005(self):
28 print("模拟测试案例005")

2.3 params

使用装饰器方的固件,还可以进行参数传递。参数的类型支持以下四种:

  • 列表

  • 元组

  • 字典列表:[{},{},{}]

  • 字典元组:({},{},{})

我们先打印一下测试方法中接收的固件名的值是啥

 1 import pytest
2
3 @pytest.fixture(scope="function")
4 def my_fixture():
5 print("一些操作......")
6
7 class TestCase:
8
9 def test_case_001(self, my_fixture):
10 print(f"模拟测试案例001----{my_fixture}")
11
12 def test_case_002(self):
13 print("模拟测试案例002")
14
15 def test_case_003(self):
16 print("模拟测试案例003")

在固件中我们尝试返回一个值试试:

 1 import pytest
2
3 @pytest.fixture(scope="function")
4 def my_fixture():
5 print("一些操作......")
6 return "success"
7
8 class TestCase:
9
10 def test_case_001(self, my_fixture):
11 print(f"模拟测试案例001----{my_fixture}")
12
13 def test_case_002(self):
14 print("模拟测试案例002")
15
16 def test_case_003(self):
17 print("模拟测试案例003")

可见在测试方法中传入固件名,除了可以执行固件对应的操作,还可以拿到固件的返回值。那么想结合 params 参数在固件中传值就十分容易了:

 1 import pytest
2
3 @pytest.fixture(scope="function", params=["aaa", "bbb", "ccc"])
4 def my_fixture(request): # 固定写法,使用参数时必须接收一个request变量
5 print("一些操作......")
6 return request.param # 固定写法,返回参数
7
8 class TestCase:
9
10 def test_case_001(self, my_fixture):
11 print(f"模拟测试案例001----{my_fixture}")
12
13 def test_case_002(self):
14 print("模拟测试案例002")
15
16 def test_case_003(self):
17 print("模拟测试案例003")

被标记的测试方法被执行了三次,是因为传的参数有三个值,每执行一次就会传一个值过去。

如果固件要执行前后置操作,就不能用 return 返回值了,要使用 yield:

 1 import pytest
2
3 @pytest.fixture(scope="function", params=["aaa", "bbb", "ccc"])
4 def my_fixture(request): # 固定写法,使用参数时必须接收一个request变量
5 print("前置操作......")
6 yield request.param # 固定写法,返回参数
7 print("后置操作......")
8
9 class TestCase:
10
11 def test_case_001(self, my_fixture):
12 print(f"模拟测试案例001----{my_fixture}")
13
14 def test_case_002(self):
15 print("模拟测试案例002")
16
17 def test_case_003(self):
18 print("模拟测试案例003")

2.4 ids

当固件使用 params 进行传值时,给每一个参数值设置一个单独的 id(意义不是很大)

 1 import pytest
2
3 @pytest.fixture(scope="function", params=["aaa", "bbb", "ccc"], ids=["parm_1", "parm_2", "parm_3"])
4 def my_fixture(request): # 固定写法,使用参数时必须接收一个request变量
5 print("前置操作......")
6 yield request.param # 固定写法,返回参数
7 print("后置操作......")
8
9 class TestCase:
10
11 def test_case_001(self, my_fixture):
12 print(f"模拟测试案例001----{my_fixture}")
13
14 def test_case_002(self):
15 print("模拟测试案例002")
16
17 def test_case_003(self):
18 print("模拟测试案例003")

2.5 name

给标记的固件方法起一个别名,后续传参都使用该别名。

 1 import pytest
2
3 @pytest.fixture(scope="function", name="init")
4 def my_fixture():
5 print("前置操作......")
6 yield
7 print("后置操作......")
8
9 class TestCase:
10 # 测试方法中传参不再是固件函数的名字,而是别名
11 def test_case_001(self, init):
12 print(f"模拟测试案例001")
13
14 def test_case_002(self):
15 print("模拟测试案例002")
16
17 def test_case_003(self):
18 print("模拟测试案例003")

注意:一旦使用别名,就不能再使用原来的函数名作为参数传递了,否则会报错。

 1 import pytest
2
3 @pytest.fixture(scope="function", name="init")
4 def my_fixture():
5 print("前置操作......")
6 yield
7 print("后置操作......")
8
9 class TestCase:
10 # 固件方法起了别名init,但是测试方法中仍然使用原固件函数名my_fixture作为参数传参
11 def test_case_001(self, my_fixture):
12 print(f"模拟测试案例001")
13
14 def test_case_002(self):
15 print("模拟测试案例002")
16
17 def test_case_003(self):
18 print("模拟测试案例003")

四、conftest.py 实现

为了避免代码的冗余,测试用例公共的前后置操作往往会抽离出来而不是重复写在每个用例之中。在 pytest 中,全局公共的前后置操作要求写在一个名为 conftest.py 的文件中。该文件主要有以下特点:

  • conftest.py 文件是单独存放的一个夹具配置文件,且文件名不能更改,必须是这个名字;

  • conftest.py 常常和 fixture 装饰器联合使用,实现测试项目用例全局的前后置操作,且定义在 conftest.py 中的固件函数可以被不同的 py 文件引用;

  • 原则上,conftest.py 文件需要和执行的用例放在同一层级上(实际上也可以放到其他目录中),且不需要做任何的 import 操作。

下面我们就用几个实际的示例来感受以下 conftest.py 的强大:

首先,我们创建一套测试用例环境:

我们先忽略conftest.py ,还是按照一开始使用装饰器定义固件的方式创建用例:

 1 ############# product/test_product.py #############
2 import pytest
3
4 @pytest.fixture(name="init_product")
5 def init():
6 print("\n产品测试前置操作....")
7 yield
8 print("\n产品测试后置操作....")
9
10 def test_product_case(init_product):
11 print("模拟产品测试......")
12
13
14 ############# user/test_user.py ##################
15 import pytest
16
17 @pytest.fixture(name="init_user")
18 def init():
19 print("\n用户测试前置操作....")
20 yield
21 print("\n用户测试后置操作....")
22
23 def test_user_case(init_user):
24 print("模拟用户测试......")

此时,我们尝试在两个测试用例里面分别调用对方用例中的固件函数:

 1 ############# product/test_product.py #############
2 import pytest
3
4 @pytest.fixture(name="init_product")
5 def init():
6 print("\n产品测试前置操作....")
7 yield
8 print("\n产品测试后置操作....")
9
10 def test_product_case(init_product,init_user):
11 print("模拟产品测试......")
12
13
14 ############# user/test_user.py ##################
15 import pytest
16
17 @pytest.fixture(name="init_user")
18 def init():
19 print("\n用户测试前置操作....")
20 yield
21 print("\n用户测试后置操作....")
22
23 def test_user_case(init_user,init_product):
24 print("模拟用户测试......")

结果是显而易见的,由于固件不是定义在当前的 py 文件中的,跨 py 文件引用且不导入,肯定报错。接下来我们将每个模块的固件分别迁移到各自的 conftest.py 下:

 1 ############# product/conftest.py #############
2 import pytest
3
4 @pytest.fixture(name="init_product")
5 def init():
6 print("\n产品测试前置操作....")
7 yield
8 print("\n产品测试后置操作....")
9
10
11 ############# product/test_product.py #############
12 def test_product_case(init_product):
13 print("模拟产品测试......")
14
15
16
17 ############# user/conftest.py ##################
18 import pytest
19
20 @pytest.fixture(name="init_user")
21 def init():
22 print("\n用户测试前置操作....")
23 yield
24 print("\n用户测试后置操作....")
25
26
27 ############# user/test_user.py ##################
28 def test_user_case(init_user):
29 print("模拟用户测试......")

执行用例通过,可见测试用例在执行时会去自己模块下的 conftest.py 中寻找对应的固件执行。当然,此时想要调用对方的固件还是没法调用的:

1 ############# product/test_product.py #############
2 def test_product_case(init_product, init_user):
3 print("模拟产品测试......")
4
5
6 ############# user/test_user.py ##################
7 def test_user_case(init_user, init_product):
8 print("模拟用户测试......")

要想实现可以全局调用,可以将两个模块中的固件方法迁移到根目录下的 conftest.py 中:

 1 # 根目录下的 conftest.py
2 import pytest
3
4 @pytest.fixture(name="init_product")
5 def product_init():
6 print("\n产品测试前置操作....")
7 yield
8 print("\n产品测试后置操作....")
9
10
11 @pytest.fixture(name="init_user")
12 def user_init():
13 print("\n用户测试前置操作....")
14 yield
15 print("\n用户测试后置操作....")

此时,我们尝试在两个测试用例里面分别调用对方用例中的固件函数:

1 ############# product/test_product.py #############
2 def test_product_case(init_product, init_user):
3 print("模拟产品测试......")
4
5
6 ############# user/test_user.py ##################
7 def test_user_case(init_user, init_product):
8 print("模拟用户测试......")

执行用例通过,且固件执行的顺序取决于固件名参数在测试方法中传参的位置,先传入的就先执行。

Pytest 固件的更多相关文章

  1. pytest测试框架 -- assert断言和fixture固件

    一.断言 (1)使用assert语句进行断言 # test_run.py @pytest.mark.assert def test_assert(self): r = requests.get(&qu ...

  2. pytest(6)-Fixture(固件)

    什么是固件 Fixture 翻译成中文即是固件的意思.它其实就是一些函数,会在执行测试方法/测试函数之前(或之后)加载运行它们,常见的如接口用例在请求接口前数据库的初始连接,和请求之后关闭数据库的操作 ...

  3. 2.pytest前后置(固件、夹具)处理

    一.setup/teardown/setup_calss/teardown_class 为什么需要这些功能? 比如:我们执行用例之前,需要做的哪些操作,我们用例执行之后,需要做哪些操作 # 在所有用例 ...

  4. pytest 2.测试用例setup和teardown

    之前我写的unittest的setup和teardown,还有setupClass和teardownClass(需要配合@classmethod装饰器一起使用),接下来就介绍pytest的类似于这类的 ...

  5. python:pytest中的setup和teardown

    原文:https://www.cnblogs.com/peiminer/p/9376352.html 之前我写的unittest的setup和teardown,还有setupClass和teardow ...

  6. pytest使用总结笔记

    简介 pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高.并且pytest兼容unittest的用例,支 ...

  7. 《带你装B,带你飞》pytest修仙之路5 - yield操作

    1. 简介 上一篇中,我们刚刚实现了在每个用例之前执行初始化操作,那么用例执行完之后如需要清除数据(或还原)操作,可以使用 yield 来实现.fixture通过scope参数控制setup级别,既然 ...

  8. Pytest单元测试框架-学习

    pytest: Python的一个单元测试框架,基于UnitTest二次开发,语法上更加简洁,可以用来做Python开发项目的单元测试,UI自动化.接口自动化测试等,有很多的插件访问Pytest插件汇 ...

  9. python单元测试框架pytest

    首先祝大家国庆节日快乐,这个假期因为我老婆要考注会,我也跟着天天去图书馆学了几天,学习的感觉还是非常不错的,这是一篇总结. 这篇博客准备讲解一下pytest测试框架,这个框架是当前最流行的python ...

  10. pytest使用小结

    一.pytest简洁和好处 自动发现测试用例 testloader 断言方便 ,自定义错误提示 assert 正则匹配 灵活运行指定的测试用例,指定模块,制定测试类,测试用例 -k 标签化,回归 正向 ...

随机推荐

  1. MySQL 插入数据 数据重复 从另一个表导入数据

    当使用MySQL插入数据时,我们可以根据需求选择合适的插入语句. 一.方法分类 二.具体方法 使用场景 作用 语句 注意 常规插入 忽略字段名 insert into 表名 values (值1, 值 ...

  2. Python基本数据类型,用户交互,格式化输出,运算符,多种赋值方式,多种运算符

    Python基本数据类型,用户交互,格式化输出,运算符,多种赋值方式,多种运算符 一.Python基本数据类型 1.回顾之前学过的基本数据类型 1.整型(整数) 应用场景:年级,班级人数,年份 代码实 ...

  3. Blazor入门100天 : 身份验证和授权 (2) - 角色/组件/特性/过程逻辑

    目录 建立默认带身份验证 Blazor 程序 `角色/组件/特性/过程逻辑 DB 改 Sqlite 将自定义字段添加到用户表 脚手架拉取IDS文件,本地化资源 freesql 生成实体类,freesq ...

  4. Nginx11 openresty连接redis(lua-resty-redis)

    1 官网 http://openresty.org/cn/lua-resty-redis-library.html https://github.com/openresty/lua-resty-red ...

  5. 线程基础知识09-JAVA的可见性和有序性问题

    1 CPU中的三级缓存及可见性问题 1.1 简介 1.2 缓存行Cacheline 1.3 可见性问题-缓存一致性协议 2 JAVA中的有序性问题 2.1 指令重排简介 2.2 as-if-seria ...

  6. javaweb-LoginDemo在JdbcTemp的登录实现及总结+进阶javabean改进

    刚开始发现- -我好像忘记了JdbcTemp的知识了,以为自己学漏了,重新回去看了一下,还好还记得,所以今天做一个案例: 案例分析: 登录的实现 步骤: 先导入jar包,然后写一个简单的html页面 ...

  7. TF坐标

    1.简介 TF是一个让用户随时间跟踪多个坐标系的功能包,它使用树形数据结构,根据时间缓冲并维护多个坐标系之间的坐标变换关系. 2.TF工具 tf_monitor :查看TF树中所有坐标系的发布状态 t ...

  8. IOS12.0 + Xcode 12.0 错误:Building for iOS Simulator, but the linked and embedded framework 'XXX.framework' was built for iOS + iOS Simulator

    环境:IOS12.0 + Xcode 12.0 问题描述:运行编译 Building for iOS Simulator, but the linked and embedded framework ...

  9. JZOJ 2934. 【NOIP2012模拟8.7】字符串函数

    题目大意 个等长的由大写英文字母构成的字符串 \(a\) 和 \(b\),从 \(a\) 中选择连续子串 \(x\),从 \(b\) 中选出连续子串y. 定义函数 \(f_{x,y}\) 为满足条件 ...

  10. map方法整理数据,接口返回值进行处理

    整理前: //map方法使thumb加上域名 --> var data =[ { id: "11", title: "新车小程序title1", thum ...