pytest之fixture之yield关键字实现teardown用例后置操作 || addfinalizer注册函数为终结函数
pytest之fixture之yield关键字实现teardown用例后置操作
前言
①pytest之fixture函数使用(pytest测试框架测试固件 文中讲到,fixture函数是通过scope参数来控制setup级别;
②既然有setup函数作为用例之前的操作,测试用例执行完成之后那肯定也有teardown操作。
③但是fixture的teardown操作并不是独立的函数,用yield关键字唤醒teardown操作。【依然存在于fixture方法中】
yield实现teardown后置操作
实例1
#使用yield关键字实现teardown_xxx的功能 import pytest # 此时,login函数是一个测试固件,相当于实现了setup_xxx&teardown_xxx的功能。 @pytest.fixture() def login(): ############# 以下的代码相当于setup部分 ########### print('登录系统') token = 'a1b23c' yield token ############# 以下的代码相当于teardown部分 ########### print('退出登录') # 在测试函数里, 通过形参声明要使用的测试固件 def test1(login): # login参数的值是测试固件函数的返回值 print('执行测试 test1: ', login) print('测试1') def test2(login): print('执行测试 test2: ', login) print('测试2') # 通过python解释器执行需要以下代码 if __name__ == '__main__': pytest.main(["-s", "test_yieldDemo.py"])
运行结果:
【注意】:return和yield两个关键字都可以返回值;
yield关键字返回值后,后面的代码还会继续运行;【由于实例1中fixture函数login需要返回token,而且还需要继续执行teardown后置操作:所以选择yield关键字所以后面代码还会继续运行】
return关键字返回值后,后面的代码不会继续运行;
实例2
import pytest @pytest.fixture(scope="module") def open(): print("打开浏览器,并且打开百度首页") yield print("执行teardown!") print("最后关闭浏览器") def test_s1(open): print("用例1:搜索python-1") def test_s2(open): print("用例2:搜索python-2") def test_s3(open): print("用例3:搜索python-3") if __name__ == "__main__": pytest.main(["-s", "test_fixturemodule2.py"])
运行结果:
实例3
#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest @pytest.fixture(scope="session") def open(): # 会话前置操作setup print("===打开浏览器open===") yield # 会话后置操作teardown print("===关闭浏览器open===") @pytest.fixture def login(open): # 方法级别前置操作setup print("===登陆操作login===") name = "===账号===" pwd = "===密码===" # 返回变量 yield name, pwd # 方法级别后置操作teardown print("===登录成功login===") def test_case1(login): print("===测试用例1===") # 返回的是一个元组 print(login) # 分别赋值给不同变量 name, pwd = login print(name, pwd) assert "账号" in name assert "密码" in pwd def test_case2(login): print("===测试用例2===") print(login)
运行结果:
yield遇到异常
1、如果其中一个用例在执行时出现异常,不影响yield后面的teardown执行,运行结果互不影响,并且全部用例执行完之后,yield唤起teardown操作。
# 新建一个文件test_f1.py # coding:utf-8 import pytest @pytest.fixture(scope="module") def open(): print("打开浏览器,并且打开百度首页") yield print("执行teardown!") print("最后关闭浏览器") def test_s1(open): print("用例1:搜索python-1") # 如果第一个用例异常了,不影响其他的用例执行 raise NameError # 模拟异常 def test_s2(open): print("用例2:搜索python-2") def test_s3(open): print("用例3:搜索python-3") if __name__ == "__main__": pytest.main(["-s", "test_f1.py"])
运行结果:
2、但是fixture函数如果在setup执行期间发生异常,那么pytest是不会去执行yield后面的teardown内容。
3、yield关键字也可以配合with语句使用;以下是官方文档给的案例:
# 官方文档案例 # content of test_yield2.py import smtplib import pytest @pytest.fixture(scope="module") def smtp(): with smtplib.SMTP("smtp.gmail.com") as smtp: yield smtp # provide the fixture value
yield关键字+with上下文管理器的结合使用
yield 关键字 也可以配合 with 上下文管理器 语句使用。【使得代码更加精简】
示例:
#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest import smtplib @pytest.fixture(scope="module") def smtp_connection(): with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection: yield smtp_connection
request.addfinalizer()将定义的函数注册为终结函数
除了yield可以实现teardown,我们也可以通过 request.addfinalizer() 的方式去注册终结函数来实现 teardown 用例的后置操作。
示例:
addfinalizer 的用法跟 yield 是不同的, addfinalizer 需要你去注册一个或多个作为终结器使用的函数。
例如:增加一个函数 fin,并且注册成终结函数。
代码如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest @pytest.fixture(scope="module") def test_addfinalizer(request): # 前置操作setup print("===打开浏览器===") test = "test_addfinalizer" def fin(): # 后置操作teardown print("===关闭浏览器===") request.addfinalizer(fin) # 返回前置操作的变量 return test def test_case(test_addfinalizer): print("===最新用例===", test_addfinalizer)
运行结果:
yield 与 addfinalizer 用法的区别:
① addfinalizer 可以注册多个终结函数。当注册多个终结函数时,用例的后置操作同时会执行完所有的终结函数。
【注意】终结函数(用例后置操作函数)的执行顺序与其在fixture函数中注册的顺序相反(即先注册的终结函数后执行,后注册的终结函数先执行)
示例:
#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest @pytest.fixture() def demo_addfinalizer(request): print("====setup====") def fin1(): print("====teardown1====") def fin2(): print("====teardown2====") def fin3(): print("====teardown3====") # 注册fin1、fin2、fin3为终结函数 request.addfinalizer(fin1) request.addfinalizer(fin2) request.addfinalizer(fin3) def test_case1(demo_addfinalizer): print("====执行用例test_case1====") def test_case2(demo_addfinalizer): print("====执行用例test_case2====") def test_case3(demo_addfinalizer): print("====执行用例test_case3====") if __name__ == '__main__': pytest.main(__file__, '-s')
运行结果:
②当执行测试用例时setup前置操作函数的代码执行错误或者发生异常时,addfinalizer 注册的终结函数依旧会执行。
③ yield 关键字可以返回setup前置操作函数中生成的测试数据,且 yield 关键字返回测试数据之后后续的代码依然可以运行。且后续执行的代码充当teardown后置操作函数。
④ addfinalizer 函数可以将一个或者多个函数注册为终结函数(一个或多个函数必须在fixture函数中定义),此时的终结函数为teardown后置操作函数;且最后可以使用 return 关键字返回setup前置操作函数生成的测试数据。
去期待陌生,去拥抱惊喜。