一、简介
1.1 pytest与unittest区别
- unittest
测试文件必须先import unittest
测试类必须继承unittest.TestCase
测试方法必须以test_开头
测试类必须有unittest.main()方法
unittest只有setup/teardown装载测试用例
2.pytest
测试文件名以“test_”开通
测试类以“Test”开头,且不能有init方法
测试方法以"test_开头"
除了setup/teardown还能自定义夹具fixture装载测试用例
断言直接使用assert
1.2 简介
安装
pip install -U pytest
查看版本号
pytest --version
1.3 简单示例
有测试用例case1.py
def add(a, b):
c = a + b
return c
def sub(a, b):
c = a - b
return c
def test_add():
# 测试加法
assert add(1, 1) == 2
def test_sub():
# 测试减法
assert sub(8, 5) == 6
执行
pytest case1.py
结果:
二、pytest的调用和使用
2.1 规则
1.测试文件必须以test_开头或者_test结尾
2.测试类必须以Test开头,且不能有init方法
3.测试方法必须以test开头
4.断言必须使用assert
2.2 运行pytest可能导致的六种不同退出代码
退出代码0:所有测试都已收集并成功通过
退出代码1:测试已收集并运行,但有些测试失败
退出代码2:测试执行被用户中断
退出代码3:执行测试时发生内部错误
退出代码4:pytest命令行使用错误
退出代码5:未收集任何测试
2.3获取版本、选项、环境变量信息
查看版本
pytest --version
显示可用内置方法
pytest --fixtures
显示命令行及配置文件帮助信息
pytest -h --help
2.4 指定/选择测试用例
在模块中运行测试用例
pytest test_mod.py
在目录文件中执行测试
pytest testing/
按关键字表达式执行测试
运行包含指定表达式匹配的名称用例,其中包含文件名、类名、函数名作为变量且支持python运算符(and/or )操作。
pytest -k "Myclass and not method"
会执行TestMyclass.test_something但不会执行TestMyclass.test_method_other
示例如下,有case1.py模块,有测试类My,其中有两个测试用例test_add()和test_sub()
import pytest
def add(a, b):
c = a + b
return c
def sub(a, b):
c = a - b
return c
class TestMy:
@staticmethod
def test_add():
# 测试加法
assert add(1, 1) == 2
@staticmethod
def test_sub():
# 测试减法
assert sub(8, 5) == 4
执行
pytest -k "My and not sub" case1.py
结果:
即通过关键字不执行My类中的sub测试方法,所以只执行了add测试方法。
按节点ID执行
每次执行收集到的测试用例集合都会被分配一个唯一节点id(nodeid),其中包含模块名,后跟说明符,如类名、函数名及参数,由::字符分隔。执行模块中某条指定的测试用例,如
pytest test_mod.py::test_func
执行模块中某个类下的用例,如
pytest test_mod.py::TestClass::test_method
通过标记表达式运行
pytest -m slow
执行所有使用@pytest.mark.slow装饰的用例
如case1.py中装饰sub用例
def add(a, b):
c = a + b
return c
def sub(a, b):
c = a - b
return c
def test_add():
# 测试加法
assert add(1, 1) == 3
@pytest.mark.slow
def test_sub():
# 测试减法
assert sub(8, 5) == 4
执行
pytest -m slow case1.py
结果
第1/N次失败即停止测试
pytest -x # 出现失败即停止
pytest --maxfail=n # 出现n次失败就停止测试
从包中运行测试
pytest --pyargs pkg.testing
即从pkg.testing包中根据命名规则查找执行测试
2.5 详细测试结果摘要
"-r"标志可用于在测试完成后显示测试摘要信息,后面可跟其他标记获取所有失败、跳过、标记失败等测试结果的清晰描述。
可用字符有:
字符 | 描述 |
---|---|
-f | fail失败的用例 |
-E | erro出错的用例 |
-s | skip跳过的用例 |
-x | 标记失败的用例 |
-X | 标记成功的用例 |
-p | pass成功的用例 |
-P | 成功用例并输出结果 |
-a | 所有pP状态的用例 |
2.6 在python代码中调用执行
pytest.main()
如
pytest.main('-m login', '--setup-show', 'case4.py')
pytest.main('-x', 'case4.py')
三 、固件fixture
固件(fixture)是一些函数,pytest会在只想测试函数前或之后加载运行。类似unittest中的setup()和teardown()。
3.1 使用固件进行预处理
pytest使用yield关键词将固件分为两部分,yield之前的代码属于预处理,在测试用例开始前运行;yield之后的代码在测试完成后执行。如,测试前需要链接数据库,测试后需要将数据库关闭等。
@pytest.fixture()
def connect():
print('数据库链接成功')
yield
print('数据库关闭成功!')
def select(id):
d = {'001': '张三'}
return d[id]
def test_select(connect):
assert select(id='001') == '张三'
执行结果:
3.2 作用域
固件使用范围作用域:
在首次请求时创建,根据scope参数声明@pytest.fixture(scope=args),可选参数有:
- function: 默认范围,在测试结束后自动销毁
- class:在测试类中执行一次,每个方法都可用
- module:每个模块执行一次,模块中的函数和方法都可用
- package:每个包中执行一次,包中的模块都可以
- session:一次测试只执行一次
如
import pytest
@pytest.fixture(scope='session')
def order():
return []
@pytest.fixture
def func(order):
order.append('function')
@pytest.fixture(scope='class')
def cla(order):
order.append('class')
@pytest.fixture(scope='module')
def mod(order):
order.append('module')
@pytest.fixture(scope='package')
def pack(order):
order.append('package')
@pytest.fixture(scope='session')
def sess(order):
order.append('session')
class TestClass:
def test_order(self, func, cla, mod, pack, sess, order):
assert order == ['session', 'package', 'module', 'function', 'class']
在run.py中执行查看详细过程:
pytest.main(['-x', '--setup-show', 'case3.py'])
结果可见具体的执行顺序和作用域:
3.3 自动执行
即标记为自动执行的函数,在测试时,就算测试函数没有使用固件也会自动自行被标记的固件。
import pytest
@pytest.fixture(autouse=True)
def get_session():
return print('获取session')
def test_func():
"""不使用固件"""
assert 1+1 == 2
测试执行果:
如上,在case5.py中标记装饰了自动执行函数get_session(),在测试函数test_func()中不使用固件,实际执行测试过程中会先执行get_session()函数。
3.4 重命名
固件默认名称是定义时的函数名,如果不想使用默认名称可通过name对固件进行重命名。
import pytest
@pytest.fixture(name='myname')
def myfuc_001_name():
return 5
def test_myfuc(myname):
assert myname == 5
执行结果:
四、参数化
@pytest.mark.parametrize()
在某些测试用例过程中需要对用例使用参数化,可使用装饰方法设置参数。如判断用户名密码输入长度,
参数化三条数据,即可完成三次用例测试
def login(name, passwd):
return len(name), len(passwd)
@pytest.mark.slow
@pytest.mark.parametrize('name, passwd', [('user01', '12345678'), ('user1', '12345678'), ('user001', '1234567')])
def test_login(name, passwd):
user, psd = login(name, passwd)
assert 6 <= user <= 10, '用户名长度需要为6到10位'
assert 8 <= psd <= 12, '密码长度为8到12位'
执行结果:
使用“--setup-show”查看到详细执行过程。
汇总信息
可看出有两条测试数据不满足条件,所有失败2,成功1
五、测试报告
5.1 生成xml格式测试报告
pytest test.py --junitxml=path
在path中生成测试报告文件
5.2 用pytest-html生成html报告
pip install pytest-html # 需先安装
pytest test.py --html=report.html
5.3用allure生成测试报告
pip install allure-pytest # 安装
pytest test.py --alluredir=result_path # 生成测试结果
allure generate result_path -o report_path --clean # 在指定目录下生成测试报告