书籍来源:房荔枝 梁丽丽《pytest框架与自动化测试应用》
一边学习一边整理老师的课程内容及实验笔记,并与大家分享,侵权即删,谢谢支持!
附上汇总贴:pytest框架进阶自学系列 | 汇总_热爱编程的通信人的博客-CSDN博客
fixture(scope="function",params=None,autouse=False,ids=None,name=None):scope有5个级别参数function(默认)、class、module、package和session。package被认为是实验性的。
- function:每个函数或方法都会调用;
- class:每个类调用一次,一个类可以有多种方法;
- module:每个.py文件调用一次,该文件内又有多个function和class;
- Session:多个文件调用一次,可以跨.py文件调用,每个.py文件就是module。
如何使用fixture功能实现不同层级的数据共享呢?默认的function级别已经举例说明了,也就是每种方法都可以调用fixture标记的方法,通过传参的方式。
现在来举个module层级的例子,module是模块级,也就是每个文件只调用一次。
模块(module)级别使用fixture实例
常用使用场景:当进行测试执行时,有些动作只在这个文件中的开始或结束执行一次。这就需要运用层级scope的设置了,当把scope参数设置为module时,只在文件开始执行一次。例如,在进行WebUI自动化测试时,需要在测试用例前打开浏览器这个动作。
实现步骤:
(1)导入pytest。
(2)创建open()函数。
(3)在open()函数上添加@pytest.fixture(scope="module")。
(4)在测试方法中传入参数(open函数名称)。
具体代码如下:
import pytest
@pytest.fixture(scope="module")
def open():
print("打开浏览器,打开百度首页")
def test_s7():
print('用例7,')
def test_s8(open):
print('用例8,')
def test_s9(open):
print('用例9,')
全部添加open参数时执行结果如下:
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_module.py
Testing started at 13:46 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_module.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 3 items
test_fixture_module.py::test_s7 打开浏览器,打开百度首页
PASSED [ 33%]用例7,
test_fixture_module.py::test_s8 PASSED [ 66%]用例8,
test_fixture_module.py::test_s9 PASSED [100%]用例9,
============================== 3 passed in 0.26s ==============================
Process finished with exit code 0
s7()函数不添加open参数时执行结果如下:
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_module.py
Testing started at 13:46 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_module.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 3 items
test_fixture_module.py::test_s7 PASSED [ 33%]用例7,
test_fixture_module.py::test_s8 打开浏览器,打开百度首页
PASSED [ 66%]用例8,
test_fixture_module.py::test_s9 PASSED [100%]用例9,
============================== 3 passed in 0.06s ==============================
Process finished with exit code 0
其他执行结果,读者可以自行体会。因为module作用于全文件,可以分几种情况:open在第一种方法中使用,其他方法可以不添加。open方法在全文件开始前只执行一次。如果第一种方法中没有open,则第一种方法单独执行,再执行下面的第二种方法,也就是说,open添加在哪个测试方法中,就从该方法以后实现open依赖注入效果。
类(class)级别使用fixture实例
当fixture为class级别的时候,如果一个class里面有多个用例,则都调用了此fixture,那么当此fixture只在该class中时所有用例开始前执行一次。
代码如下:
import pytest
@pytest.fixture(scope="class")
def first():
print("\n获取用户名,scope为class级别只运行一次")
a = "linda"
return a
def test_3():
print("不在类中的测试方法")
class TestCase():
def test_1(self, first):
print("测试账号:%s" % first)
assert first == "linda"
def test_2(self, first):
print("测试账号:%s" % first)
assert first == "linda"
执行的结果如图3-3所示。
会话(session)级别使用fixture与conftest.py配合
fixture为session级别是可以跨.py模块调用的,也就是当有多个.py文件用例的时候,如果多个用例只需调用一次fixture,那就可以设置为scope="session"。
既然已经是跨模块,需要在.py模块之上。因此采用一个单独的文件conftest.py,文件名称是固定的,pytest会自动识别该文件。放到工程的根目录下就可以全局调用了,如果放到某个package包下,那就只在该package内有效。
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_class.py
Testing started at 13:51 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_class.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 3 items
test_fixture_class.py::test_3 PASSED [ 33%]不在类中的测试方法
test_fixture_class.py::TestCase::test_1
获取用户名,scope为class级别只运行一次
PASSED [ 66%]测试账号:linda
test_fixture_class.py::TestCase::test_2 PASSED [100%]测试账号:linda
============================== 3 passed in 0.02s ==============================
Process finished with exit code 0
常用使用场景:当用户与其他测试工程师合作开发时,公共的模块要存放在不同文件中,要存放在大家都能访问的地方。通常是一些公用的配置。公共模块也可以,例如登录模块是大家公用的,因此应放在项目或包的路径下。
具体执行步骤如下:
(1)在本目录下创建conftest.py文件(文件名必须是这个)。
(2)将登录模块带@pytest.fixture写在conftest.py文件中。
import pytest
@pytest.fixture()
def login():
print("\n用户名linda密码登录!")
(3)在原来的test_fixture.py文件中删除部分代码(login方法),代码如下:
import pytest
def test_cart(login):
print('\n用例1,登录后执行查看购物车其他功能1')
def test_find_goods():
print('\n用例2,不登录,执行浏览商品功能2')
def test_pay(login):
print('\n用例3,登录后执行支付功能3')
(4)右击鼠标,选择pytest执行test_fixture.py。
在执行过程中当读到login时,如果在本用例中没找到,则去本目录下conftest.py中查找。如果找到就执行,如果找不到就报错。同时其他工程师也可以在本目录中新建文件,并使用login函数,可以跟上述代码类似。
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture.py
Testing started at 13:53 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 3 items
test_fixture.py::test_cart
用户名linda密码登录!
PASSED [ 33%]
用例1,登录后执行查看购物车其他功能1
test_fixture.py::test_find_goods PASSED [ 66%]
用例2,不登录,执行浏览商品功能2
test_fixture.py::test_pay
用户名linda密码登录!
PASSED [100%]
用例3,登录后执行支付功能3
============================== 3 passed in 0.18s ==============================
Process finished with exit code 0
session级别实例
需要使用网络接入的fixture往往依赖于网络的连通性,并且创建过程一般非常耗时。放在全局conftest.py文件中,类似一个单例模式的方法,提前建立连接,所有人可以使用这个连接。
(1)创建conftest.py文件,把网络连接的函数写在这个文件中,代码如下:
import pytest
import smtplib
@pytest.fixture()
def login():
print("\n用户名linda密码登录!")
@pytest.fixture(scope='module')
def smtp_connection():
return smtplib.SMTP("smtp.163.com", 25, timeout=5)
(2)在相同的目录下,新建一个测试模块test_module.py,将smtp_connection作为形参传入每个测试用例,它们共享同一个smtp_connection()的返回值。
def test_ehlo(smtp_connection):
response, _ = smtp_connection.ehlo()
assert response == 250
smtp_connection.extra_attr = 'test'
assert 0
def test_noop(smtp_connection):
response, _ = smtp_connection.noop()
assert response == 250
assert smtp_connection.extra_attr == 0
(3)执行这个测试模块,结果如下:
可以看到:两个测试用例使用的smtp_connection实例都是<smtplib.SMTP object at 0x1104bbe48>,说明smtp_connection只被调用了一次。在前一个用例test_ehlo中修改smtp_connection实例(上述例子中,为smtp_connection添加extra_attr属性),也会反映到test_noop用例中,如图3-5所示。
D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_module.py
Testing started at 13:57 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_module.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 2 items
test_module.py::test_ehlo FAILED [ 50%]
src\chapter-3\test_module.py:0 (test_ehlo)
smtp_connection = <smtplib.SMTP object at 0x000001C1E8D31188>
def test_ehlo(smtp_connection):
response, _ = smtp_connection.ehlo()
assert response == 250
smtp_connection.extra_attr = 'test'
> assert 0
E assert 0
test_module.py:5: AssertionError
FAILED [100%]
src\chapter-3\test_module.py:6 (test_noop)
'test' != 0
Expected :0
Actual :'test'
<Click to see difference>
smtp_connection = <smtplib.SMTP object at 0x000001C1E8D31188>
def test_noop(smtp_connection):
response, _ = smtp_connection.noop()
assert response == 250
> assert smtp_connection.extra_attr == 0
E AssertionError: assert 'test' == 0
E +'test'
E -0
test_module.py:10: AssertionError
test_module.py::test_noop
================================== FAILURES ===================================
__________________________________ test_ehlo __________________________________
smtp_connection = <smtplib.SMTP object at 0x000001C1E8D31188>
def test_ehlo(smtp_connection):
response, _ = smtp_connection.ehlo()
assert response == 250
smtp_connection.extra_attr = 'test'
> assert 0
E assert 0
test_module.py:5: AssertionError
__________________________________ test_noop __________________________________
smtp_connection = <smtplib.SMTP object at 0x000001C1E8D31188>
def test_noop(smtp_connection):
response, _ = smtp_connection.noop()
assert response == 250
> assert smtp_connection.extra_attr == 0
E AssertionError: assert 'test' == 0
E +'test'
E -0
test_module.py:10: AssertionError
=========================== short test summary info ===========================
FAILED test_module.py::test_ehlo - assert 0
FAILED test_module.py::test_noop - AssertionError: assert 'test' == 0
============================== 2 failed in 0.60s ==============================
Process finished with exit code 1
如果期望拥有一个会话级别作用域的fixture,则可以简单地将其声明为session。
import pytest
import smtplib
@pytest.fixture()
def login():
print("\n用户名linda密码登录!")
@pytest.fixture(scope='session')
def smtp_connection():
return smtplib.SMTP("smtp.163.com", 25, timeout=5)
注意:pytest每次只缓存一个fixture实例,当使用参数化的fixture时,pytest可能会在声明的作用域内多次调用这个fixture。