目录
一: Pytest的安装:二: pytest的简单案例:三: 断言1: assert断言:2:异常断言测试:抛出的异常和指定的一致测试: 抛出的异常和测试的异常不一致。测试:将异常信息存储到变量中。测试:通过异常的内容捕获异常内容。
3:警告断言测试警告断言:将警告信息写入变量:通过警告内容捕获信息
四:setup和teardown函数案例:验证函数级别案例:验证类级别案例: 验证模块级别:
五:pytest.fixture的使用1: fixture的简单使用:2:conftest.py文件的使用3:@pytest.mark.usefixtures装饰
六:fixture参数1: scope参数:指定被标记函数(方法)的作用域测试function:测试:class测试: module:
2: params参数3:autouse参数
七:mark标记1: pytest.mark.xfail()2:pytest.mark.skip()3:pytest.mark.skipif()4: pytest.mark.parametrize()
八:配置文件九:常用插件1: 插件的安装2:生成测试报告3: 控制测试用例的执行顺序4: 失败重试5:取消插件
十: Yaml1:Yaml的语法规则2:Yaml数据结构的使用3: Python读取Yaml文件和写入Yaml文件1: 安装PyYaml库:2:读取Yaml文件:3:将内容写入到Yaml文件
一: Pytest的安装:
pip install -U pytest -i https://pypi.tuna.tsinghua.edu.cn/simple
二: pytest的简单案例:
Pytest能够识别test开头的方法和Test开头的类。默认不打印,需要指定打印: pytest.main([’-s’])如果类不以Test开头,类中的方法即使是以test开头也不会执行。
import pytest
def test_01():
print("我是test_01")
def test_02():
print("我是test_02")
class Test_demo(object):
def test_03(self
):
print("我是test_03")
def test_04(self
):
print("我是test_04")
class Tsts_demo(object):
def test_05(self
):
print("我是test_05")
if __name__
== '__main__':
pytest
.main
(['-s'])
运行结果:
三: 断言
断言是python自带的,不是unitest中的。断言的分类: assert断言,异常断言, 警告断言。
1: assert断言:
当断言预测失败时,备注信息会以AssertionError抛出,并在控制台输出。 案例:
import pytest
def test_1():
num
= 1 + 1
assert num
== 2, 'num==2条件不满足'
def test_2():
num
= 1 + 1
assert num
== 3, 'num==3条件不满足'
if __name__
== '__main__':
pytest
.main
(['-s'])
运行结果:
2:异常断言
出现指定异常则通过,如果出现不是指定的异常则报错。pytest.raises(预期的异常类,match = 异常的匹配信息)with pytest.raises(TypeError) as 变量: 这个变量存储该异常的所有信息。变量.__dict__获取变量的所有属性。
案例: 先给定一个闰年的函数作为被测试的函数:
def is_leap_year(year
):
if isinstance(year
, int) is not True:
raise TypeError
("传入的参数不是整数")
elif year
== 0:
raise ValueError
("公元元年是从公元一年开始!!")
elif abs(year
) != year
:
raise ValueError
("传入的参数不是正整数")
elif (year
% 4 == 0 and year
% 100 != 0) or year
% 400 == 0:
print("%d年是闰年" % year
)
return True
else:
print("%d年不是闰年" % year
)
return False
测试:抛出的异常和指定的一致
class TestAssert(object):
def test_01(self
):
with pytest
.raises
(TypeError
):
is_leap_year
('2020')
测试: 抛出的异常和测试的异常不一致。
class TestAssert(object):
def test_01(self
):
with pytest
.raises
(TypeError
):
is_leap_year
(0)
测试:将异常信息存储到变量中。
class TestAssert(object):
def test_01(self
):
with pytest
.raises
(TypeError
) as err_info
:
is_leap_year
('2000')
print("出现的异常类型是:", err_info
.type)
print("异常的所有信息:", err_info
.__dict__
)
测试:通过异常的内容捕获异常内容。
匹配异常信息中必须有参数这两个字,并且类型是TypeError。
class TestAssert(object):
def test_01(self
):
with pytest
.raises
(TypeError
, match
="参数") as err_info
:
is_leap_year
('2000')
3:警告断言
警告断言最大的区别是变量可以存储多个警告。pytest.warns()
主备工作:自定义三个抛出警告函数:
def make_warn():
warnings
.warn
("deprecated", DeprecationWarning
)
def not_warn():
pass
def user_warn():
warnings
.warn
("user warn", UserWarning
)
测试警告断言:
class TestWarns(object):
def test_01(self
):
with pytest
.warns
(DeprecationWarning
):
make_warn
()
def test_02(self
):
with pytest
.warns
(DeprecationWarning
):
user_warn
()
def test_03(self
):
with pytest
.warns
(DeprecationWarning
):
not_warn
()
测试结果:
将警告信息写入变量:
class TestWarns(object):
def test_01(self
):
with pytest
.warns
((DeprecationWarning
, UserWarning
)) as record
:
make_warn
()
user_warn
()
print("存储异常信息变量的长度:", len(record
))
print("存储异常信息变量第一个的信息:", record
[0].message
)
print("存储异常信息变量第一个的异常类型:", record
[0].category
)
print("异常信息的所有属性", record
.__dict__
)
测试结果:
通过警告内容捕获信息
class TestWarns(object):
def test_01(self
):
with pytest
.warns
((DeprecationWarning
), "user") as record
:
make_warn
()
结果:因为匹配内容中没有user,所以报错。
四:setup和teardown函数
函数级别:setup_function() teardown_function()
作用范围:只针对类外面的test函数起作用,每个都执行一次。
类级别: setup_class() teardown_class() setup_method() teardown_method()
setup_class() teardown_class(): 针对当前类只执行一次。 setup_method() teardown_method(): 针对当前类中的方法,每次执行都执行一遍。
模块级别:setup_model() teardown_model()
只有执行当前文件的开始和结束执行一次。
案例:验证函数级别
import pytest
def test_01():
print("我是普通函数01")
def test_02():
print("我是普通函数02")
def setup_function():
print("我是普通函数的前置函数")
def teardown_function():
print("我是普通函数的后置函数")
案例:验证类级别
import pytest
class Test_Demo(object):
def test_01(self
):
print("我是类中函数01")
def test_02(self
):
print("我是类中函数02")
@
classmethod
def setup_class(self
):
print("我是类前置函数")
@
classmethod
def teardown_class(self
):
print("我是类后置函数")
def setup_method(self
):
print("我是类中方法前置函数")
def teardown_method(self
):
print("我是类中方法后置函数")
if __name__
== '__main__':
pytest
.main
()
案例: 验证模块级别:
import pytest
def setup_module():
print("我是模块前置函数")
def test_01():
print("我是测试函数")
def teardown_module():
print("我是模块后置函数")
if __name__
== '__main__':
pytest
.main
()
五:pytest.fixture的使用
1: fixture的简单使用:
装饰在普通的函数上面,测试函数中传入普通函数的名,测试函数内部可以用普通函数的名,代表普通函数的返回值。
案例: 注意:运行测试函数前先运行被fixture装饰的函数。
import pytest
@pytest
.fixture
()
def get_url():
print("我就是个普通函数")
return "http://www.baidu.com"
def test_web(get_url
):
print("正在运行测试函数")
print("打印get_url的返回值:", get_url
)
if __name__
== '__main__':
pytest
.main
()
2:conftest.py文件的使用
(这个模块里面的被fixture装饰的函数可以直接用)
如果在测试中多个测试文件中用例用到同一个的fixture函数,则可以将其移动到conftest.py文件中, 所需的fixture对象会自动被pytest发现,而不需要再每次导入。
conftest.py文件名固定
在conftest.py文件中实现共用的fixture函数
案例: 1: 新建conftest.py:里面定义一个被@pytest.fixture()修饰只返回百度地址的函数。
import pytest
@pytest
.fixture
()
def get_baidu_url():
return "www.baidu.com"
2: 另一个文件不导包的情况下直接用conftest中的函数名。
import pytest
def test_01_demo(get_baidu_url
):
print(get_baidu_url
)
if __name__
== '__main__':
pytest
.main
()
3: 运行结果:
3:@pytest.mark.usefixtures装饰
作用 : 如果我不想使用被装饰函数的返回值,但是需要函数执行前调用一下被装饰函数,那么可以使用@pytest.mark.usefixtures('被装饰函数名')来处理。 案例:
import pytest
@pytest
.fixture
()
def return_url():
print("我是百度地址: www.baidu.com")
return "www.baidu.com"
@pytest
.mark
.usefixtures
('return_url')
def test_demo():
print("我是测试函数")
if __name__
== '__main__':
pytest
.main
()
六:fixture参数
1: scope参数:指定被标记函数(方法)的作用域
function : 每个测试用例都要执行一次。(默认)class: 作用于整个类,每个类只执行一次。module : 作用域整个模块(文件),每个模块只执行一次。session : 作用域整个项目,每个项目只执行一次。
测试function:
import pytest
@pytest
.fixture
(scope
='function')
def foo():
print("我是被fixture修饰的函数")
return "哈喽哈喽......."
def test_01(foo
):
print("我是普通测试01:")
print(foo
)
def test_02(foo
):
print("我是普通测试02")
print(foo
)
class Test_demo(object):
def test_03(self
, foo
):
print("我是类中的测试用例03: ")
print(foo
)
def test_04(self
, foo
):
print("我是类中的测试用例04: ")
print(foo
)
if __name__
== '__main__':
pytest
.main
()
测试:class
修改刚才代码: 变成class,运行结果:注意:非类的测试用例还是每次测试用例都要调用。
测试: module:
2: params参数
params 参数接收list类型的参数。对于param里面的每个值,fixture函数都会去遍历执行一次相应的每次都会驱动使用fixture函数的测试函数执行一次。 测试
import pytest
@pytest
.fixture
(params
=['renshanwen', '15', '1173714248@qq.com'])
def username(request
):
print("我是被fixture修饰的函数")
return request
.param
def test_demo(username
):
print("我是测试用例")
print(username
)
if __name__
== '__main__':
pytest
.main
()
运行结果:
由于装饰器有列表中有三个,所以测试用例也会执行三次,就会调用三次被装饰函数。
3:autouse参数
pytest.fixture(autouse=False) 的autouse参数默认为False, 不会自动执行。设置为True时,当前运行的所有测试函数在运行前都会执行fixture函数。
演示:
import pytest
@pytest
.fixture
(autouse
=True)
def before():
print('不管你是谁,都要先调用我')
def test_1():
print("test_1")
class Test2:
def test_2(self
):
print('test_2')
def test_3(self
):
print('test_3')
七:mark标记
1: pytest.mark.xfail()
1: 将测试函数标记为预期失败。
import pytest
@pytest
.mark
.xfail
(reason
= "功能不完善")
def test_1_demo():
print("我就是个被装饰的")
if __name__
== '__main__':
pytest
.main
()
运行结果:
2:pytest.mark.skip()
1: 无条件跳过测试用例:
import pytest
@pytest
.mark
.skip
(reason
= "跳过执行")
def test_1_demo():
print("我就是个被装饰的")
if __name__
== '__main__':
pytest
.main
()
3:pytest.mark.skipif()
1:有条件地跳过测试函数。
import pytest
@pytest
.mark
.skipif
(condition
= True, reason
= "跳过")
def test_1_demo():
print("我就是个被装饰的111111111111111")
@pytest
.mark
.skipif
(condition
= False, reason
= "不跳过")
def test_2_demo():
print("我就是个被装饰的222222222222222")
if __name__
== '__main__':
pytest
.main
()
2: 运行结果:
4: pytest.mark.parametrize()
目的:参数化Fixture方法和测试函数
import pytest
@pytest
.mark
.parametrize
(['username', 'password'], [('renshanwen', '12345678'), ('niuniu', '87654321')])
def test_login(username
, password
):
print("\n")
print("username是:", username
)
print("password是:", password
)
if __name__
== '__main__':
pytest
.main
()
运行结果:
八:配置文件
配置文件名字, pytest.ini固定。配置文件中的注释是 ;addopts = -s test_20.py : 指定运行的命令testpaths = ./ : 指定测试文件的路径python_files = test*.py : 识别以test开头的文件python_classes = Test* : 识别以Test开头的类python_functions = mike* : 识别以test开头的函数 案例:
[pytest
]
addopts
= -s test_12
.py test_13
.py
testpaths
= ./
python_files
= test_
*.py
python_classes
= Test_
*
python_functions
= test_
*
;在ini文件中注释语句是以分号开始的
, 所有的注释语句不管多长都是独占一行直到结束的
九:常用插件
1: 插件的安装
workon test_v7 # 进入虚拟环境 pip install pytest-html # 生成测试报告 pip install pytest-ordering # 控制函数执行顺序 pip install pytest-rerunfailures # 失败重试
2:生成测试报告
1: 测试代码:
import pytest
def test_01():
print("测试成功的测试用例")
assert True
def test_02():
print("测试失败的测试用例")
assert False
if __name__
== '__main__':
pytest
.main
()
2: 执行命令:
pytest -s test17_port.py --html=./report.html
3: 报告展示:
3: 控制测试用例的执行顺序
import pytest
@pytest
.mark
.run
(order
= 3)
def test_01():
print("我是测试用例一")
@pytest
.mark
.run
(order
=1)
def test_02():
print("我是测试用例二")
@pytest
.mark
.run
(order
=2)
def test_03():
print("我是测试用例三")
if __name__
== '__main__':
pytest
.main
()
运行结果:
4: 失败重试
1: 代码:
import pytest
def test_01():
print("测试成功的测试用例")
assert True
def test_02():
print("测试失败的测试用例")
assert False
if __name__
== '__main__':
pytest
.main
()
2:执行命令:
pytest -s test17_port.py --reruns 2
3: 运行结果:
5:取消插件
-p no:ordering : 取消排序-p no:html : 取消生成测试报告-p no:rerunfailures : 取消失败重试
十: Yaml
1:Yaml的语法规则
大小写敏感。
使用缩进表示层级关系。
缩进时不允许使用tab键,只允许使用空格。
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。
Yaml在线编辑器地址: http://old.qqe2.com/jsontool/yaml.php
2:Yaml数据结构的使用
1: Yaml与python的转换: 注意:Yaml对象冒号后面的空格不能省略。 2: Yaml中的数组: 3: Yaml中的锚点和引用:
3: Python读取Yaml文件和写入Yaml文件
1: 安装PyYaml库:
pip3 install -U PyYAML
2:读取Yaml文件:
准备测试文件test.yaml
Search_Data
:
search_test_001
:
value
: 456
expect
: [4,5,6]
search_test_002
:
value
: "你好"
expect
: {"value":"你好"}
yaml.read.py文件中读取test.yaml中的数据:
import yaml
with open("./test.yaml", "r") as f
:
data
= yaml
.load
(f
, Loader
=yaml
.FullLoader
)
print(data
运行结果:
3:将内容写入到Yaml文件
1: yaml_write.py进行写入
import yaml
data
={'Search_Data': {
'search_test_002': {'expect': {'value': '你好'}, 'value': '你好'},
'search_test_001': {'expect': [4, 5, 6], 'value': 456}
}
}
with open('./yaml_hello.yaml', 'w', encoding
='utf-8') as f
:
yaml
.dump
(data
, f
,allow_unicode
=True)
2: 执行结果: