pytest学习笔记
使用环境
- python3
- pycharm
- pytest
pytest编写规范
- 测试文件以test_开头或以_test结尾
- 测试类以Test开头,并且不能带有init方法
- 测试函数以test_开头
实例
-
设置pycharm为pytest运行
pytest - 创建
demo.py
文件
# 加法
def add(a, b)
return a + b
- 创建
test_demo.py
文件
from src.demo import add
# 测试加法
def test_add():
assert add(1, 2) == 3
assert add(1, 0) == 1
assert add(1, -1) == 0
- 使用pycharm执行test_demo文件
- 使用命令行执行
pytest test_demo
常用的pytest第三方插件
插件安装方式pip install 插件名称
-
pytest-sugar
显示执行的进度条
pytest-sugar -
pytest-assume
一次性执行完所有测试用例,不会因为某个测试用例失败而终止后面的用例。如,上面的例子就会因为某个用例失败而终止
def test_add():
assert add(1, 2) == 3
assert add(1, 0) == 2
assert add(1, -1) == 0
执行结果
使用pytest-assume
def test_add(self):
pytest.assume(add(1, 2) == 3)
pytest.assume(add(1, 0) == 2)
pytest.assume(add(1, -1) == 0)
-
pytest-ordering
设置测试用例执行顺序
import pytest
class TestAdd():
@pytest.mark.run(order=2)
def test_add1(self):
print('test_add1')
pytest.assume(add(1, 2) == 3)
@pytest.mark.run(order=1)
def test_add2(self):
print('test_add2')
pytest.assume(add(1, -1) == 0)
# order=1先执行,order=2后执行
- pytest-selenium
- pytest-play
- pytest-rerunfailures 重新运行失败的测试用例
# pytest-rerunfailures执行方式
pytest --reruns 3 test_demo.py # 3代表失败后重新运行的次数,如果加上正常运行的次数总共是4次
- pytest-allure 测试报告
- pytest-datadir
- pytest-datafiles
pytest参数化
通过@pytest.mark.parametrize
装饰器传参
import pytest
# 加法
def add(a, b):
return a + b
# 参数
@pytest.mark.parametrize(
'a, b, re', [
(3, 4, 7),
(1, -1, 0),
(1, 1.2, 2.2),
])
# 测试加法
def test_add(a, b, re):
pytest.assume(add(a, b) == re)
通过读取文件的形式参数化
读json文件
import json
import pytest
# 读取json文件
def load_json():
with open('test.json', 'r') as f:
re = json.load(f)
return re
# 加法运算
def add(a, b):
return a + b
# 把读到文件的数据通过参数传递
@pytest.mark.parametrize('a, b, re', load_json())
# 测试加法运算
def test_add(a, b, re):
pytest.assume(add(a, b) == re)
读yaml文件
import yaml
import pytest
# 读取yaml文件
def load_json():
with open('test.yaml', 'r') as f:
re = yaml.load(f)
return re
# 加法运算
def add(a, b):
return a + b
# 把读到文件的数据通过参数传递
@pytest.mark.parametrize('a, b, re', load_json())
# 测试加法运算
def test_add(a, b, re):
pytest.assume(add(a, b) == re)
说明: pytest.mark.parametrize
的第一个参数代表测试方法接收的参数个数,第二个参数为接收的数据内容。如果是通过读文件的形式传参需要注意读出来的文件内容是否和要传的参数类型一致。
pytest执行级别
-
setup_module
模块级别
如果在单个模块中有多个测试函数和测试类,则可以选择实现以下方法(只会执行一次)
def setup_module():
""" 模块执行前准备工作 """
class Test1():
""" 测试类1 """
class Test2():
""" 测试类2 """
def teardown_module():
""" 模块执行后的结束工作 """
-
setup_class
类级别
在调用类的所有测试方法之前和之后调用以下方法(只会执行一次)
class TestDemo():
@classmethod
def setup_class(cls):
""" 类方法执行前准备工作 """
def test_add(self):
"" 测试方法 """
@classmethod
def teardown_class(cls):
""" 类方法执行后结束工作 """
-
setup_method
方法级别
围绕每个方法执行之前之后调用以下方法(每个方法执行之前都会执行一次)
class Test1():
def setup_method(self):
""" 每个方法执行之前准备工作 """
def test_add1(self):
""" 测试方法1 """
def test_add2(self):
""" 测试方法1 """
def teardown_method(self):
""" 每个方法执行之后结束工作 """
-
setup_function
函数级别
在执行每个函数执行,调用以下方法(只会执行一次)
def setup_function():
""" 函数执行前准备工作 """
def test_add1():
""" 测试函数1 """
def test_add2():
""" 测试函数2 """
def teardown_function():
""" 函数执行后结束工 """
pytest fixtures
fixture函数的作用:
- 完成setup和teardown操作,处理数据库、文件等资源的打开和关闭
- 完成大部分测试用例需要完成的通用操作,例如login、设置config参数、环境变量等
- 准备测试数据,将数据提前写入到数据库,或者通过params返回给test用例,等
- 有独立的命名,可以按照测试的用途来激活,比如用于functions/modules/class/session
fixture 参数
scope
参数
scope=function
:每个test都运行,默认是function的scope
scope=class
:每个class的所有test只运行一次
scope=module
:每个module的所有test只运行一次
scope=session
:每个session只运行一次
1. 定义一个函数级别的fixture
import pytest
# 定义fixture函数
@pytest.fixture()
def fixture_func():
print("\n这是一个fixture函数,用来完成一些提前准备")
def test_1(fixture_func):
print('测试用例1')
assert 1==1
def test_2(fixture_func):
print('测试用例2')
assert 2==2
函数级别的fixture执行结果
函数级别执行结果
2. 定义一个模块级别的fixture
@pytest.fixture(scope="module")
def fixture_func():
print("\n这是一个fixture函数,用来完成一些提前准备")
def test_1(fixture_func):
print('测试用例1')
assert 1==1
def test_2(fixture_func):
print('测试用例2')
assert 2 == 2
模块级别的fixture执行结果
module级别执行结果
3. 定义一个session级别fixture
(1) 先创建一个conftest.py
文件,输入以下代码
说明:当有测试用例调用pytest.fixture函数时,pytest会自动去找conftest.py文件里找pytest.fixture函数,不需要import。
import pytest
@pytest.fixture(scope="session")
def fixture_func():
print("\n这是一个fixture函数,用来完成一些提前准备")
(2) 创建第一个测试用例文件 test_demo.py
def test_1(fixture_func):
print('测试用例1')
assert 1 == 1
def test_2(fixture_func):
print('测试用例2')
assert 2 == 2
(3) 创建第二个测试用例文件 test_demo2.py
def test_1(fixture_func):
print('测试用例3')
assert 3 == 3
def test_2(fixture_func):
print('测试用例4')
assert 4 == 4
(4) 使用命令行执行测试用例
$ pytest -s test_*
(5) session级别fixture执行结果
session级别执行结果
4. 加上yield的fixture函数
以上几个级别都是相当于steup的方法,加上yield可以构造出相当于teardown的方法。如下
import pytest
def start_prepare():
print("\n这是一个用来执行测试之前的准备工作")
def end_operation():
print("\n这是一个用来执行测试结束时的结束工作")
# fixture函数
@pytest.fixture(scope="module")
def fixture_func():
print("测试开始")
# 当程序碰到yield会先执行yield前面的代码,等代码都执行完后回到yield处继续执行后面的代码
yield start_prepare()
end_operation()
# 测试用例
def test_1(fixture_func):
print('测试用例1')
assert 1==1
def test_2(fixture_func):
print('测试用例2')
assert 2 == 2
yield 执行结果
yield执行结果
下面来看下函数级别的fixture函数加上yield后的结果
import pytest
def start_prepare():
print("\n这是一个用来执行测试之前的准备工作")
def end_operation():
print("\n这是一个用来执行测试结束时的结束工作")
@pytest.fixture()
def fixture_func():
print("测试开始")
yield start_prepare()
end_operation()
# 测试用例
def test_1(fixture_func):
print('测试用例1')
assert 1==1
def test_2(fixture_func):
print('测试用例2')
assert 2 == 2
yield 执行结果
执行结果
说明:
当测试用例调用了fixture_func函数会先执行fixture_func函数的代码
执行顺序是:
- print("测试开始")
- start_prepare()
- print('测试用例1')
- assert 1==1
- yield
- end_operation()
5. 使用addfinalizer()方法实现类似teardown的方法
import pytest
def start_prepare():
print("\n这是一个用来执行测试之前的准备工作")
@pytest.fixture()
def fixture_func(request): # 注意需要加个request参数
print("\n测试开始")
def end_operation():
print("\n这是一个用来执行测试结束时的结束工作")
# 主要是这句代码来完成结束工作
request.addfinalizer(end_operation)
return start_operation()
# 测试用例
def test_1(fixture_func):
print('测试用例1')
assert 1==1
def test_2(fixture_func):
print('测试用例2')
assert 2 == 2
addfinalizer()方法执行结果
addfinalizer()执行结果
6. fixture参数化
import pytest
@pytest.fixture(params=[
"参数1",
"参数2"
])
def fixture_func(request):
str = request.param
print(f"\n{str}")
# 测试用例
def test_1(fixture_func):
print('测试用例1')
assert 1==1
执行结果,可以看出一个用例执行了两次
参数化执行结果调用fixture的三种方式
方式1: 在测试用例中直接调用它,把fixture函数当做参数传入,例如上面的例子。
方式2:使用@pytest.mark.usefixtures("fixture函数名称")
装饰器
import pytest
@pytest.fixture()
def fixture_func():
print("\n测试前准备")
@pytest.mark.usefixtures("fixture_func")
def test_1():
print("函数测试用例1")
assert 1 == 1
@pytest.mark.usefixtures("fixture_func")
def test_2():
print('函数测试用例2')
assert 1 == 1
class Test1:
@pytest.mark.usefixtures("fixture_func")
def test_3(self):
print('类测试用例1')
assert 1 == 1
@pytest.mark.usefixtures("fixture_func")
def test_4(self):
print('类测试用例2')
assert 1 == 1
@pytest.mark.usefixtures("fixture_func")
class Test2:
def test_5(self):
print('类测试用例3')
assert 1 == 1
def test_6(self):
print('类测试用例4')
assert 1 == 1
执行结果
@pytest.mark.usefixtures()执行结果
方式3: 用autos调用fixture,autouse 默认设置为False。 当默认为False,就可以选择用上面两种方式来试用fixture。 当设置为True时,在一个session内的所有的test都会自动调用这个fixture,所以用该功能时要谨慎小心。
@pytest.fixture(scope="function", autouse=True)
import pytest
@pytest.fixture(scope="function", autouse=True)
def fixture_func():
print("\n测试前准备")
def test_1():
print("函数测试用例1")
assert 1 == 1
def test_2():
print('函数测试用例2')
assert 1 == 1
class Test1:
def test_3(self):
print('类测试用例1')
assert 1 == 1
def test_4(self):
print('类测试用例2')
assert 1 == 1
class Test2:
def test_5(self):
print('类测试用例3')
assert 1 == 1
def test_6(self):
print('类测试用例4')
assert 1 == 1
执行结果
autouse=True执行结果