python + pytest 之 fixture+yield 使用详解
一、前言
用例执行完之后,如需要环境恢复和清除数据操作,可以使用yield来实现。fixture的teardown操作并不是独立的函数,
用yield关键字呼唤teardown操作。fixture通过scope参数控制setup级别,既然有setup作为测试用例之前的操作,那么测试
用例执行完后肯定也有teardown操作。
yield是一个关键字,它不是单独存在的,要写在fixture标记的固件中。
fixture固件函数 + yield关键字,可以实现setup环境预设和teardown环境恢复。
如果测试用例中的代码出现异常或者断言失败,并不会影响他的固件fixture中yield后的代码执行;但是如果固件fixture中的yield
之前的代码(也是相当于setup部分的代码),出现错误或断言失败,那么yield后的代码将不会再执行,当然test测试用例方法也不会执行的。
二、scope="function"
当pytest.fixture(scope="function")时,pytest的yield类似unittest的teardown。每个方法(函数)之前都会执行一次。
1.文件:test_func1.py
import pytest
@pytest.fixture(scope="function")
def login():
print("登录成功")
yield # 让步,先暂时停一下。下面的代码相当于teardown
print("用例执行完成,关闭会话")
print("--------------------------------------------")
def test1(login):
print("操作1...")
def test2(login):
print("操作2...")
def test3(login):
print("操作3...")
if __name__ == "__main__":
pytest.main(["-s", "test_func1.py"])
============================= test session starts ============================
platform win32 -- Python 3.8.5, pytest-5.4.2, py-1.11.0, pluggy-0.13.1
rootdir: D:\PycharmProjects\requests_fuxi_230313\api
plugins: allure-pytest-2.8.6, assume-2.4.3
collected 3 items
test_func1.py 登录成功
操作1...
.用例执行完成,关闭会话
--------------------------------------------
登录成功
操作2...
.用例执行完成,关闭会话
--------------------------------------------
登录成功
操作3...
.用例执行完成,关闭会话
--------------------------------------------
============================== 3 passed in 0.05s =============================
2.文件:test_func2.py
import pytest
@pytest.fixture(scope="function")
def login():
print("登录成功")
yield # 让步,先暂时停一下。下面的代码相当于teardown
print("用例执行完成,关闭会话")
print("--------------------------------------------")
def test1():
print("操作1...")
def test2(login):
print("操作2...")
def test3():
print("操作3...")
if __name__=="__main__":
pytest.main(["-s","test_func2.py"])
========================== test session starts ============================
platform win32 -- Python 3.8.5, pytest-5.4.2, py-1.11.0, pluggy-0.13.1
rootdir: D:\PycharmProjects\requests_fuxi_230313\api
plugins: allure-pytest-2.8.6, assume-2.4.3
collected 3 items
test_func2.py 操作1...
.登录成功
操作2...
.用例执行完成,关闭会话
--------------------------------------------
操作3...
.
========================== 3 passed in 0.05s ===============================
三、scope=“module”
fixture参数scope=“module”的作用范围是某个.py文件(该文件中只会执行一次)。
1.文件:test_func3.py
import pytest
@pytest.fixture(scope="module")
def login():
print("登录成功")
yield # 让步,先暂时停一下。下面的代码相当于teardown_class
print("用例执行完成,关闭会话")
print("--------------------------------------------")
def test1(login): # 调用login成功
print("操作1...")
def test2(login): # 调用login不执行
print("操作2...")
def test3(login): # 调用login不执行
print("操作3...")
if __name__=="__main__":
pytest.main(["-s","test_func3.py"])
============================ test session starts ==========================
platform win32 -- Python 3.8.5, pytest-5.4.2, py-1.11.0, pluggy-0.13.1
rootdir: D:\PycharmProjects\requests_fuxi_230313\api
plugins: allure-pytest-2.8.6, assume-2.4.3
collected 3 items
test_func3.py 登录成功
操作1...
.操作2...
.操作3...
.用例执行完成,关闭会话
--------------------------------------------
============================= 3 passed in 0.05s ============================
2.文件:test_func4.py
import pytest @pytest.fixture(scope="module") def login(): print("登录成功") yield # 让步,先暂时停一下。下面的代码相当于teardown_class print("用例执行完成,关闭会话") print("--------------------------------------------") def test1(): print("操作1...") def test2(login): # 调用login方法 print("操作2...") def test3(): print("操作3...") if __name__=="__main__": pytest.main(["-s","test_func4.py"])
============================ test session starts ==========================
platform win32 -- Python 3.8.5, pytest-5.4.2, py-1.11.0, pluggy-0.13.1
rootdir: D:\PycharmProjects\requests_fuxi_230313\api
plugins: allure-pytest-2.8.6, assume-2.4.3
collected 3 items
test_func4.py 操作1...
.登录成功
操作2...
.操作3...
.用例执行完成,关闭会话
--------------------------------------------
============================ 3 passed in 0.05s ===========================
四、scope=“class”
fixture参数scope=“class”的作用范围是某个class测试类(该测试类中只会执行一次)。
文件:test_func5.py
import pytest
@pytest.fixture(scope="class")
def login():
print("登录成功")
yield # 让步,先暂时停一下。下面的代码相当于teardown_class
print("用例执行完成,关闭会话")
print("--------------------------------------------")
class TestCase:
def test1(self):
print("操作1...")
def test2(self, login): # 调用login成功
print("操作2...")
def test3(self, login): # 调用login不执行
print("操作3...")
if __name__=="__main__":
pytest.main(["-s","test_func5.py"])
============================= test session starts ============================
platform win32 -- Python 3.8.5, pytest-5.4.2, py-1.11.0, pluggy-0.13.1
rootdir: D:\PycharmProjects\requests_fuxi_230313\api
plugins: allure-pytest-2.8.6, assume-2.4.3
collected 3 items
test_func5.py 操作1...
.登录成功
操作2...
.操作3...
.用例执行完成,关闭会话
--------------------------------------------
============================= 3 passed in 0.05s ===============================
五、yield遇到异常
1.如果其中一个test用例抛出异常,是不会影响yield后面的teardown执行,且各测试用例运行结果互不影响。
文件:test_func6.py
import pytest
@pytest.fixture(scope="module")
def login():
print("登录成功")
yield # 让步,先暂时停一下。下面的代码相当于teardown_class
print("用例执行完成,关闭会话")
print("--------------------------------------------")
def test1(login):
print("操作1...")
def test2(login):
print("操作2...")
# 如果一个用例抛出异常了,是不影响其他用例的执行
raise Exception() # 手动抛出异常
def test3(login):
print("操作3...")
if __name__=="__main__":
pytest.main(["-s","test_func6.py"])
============================== test session starts =============================
platform win32 -- Python 3.8.5, pytest-5.4.2, py-1.11.0, pluggy-0.13.1
rootdir: D:\PycharmProjects\requests_fuxi_230313\api
plugins: allure-pytest-2.8.6, assume-2.4.3
collected 3 items
test_func6.py 登录成功
操作1...
.操作2...
F操作3...
.用例执行完成,关闭会话
--------------------------------------------
=================================== FAILURES ====================================
__________________________________ test2 ________________________________________
login = None
def test2(login):
print("操作2...")
# 如果一个用例抛出异常了,是不影响其他用例的执行
> raise Exception() # 手动抛出异常
E Exception
test_func6.py:17: Exception
============================ short test summary info ============================
FAILED test_func6.py::test2 - Exception
============================= 1 failed, 2 passed in 0.34s =======================