一. 安装
1.1 Pytest 环境搭建
安装:
查看是否安装成功:
1.2 命名规则
.py
测试文件必须以 test_
开头(或者以 _test
结尾)- 测试类必须以
Test
开头,并且 不能 有 init 方法
- 测试方法必须以
test_
开头 - 断言必须使用
assert
1.3 pytest 输出的信息
.
:用例通过F
:用例失败—没有语法报错E
:语法错误
1.4 运行参数
-s
: 打印代码中 print
内容-v
: 打印更详细的执行信息, 包括 测试类、测试函数等
1.5 案例
1 2 3 4 5 6 7 8 9 10
| class TestLogin: @pytest.mark.parametrize('inData,respData', get_excelData2('登录模块', 'Login')) def test_login(self, inData, respData): res = Login().login(inData, getToken=False) print(res) assert res['msg'] == respData['msg']
|
二. 初始化
2.1 模块级别
在整个模块的测试用例 执行前后执行, 只会执行 1 次
模块级别 - 初始化案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| def setup_module(): print('\n *** 初始化-模块 ***')
def teardown_module(): print('\n *** 清除-模块 ***')
class TestDemo1:
def test_C001001(self): print('\n用例C001001') assert 1 == 1 def test_C001002(self): print('\n用例C001002') assert 2 == 2 def test_C001003(self): print('\n用例C001003') assert 3 == 2
class TestDemo2:
def test_C001021(self): print('\n用例C001021') assert 1 == 1 def test_C001022(self): print('\n用例C001022') assert 2 == 2
if __name__ == '__main__': pytest.main(['test_demo1.py', '-s'])
""" *** 初始化-模块 *** 用例C001001 . 用例C001002 . 用例C001003 F 用例C001021 . 用例C001022 . *** 清除-模块 *** """
|
2.2 类级别
类级别 - 初始化案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| import pytest
def setup_module(): print('\n *** 初始化-模块 ***')
def teardown_module(): print('\n *** 清除-模块 ***')
class TestDemo1:
@classmethod def setup_class(cls): print('\n === 初始化-类 ===')
@classmethod def teardown_class(cls): print('\n === 清除 - 类 ===')
def test_C001001(self): print('\n用例C001001') assert 1 == 1
def test_C001002(self): print('\n用例C001002') assert 2 == 2
def test_C001003(self): print('\n用例C001003') assert 3 == 2
class TestDemo2:
def test_C001021(self): print('\n用例C001021') assert 1 == 1
def test_C001022(self): print('\n用例C001022') assert 2 == 2
if __name__ == '__main__': pytest.main(['test_demo1.py', '-s'])
""" *** 初始化-模块 *** === 初始化-类 === 用例C001001 . 用例C001002 . 用例C001003 F === 清除 - 类 === 用例C001021 . 用例C001022 . *** 清除-模块 *** """
|
2.3 方法类别
在 每个方法执行前后执行, 每个用例分别执行 1 次
方法级别 - 初始化案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| import pytest
def setup_module(): print('\n *** 初始化-模块 ***')
def teardown_module(): print('\n *** 清除-模块 ***')
class TestDemo1: @classmethod def setup_class(cls): print('\n === 初始化-类 ===')
@classmethod def teardown_class(cls): print('\n === 清除 - 类 ===')
def setup_method(self): print('\n --- 初始化-方法 ---')
def teardown_method(self): print('\n --- 清除 -方法 ---')
def test_C001001(self): print('\n用例C001001') assert 1 == 1
def test_C001002(self): print('\n用例C001002') assert 2 == 2
def test_C001003(self): print('\n用例C001003') assert 3 == 2
class TestDemo2: def test_C001021(self): print('\n用例C001021') assert 1 == 1
def test_C001022(self): print('\n用例C001022') assert 2 == 2
if __name__ == '__main__': pytest.main(['test_demo1.py', '-s'])
""" *** 初始化-模块 *** === 初始化-类 === --- 初始化-方法 --- 用例C001001 . --- 清除 -方法 --- --- 初始化-方法 --- 用例C001002 . --- 清除 -方法 --- --- 初始化-方法 --- 用例C001003 F --- 清除 -方法 --- === 清除 - 类 === 用例C001021 . 用例C001022 . *** 清除-模块 *** """
|
三. 挑选用例执行
3.1 运行执行模块
3.2 运行指定目录
1 2 3 4 5
| pytest cases1
pytest cases1 cases2
|
3.3 指定模块里的函数或类
1 2
| pytest xxx/xxx.py::TestDemo
|
1 2
| pytest xxx/xxx.py::TestDemo::test_demo1
|
3.4 根据函数名执行
-k
后面的名字:
- 可以是函数名、类名、模块名 目录名
- 大小写敏感
- 不一定要完整,匹配上就行(如
test_demo1
可以写为 demo1
) - 支持关键字连接:
not
: 表示不包含 (pytest -k "not demo1" -s)
and
: 表示同时包含多个关键字 (pytest -k "demo1 and demo2")
or
: 表示包含关键字之一 (pytest -k "demo1 or demo2")
3.5 根据标签执行
- 使用
@pytest.mark.标签名
给类或方法或整个文件添加标签 - 标签支持中文
1 2 3 4 5 6 7
| import pytest
pytestmark = pytest.mark.网页测试
pytestmark = [pytest.mark.网页测试, pytest.mark.登录测试]
|
1 2 3 4 5 6
| @pytest.mark.登录测试 class Test_错误密码2: @pytest.mark.webtest def test_C001021(self): print('\n用例C001021') assert 1 == 1
|
1
| pytest cases -m webtest -s
|
四. 数据驱动
@pytest.mark.parametrize(变量1,变量2,... [(数据1,数据2...),(数据1,数据2...)...])
1 2 3 4 5 6 7 8 9 10 11 12
| class Test_错误登录: @pytest.mark.parametrize('username, password, expectedalert', [ (None, '88888888', '请输入用户名'), ('byhy', None, '请输入密码'), ('byh', '88888888', '登录失败 : 用户名或者密码错误'), ('byhy', '8888888', '登录失败 : 用户名或者密码错误'), ('byhy', '888888888', '登录失败 : 用户名或者密码错误'), ] ) def test_UI_0001_0005(self, username, password, expectedalert): alertText = loginAndCheck(username, password) assert alertText == expectedalert
|
五. fixture
5.1 初始化
@pytest.fixture : 声明式 初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import pytest
@pytest.fixture def createzhangSan(): print('\n *** createzhangSan ***') zhangSan = { 'username' : 'zhangsan', 'password' : '111111', 'invitecode' : 'abcdefg' } return zhangSan
def test_A001001(createzhangSan): print('\n用例 A001001') print('\ninvitecode is', createzhangSan['invitecode'])
def test_C001001(): print('\n用例 C001001')
""" test_1.py::test_A001001 *** createzhangSan *** 用例 A001001 invitecode is abcdefg PASSED
test_1.py::test_C001001 用例 C001001 PASSED """
|
5.2 清除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import pytest
@pytest.fixture def createzhangSan(): print('\n *** 执行创建张三账号的代码 ***') zhangSan = { 'username' : 'zhangsan', 'password' : '111111', 'invitecode' : 'abcdefg' }
yield zhangSan
print('\n *** 执行清除张三账号的代码 ***')
def test_A001001(createzhangSan): print('\n用例 A001001') print('\ninvitecode is', createzhangSan['invitecode'])
if __name__ == '__main__': pytest.main(['test_demo1.py', '-s'])
""" test_1.py::test_A001001 *** 执行创建张三账号的代码 ***
用例 A001001
invitecode is abcdefg PASSED *** 执行清除张三账号的代码 *** """
|
5.3 fixture 参数
如果需要不同的参数来初始化, 则需要使用 parametrize
装饰器, 并指定参数 indirect = True
fixture 参数化 案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import pytest
@pytest.fixture def createUser(request): print("\n **** 初始化 ****") print('\n *** createUser ***') user = { 'username': request.param[0], 'password': request.param[1], 'invitecode': 'abcdefg' }
yield user print("****** 清除 ********")
@pytest.mark.parametrize("createUser", [("zhangsan", "111111"), ("lisi", "111")], indirect=True) def test_A001001(createUser): print('\n用例 A001001') print('\nusername is', createUser['username']) print('测试行为1111')
if __name__ == '__main__': pytest.main(['test_demo1.py', '-s'])
""" test_demo/test_demo1.py::test_A001001[createUser0] **** 初始化 **** *** createUser *** PASSED [ 50%] 用例 A001001
username is zhangsan 测试行为1111 ****** 清除 ********
test_demo/test_demo1.py::test_A001001[createUser1] **** 初始化 **** *** createUser *** PASSED [100%] 用例 A001001
username is lisi 测试行为1111 ****** 清除 ******** """
|
5.4 使用范围
fixture 由小到大,有下面这几种级别范围
function
: 函数级别,缺省值- 会在测试函数结束时执行 fixture 对应的清除
class
: 类级别- 会在 类 里面的最后一个测试方法 结束时 执行 fixture 对应的清除
module
: 模块文件级别- 会在 模块文件 里面的最后一个测试方法 结束时 执行 fixture 对应的清除
package
: 目录级别- 会在 目录 里面的最后一个测试方法 结束时 执行 fixture 对应的清除
session
: 整个测试 级别- 会在 整个测试 的最后一个测试方法 结束时 执行 fixture 对应的清除
5.5 自动使用 fixture
需要默认使用时, 设置参数 autouse=True
, 如果不设置, 则需要在使用的函数中传入 fixture 函数名
fixture 使用案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| import pytest
pytestmark = pytest.mark.模块标签
@pytest.fixture(scope='function', autouse=True) def init_function(): print("\n 函数级 - 用例初始化") yield print("\n 函数级 - 数据清除")
@pytest.fixture(scope='class', autouse=True) def init_class(): print("\n 类级 - 用例初始化") yield print("\n 类级 - 数据清除")
@pytest.fixture(scope='module') def init_module(): print("\n 模块级 - 用例初始化") yield print("\n 模块级 - 数据清除")
@pytest.fixture(scope='package', autouse=True) def init_package(): print("\n 目录级 - 用例初始化") yield print("\n 目录级 - 数据清除")
@pytest.fixture(scope='session', autouse=True) def init_session(): print("\n 整个测试级 - 用例初始化") yield print("\n 整个测试级 - 数据清除")
@pytest.mark.类标签 class TestDemo: @pytest.mark.方法标签 def test_demo1(self, init_module): print("\n 测试用例 1 执行")
if __name__ == '__main__': pytest.main(['test_demo1.py', '-s'])
""" test_demo/test_demo1.py::TestDemo::test_demo1 整个测试级 - 用例初始化 目录级 - 用例初始化 模块级 - 用例初始化 类级 - 用例初始化 函数级 - 用例初始化 PASSED [100%] 测试用例 1 执行 函数级 - 数据清除 类级 - 数据清除 模块级 - 数据清除 目录级 - 数据清除 整个测试级 - 数据清除 """
|
六. allure 报告环境搭建
6.1 安装插件
1
| pip install Allure-Pytest
|
6.2 安装 allure
- 安装 JAVA 环境
- 下载 allure 压缩包
- 解压 压缩包,配置环境变量
path
中 配置环境变量 (G:\allure-2.13.3\bin
)
6.3 使用
1 2 3 4 5 6 7 8
| if __name__ == '__main__': pytest.main(['test_login.py', '-s', '--alluredir', '../report/tmp']) os.system('allure serve ../report/tmp')
|