知识点1:
1、测试结果信息阅读
-
passed表示通过,有个简写.
-
failed表示失败,有个简写F
2、命令行参数
-
-h:帮助
-
-version:版本信息
3、测试用例命名规则:
-
测试函数必须以test开头
-
测试类必须以Test开头,但测试类不能有init方法
-
测试文件必须以test/Test开头,或者test/Test结尾
4、运行指定的测试用例:指定文件[::类名][::函数名]
-
用例搜索的规则默认是pytest执行当前目录及子目录(遍历)
-
指定类名运行:pytest 文件名::类名
-
指定函数名运行:pytest 文件名::类名::函数名
-
指定函数名运行:pytest 文件名::函数名
-
指定目录下的文件运行:pytest 目录名\文件名::函数名
-
pytest .\test_a02\test_a02_a.py::test_001 (test_a02_a.py文件下的test_001 类;终端)
5、pytest.main()执行用例
- 命令行参数args,列表类型,一个或多个str组成,逗号分隔
import pytest
def test_a01():
assert 1 == 1
if __name__ == '__main__': # 运行测试的话,需要在pycharm中设置python集成工具=>unittest
# pytest.main() # 等价于pytest命令
# pytest.main([__file__]) # 测试当前文件
# pytest.main(['--version'])
pytest.main['-sv', __file__] # 多参数调用
6、运行指定测试用例——k参数
-
匹配测试类或测试方法的名字部分
-
大小写敏感
-
逻辑运算
表达式 | 说明 |
---|---|
-k login | 测试类名或函数名中包含login的用例 |
-k not login | 不包含login的 |
-k login and success | 包含login且包含success的 |
-k login or success | 包含login或success的 |
-k login and not success | 包含login且不包含success的 |
import pytest
def test_login_success():
pass
def test_login_failed():
pass
def test_logout_success():
pass
def test_logout_failed():
pass
if __name__ == '__main__':
pytest.main(['-k success and not failed', '--collect-only', __file__]) # –collect-only 含义:展示给定配置下哪些测试用例会被运行
7、pytest.mark.skip/skipif
- 有条件的忽略skipif
import pytest, sys
@pytest.mark.skipif(1 == 1, reason='if 1==1 skip this') # 条件满足就忽略测试 reason:标注原因
def test_a01_1(reason='i wanna skip this case'):
pass
@pytest.mark.skipif(1 == 2, reason='if 1==2 skip this') # 条件不满足不会跳过
def test_a01_2():
pass
@pytest.mark.skipif(sys.platform == 'linux', reason='if linux skip this') # sys.platform 操作系统平台标识
def test_a01_3():
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
- 输出结果s
import pytest
@pytest.mark.skip
def test_a01_1(reason='i wanna skip this case'):
pass
def test_a01_2():
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
# pytest.main(['--collect-only', __file__]) # 能被采集
# pytest.main([__file__]) # 输出s
import pytest
pytestmark = pytest.mark.skip('skip all cases') # pytestmark 这个名字不可更改
def test_a01_1():
pass
def test_a01_2():
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
def test_a01_1():
arrow = pytest.importorskip('arrow') # 这个库在当前环境下没有的话会报错,有会自动导入
nowtime = arrow.now().format('YYYY-MM-DD') # 获取当前时间
print(nowtime)
if __name__ == '__main__':
pytest.main(['-sv', __file__])
8、pytest.mark.parameterize参数化
-
当对一个测试函数进行测试时,通常会给函数传递多组参数。比如测试账号登录,我们需要模拟各种千奇百怪的账号密码。当然,我们可以把这些参数写在测试函数内部进行遍历。不过虽然参数众多,但仍然是一个测试,当某组参数导致断言失败,测试也就终止了。通过异常捕获,我们可以保证所有参数完整执行,但要分析测试结果就需要做不少额外的工作
-
在pytest中,我们有更好的解决方法,就是参数化测试,即每组参数都独立执行一次测试。使用的工具就是 pytest.mark.parametrize(argnames, argvalues)
-
分析parameterize的源码,能看到调用时要用到的参数
参数 | 含义 |
---|---|
argnames | 必选,参数名,逗号分隔的字符串,表示一个或多个参数名字,也可以是字符串组成的列表或元组 |
argvalues | 必选,参数值列表 |
indirect | 为True时,argnames一定是一个fixture的函数名称,argvalues的值传入对应的fixture内,相当于@pytest.fixture(params=)的做法,默认为False |
ids | 用例id,标记子用例执行名称,与argvalues数量一致,未指定则自动生成,默认为None |
scope | 作用范围,类似于fixture中的scope参数,表示参数的范围,范围用于按参数实例对测试进行分组。它将覆盖任何fixture函数定义的范围,允许使用测试上下文或配置设置动态范围 |
参数匹配,argnames跟测试函数的参数必须一致否则会报错
import pytest
@pytest.mark.parametrize('arg0', [0, 1]) # (0, 1) '01' 不推荐用
def test_a01_1(arg0):
print(arg0)
if __name__ == '__main__':
pytest.main(['-sv', __file__])
多个参数
import pytest
@pytest.mark.parametrize('arg0, arg1', [(0, 1), (2, 2), (3, 4)]) # argvalues 往往是通过其他程序读取到
def test_a01_1(arg0, arg1):
print(f'arg0 is {arg0},arg1 is {arg1}')
assert arg0 == arg1
if __name__ == '__main__':
pytest.main(['-sv', __file__])
多个装饰器
import pytest
@pytest.mark.parametrize('arg0', [1, 2, 3]) # 多装饰器 笛卡尔积的效果 两两相乘
@pytest.mark.parametrize('arg1', [4, 5, 6])
def test_a01_1(arg0, arg1):
print(f'arg0 is {arg0},arg1 is {arg1}')
if __name__ == '__main__':
pytest.main(['-sv', __file__])
ids参数的作用
import pytest
def login(username, password):
data = {'allen': '12', 'smith': '34', 'ford': '56'}
if data.get(username) == password:
return 'success'
else:
return 'failed'
@pytest.mark.parametrize('username, password, expect', [('allen', '12', 'success'), ('smith', '344', 'failed')],
ids=['login success', 'login failed'])
def test_a01_1(username, password, expect):
assert login(username, password) == expect
if __name__ == '__main__':
pytest.main(['-sv', __file__])
9、setup和teardown(不常用,有更好的)
-
setup往往指测试用例中的配置部分(预置条件),teardown则对应销毁部分(环境拆除)
-
PyTest支持xUnit style结构,setup()和teardown()方法用于初始化和清理测试环境,可以保证测试用例的独立性
方法 | 作用域 | 说明 |
---|---|---|
setup_function/teardown_function | 函数function | 每个函数都使用一次(类中的函数叫方法不记在内) |
setup_class/teardown_class | 类class | 需要在类中定义,一个类运行一次 |
setup_module/teardown_function | 模块(一个py文件) | 一个模块运行一次 |
setup_method/teardown_method | 方法(类中函数) | 一个方法运行一次 |
setup/teardown | 方法(类中函数) | 一个方法运行一次 |
import pytest
def setup_function():
print('\n函数前执行 setup')
def teardown_function():
print('\n函数后执行 teardown')
def test_a01_1():
pass
def test_a01_2():
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
class Test_all():
def setup_class(self):
print('\n方法前执行 setup')
def teardown_class(self):
print('\n方法后执行 teardown')
def test_a01_1(self): # method 方法不叫函数
pass
def test_a01_2(self):
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
def test_a01_3():
pass
def setup_module():
print('\n模块前执行 setup')
def teardown_module():
print('\n模块后执行 teardown')
class Test_all():
def test_a01_1(self): # method 方法不叫函数
pass
def test_a01_2(self):
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
class Test_all():
def setup_method(self):
print('\n方法前执行 setup01')
def teardown_method(self):
print('\n方法后执行 teardown01')
def setup(self):
print('\n方法前执行 setup02')
def teardown(self):
print('\n方法后执行 teardown02')
def test_a01_1(self): # method 方法不叫函数
pass
def test_a01_2(self):
pass
if __name__ == '__main__':
pytest.main(['-sv', __file__])
10、pytest-base-url插件
import pytest, requests
def test_a01_1(base_url): # 当作为fixture使用的时候是base_url,安装的是pytest-base-url
resp = requests.get(base_url)
assert resp.status_code == 200
if __name__ == '__main__':
pytest.main(['-sv', '--base-url', 'https://www.baidu.com', __file__])
# pytest.main(['-sv', __file__]) # 使用配置文件执行,本级目录下新建pytest.init文件,新增一行 base_url = https://www.baidu.com
11、pytest-repeat插件
import pytest
@pytest.mark.repeat(3)
def test_a01_1():
print('01 testing')
assert 1 == 1
if __name__ == '__main__':
pytest.main(['-sv', __file__])
import pytest
def test_a01_1():
print('01 testing')
assert 1 == 1
if __name__ == '__main__':
pytest.main(['-sv','--count=3', __file__])
import pytest
def test_a01_A1():
print('A1 testing')
assert True
def test_a01_A2():
print('A2 testing')
assert True
def test_a01_B1():
print('B1 testing')
assert True
def test_a01_B2():
print('B2 testing')
assert True
if __name__ == '__main__':
pytest.main(['-sv', '--count=2', '--repeat-scope=session', __file__]) # session/module/class都是1212的顺序
12、allure生成报告