全篇大概 5000 字(含代码),建议阅读时间10min
简介
Pytest是一个非常成熟的测试框架,适用于但愿测试、UI测试、接口测试。
- 简单灵活、上手快
- 支持参数化
- 具有多个第三方插件
- 可以直接使用 assert 进行断言
一、Pytest安装
pip install pytest
验证是否安装成功
pytest --version
二、控制台打印参数
- pytest -h 查看帮助信息
- pytest -v 详细输出信息
- pytest -q 简化输出信息
- pytest -l 由于失败的测试用例会被堆栈追踪,所以所有的局部变量及其值都会显示出来
- pytest -k 模糊匹配时使用
- pytest -m 标记测试并且分组,运行时可以快速选择分组并且运行
- pytest -x 运行时遇到失败的测试用例会终止运行
- pytest -collect-only 显示要执行的用例,不执行
- pytest -ff 执行上次失败的测试,在执行上次正常的测试
- pytest -lf 执行上次失败的测试
- pytest -s 显示测试函数中print() 输出
- pytest -setup-show 查看具体的setup和teardown排序
- pytest -sw 测试失败时退出并从上次失败的测试继续下一次
- pytest -junit-xml=path 在执行路径创建 Junit XML样式的报告文件
- pytest -color=color 终端信息彩色输出,选值 yes/no/auto
三、mark标记
查看官方提供的mark
pytest --markers
3.1 skip跳过
执行过程中遇到该测试方法跳过
@pytest.mark.skip() 跳过当前测试方法
@pytest.mark.skip(reason='注释') 添加一个注释
# -*-coding:utf-8-*-
import pytest
# @pytest.mark.skip() 跳过当前测试方法
@pytest.mark.skip()
def test_skip1():
assert 1 == 2
# reason="跳过该条测试用例" 只是做一个注释,对结果不会影响
@pytest.mark.skip(reason="跳过该条测试用例")
def test_skip2():
assert 1 == 2
四、skipif 判断跳过
@pytest.mark.skipif('sys.platform == "win32"') 判断如果是32位操作系统,跳过当前测试
import pytest
# 只有在满足条件下才跳过当前测试方法
@pytest.mark.skipif('sys.platform == "win32"', reason="不适合在 win32 中运行")
def test_skipif1():
assert 1 == 2
def test_skip1():
assert 1 == 2
@pytest.mark.skipif('sys.platform != "win32"')
def test_skipif2():
assert 1 == 1
4.1 xfail 标识
通过xfail装饰器可以去查看结果为失败的,又不想跳过的测试方法,给出相应的标识。
@pytest.mark.xfail
@pytest.mark.xfail(reason="运算错误")
import pytest
@pytest.mark.xfail(reason="运算错误")
def test_xfail1():
assert 1 + 1 == 1
@pytest.mark.xfail
def test_xfail2():
assert 1 + 1 == 2
五、parametrize 参数化
用于对测试方法进行参数化,一个测试方法可以结合不同的测试数据同时进行测试
@pytest.mark.parametrize('number', _list) 遍历_list 将元素依次进行传入
import pytest
list_one = [1, 2, 3, 4]
# 执行遍历 list_one 依次作为参数传入测试方法
@pytest.mark.parametrize('number', list_one)
def test_parametrizel1(number):
assert number in list_one
list_two = [(1, 2, 3), (2, 3, 5), (3, 4, 7)]
@pytest.mark.parametrize('num1, num2, sum', list_two)
def test_parametrizel2(num1, num2, sum):
assert num1 + num2 == sum
六、mark自定义标记
可以通过在测试方法前添加装饰器 pytest.mark.标记名 就可以使用。
例: pytest.mark.done, pytest.mark.conmmit
运行时通过加入 -m 即可标记测试方法。
import pytest
def add_number(a, b):
return a + b
# mark 自定义标记 可以通过 pytest -m "done" 去执行被标记为 done的测试方法
@pytest.mark.done
def test_add1():
assert add_number(2, 3) == 5
@pytest.mark.undo
def test_add2():
assert add_number(2, 3) == 4
@pytest.mark.undo
def test_add3():
assert add_number(3, 3) == 6
七、固件 Fixture
fixture (固件)用于测试用例执行前的数据准备、环境搭建和测试用例执行后的数据销毁、环境恢复等。
@pytest.fixture()
运行时通过-s 参数 输出信息到控制台。
import pytest
@pytest.fixture()
def fixture_prepare():
print('\n 开始准备固件')
def test_fixturel(fixture_prepare):
print('test_fixture1')
def test_fixture2():
print('test_fixture2')
Fixture 参数
- scope: 定义Fixture作用域,有四个可选参数 function、class、module、package/session
- 默认function
- params: 可选参数,使多个参数调用 Fixture函数和所有测试使用
- autouse: 如果为true、则所有测试方法都会执行固件方法,否则只对添加固件方法的测试方法执行固件方法
- ids: 每个参数都与列表中的字符串id对应,如果没有提供id将会从参数中自动生成
- name: Fixture的名称,默认是装饰器名称,如果Fixture在与定义的模块中使用,name功能名称将会被请求的Fixture参数遮盖。
7.1 Fixture 作用域
fi作用域是用来指定固件的使用范围,固件的范围可以通过scope参数声明,scope参数有:
- function: 函数级别,默认级别,每个测试方法执行前都会执行
- class: 类级别,每个测试类执行前执行一次
- module: 模块级别,每个模块执行前执行一次,每个.py 文件执行前都会执行一次
- session: 会话级别,一次测试只执行一次,即多个文件调用一次。
@pytest.fixture(scope="作用域名称")
import pytest
@pytest.fixture(scope="session")
def session_fixture():
pass
@pytest.fixture(scope="module")
def module_fixture():
pass
@pytest.fixture(scope="class")
def class_fixture():
pass
@pytest.fixture(scope="function")
def function_fixture():
pass
如果全部测试方法都使用了Fixture,可以直接在class类上进行使用装饰器
@pytest.mark.usefixtures("固件名")
import pytest
# 通过在类上使用装饰器,让所有方法都可以用到fixture
@pytest.mark.usefixtures('func_fixture')
class TestFixture():
def test_fixture1(self):
pass
def test_fixture2(self):
pass
7.2 autouse 自动使用
autouse 可以自动将测试固件添加到测试方法上,默认为false 不启用。
@pytest.fixture(autouse=True)
import pytest
@pytest.fixture(autouse=True)
def autouse_fixture():
print("这是固件中的autouse参数")
def test_fixture1():
print("这是test_fixture1")
def test_fixture2():
print("这是test_fixture2")
if __name__ == '__main__':
pytest.main(['-s', '--setup-show', 'fixture_autouse_learn.py'])
7.3 使用 yield
yield 就是把准备、销毁操作放在一起。
如果在yield之前代码有异常,则yield后面代码不继续执行。
yield
import pytest
@pytest.fixture()
def fixture_yield():
print('\n开始测试')
yield
print('\n结束测试')
def test_yield(fixture_yield):
print('\n数据销毁测试')
7.4 Fixture 共享
将测试相同的内容做到共享复用。例如:登录功能,就可以将登录写成方法。
名称必须是 conftest, pytest 会自动识别
conftest 文件中存储将要共享的功能需要在运行用例在同一个包下
所有同目录测试运行前都会执行 conftest文件
用例运行过程每个测试方法都会被执行,如果想只运行一次,需要将Fixture作用域改为会话级别session
@pytest.fixture(scope="session") 会话级别
7.5 参数化
Fixture 参数化通过参数 params实现,如果测试方法需要不同的参数来构造逻辑基本相同、场景不同的情况下,就可以使用参数化来简化工作。
@pytest.fixture(params=[])
比如测试两个数之间的乘奇, 就可以使用参数化进行测试。
import pytest
@pytest.fixture(params=[
(2, 2, 4),
(2, 4, 8),
(2, 8, 16)
])
def test_params(request):
return request.param
def test_add(test_params):
assert test_params[2] == test_params[0] * test_params[1]
7.6 内置 Fixture
tmpdir
用于创建临时文件目录使用于单个测试方法
def function_1(tmpdir):
import pytest
def test_tmpdir(tmpdir):
# 创建临时目录
tmp_dir = tmpdir.mkdir('testdir')
tmp_file = tmp_dir.join('tmpfile.txt')
tmp_file.write('hello world')
assert tmp_file.read() == 'hello world'
tmpdir_factory
创建临时文件目录。
作用范围是会话级别的: session、module、class、function
def function_1(tmpdir_factory):
import pytest
@pytest.fixture(scope='module')
def test_tmpdir_factory(tmpdir_factory):
tmp_dir = tmpdir_factory.mktemp('testdir')
tmp_file = tmp_dir.join('tmpfile.txt')
tmp_file.write('hello world')
return tmp_file
def test_tempdir1(test_tmpdir_factory):
with test_tmpdir_factory.open() as f:
assert f.read() == 'hello world'
def test_tempdir2(test_tmpdir_factory):
assert 'hello world' in test_tmpdir_factory.read()
未完待续...