pytest是一个基于Python的自动化测试框架,其实现原理主要包括以下几个部分:
相比于其它自动化测试框架,pytest的优势在于:
总之,pytest框架具有易用、扩展性强、使用文档齐全等优点,适合用于各种规模的测试项目。
pip install pytest
进程入cmd界面执行pip install pytest,如果需要指定版本执行pip install pytest=XXX(版本号)
pycharm中安装可以进入Terminal界面,执行pip install pytest
或者通过pycharm中的settings --->Python Interpreter中进行安装,点击加号进入下面界面
输入pytest,找到pytest对应的第三方库,点击下方的Install Package就可以了
#简单实例,首先创建一个test_sample.py文件,再复制下面的代码到文件中
#功能函数
def func(x):
return x+1
#测试功能函数的用例
def test_case():
assert func(1) ==2
思考:1. 为什么创建的文件名称需要test开头;2. 测试用例函数为什么也是需要test开头,是因为pytest是对文件和用例函数有特定的命名要求,下面我们看看pytest的命名规则
类型 | 规则 | 实例 |
---|---|---|
文件 | test_开头 或者 _test 结尾 | test_sample.py或者sample_test.py |
类 | Test 开头 | TestDemo |
方法/函数 | test_开头 | test_case() |
注意:测试类中不可以添加__init__ 构造函数,测试包的命名是没有要求的 |
用例结构3部分组成:用例名称,用例步骤,用例
#用例示例
def test_***(self):
# 测试步骤1
# 测试步骤2
# 断言 实际结果 对比 预期结果
assert ActualResult == ExpectedResult
#类级别的用例示例
class TestXXX:
def setup(self):
# 资源准备
pass
def teardown(self):
# 资源销毁
pass
def test_XXX(self):
# 测试步骤1
# 测试步骤2
# 断言 实际结果 对比 预期结果
assert ActualResult == ExpectedResult
断言(assert),是一种在程序中的一阶逻辑(如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果。当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。
2.断言的用法
"""
断言写法
assert <表达式>
assert <表达式>,<描述>
assert ;
assert : ;
"""
#第一种:assert <表达式>
def test_case():
assert True
def test_c():
a = 1
b = 1
c = 2
assert 'abc' in "abcd"
#第二种:assert <表达式>,<描述>
def test_case1():
a = 1
b = 1
c = 2
assert a + b == c, f"{a}+{b}=={c}, 结果为真"
import sys
def test_plat():
assert ('win32' in sys.platform), "该代码只能在 win32下执行"
在接口自动化测试中,用setup方法可以进行测试前的初始化、参数配置等工作,用teardown方法可以进行测试后的清理、还原、退出等工作。pytest测试框架提供了5种类型的setup和teardown的方法,具体如下
类型 | 规则 |
---|---|
setup_module/teardown_module | 全局模块级 |
setup_class/teardown_class | 类级,只在类中前后运行一次 |
setup_function/teardown_function | 函数级,在类外 |
setup_method/teardown_method | 方法级,类中的每个方法执行前后 |
setup/teardown | 在类中,运行在调用方法的前后(重点) |
模块级别的setup和teardown,只执行一次
#模块级别,只被调用一次
def setup_module():
print("资源准备:模块级别setup module")
def teardown_module():
print("资源准备:模块级别teardown module")
#定义函数级别的测试用例
def test_case1():
print('用例01')
#定义函数级别的测试用例
def test_case2():
print('用例02')
函数级别的setup和teardown,每次函数调用的时候,都会调用它们
#定义函数级别的测试用例
def test_case1():
print('用例01')
#定义函数级别的测试用例
def test_case2():
print('用例02')
#函数级别,每次函数调用的时候,都会调用
def setup_function():
print("资源准备:函数级别setup function")
def teardown_function():
print("资源准备:函数级别teardown function")
类级别与方法级别的setup和 teardown
class TestDemo:
#执行类前后分别执行setup_class teardown_class
def setup_class(self):
print("TestDemo setup_class")
def teardown_clase(self):
print("TestDemo teardown_class")
#每个类里面的方法前后分别执行setup,teardown
def setup(self):
print("TestDemo setup")
def teardown(self):
print("TestDemo teardown")
def test_demo1(self):
print("test_demo1")
def test_demo2(self):
print("test_demo2")
参数化的目的:
参数化使用场景:
def test_param_login_ok():
# 登录成功
username = "right"
password = "right"
login(username,password)
def test_param_login_fail():
# 登录失败
username = "wrong"
password = "wrong"
login(username,password)
通过pytest 参数化实现方法,装饰器:@pytest.mark.parametrize
#计算两数在【-99,99】之间的和
class Calculator:
def add(self, a, b):
if a > 99 or a < -99 or b > 99 or b < -99:
# print("请输入范围为【-99, 99】的整数或浮点数")
return "参数大小超出范围"
return a + b
ca = Calculator()
#第一个参数对应的是用例的参数,第二个参数对应的参数的value值,第三个参数对应的是用例的名称
@pytest.mark.parametrize(
"a,b",
[[99, 99], [100, 100], [-100, -100], [-99, -99], [0, 59]],
ids=["最大值相加", "100相加", "-100相加", "-99相加", "两数不相同相加"],
)
def test_add(a, b):
data = ca.add(a, b)
if a > 99 or a < -99 or b > 99 or b < -99:
assert data == "参数大小超出范围"
else:
assert data == a + b
#单参数,可以将数据放在列表中
search_list = ['appium','selenium','pytest']
@pytest.mark.parametrize('name',search_list)
def test_search(name):
assert name in search_list
#多参数情况
# 数据放在元组中
@pytest.mark.parametrize("test_input,expected",[
("3+5",8),("2+5",7),("7+5",12)
])
def test_mark_more(test_input,expected):
assert eval(test_input) == expected
# 数据放在列表中
@pytest.mark.parametrize("test_input,expected",[
["3+5",8],["2+5",7],["7+5",12]
])
def test_mark_more(test_input,expected):
assert eval(test_input) == expected
#笛卡尔积
"""
两组数据
a=[1,2,3]
b=[a,b,c]
对应组合形势
(1,a),(1,b),(1,c)
(2,a),(2,b),(2,c)
(3,a),(3,b),(3,c)
"""
@pytest.mark.parametrize("b",["a","b","c"])
@pytest.mark.parametrize("a",[1,2,3])
def test_param1(a,b):
print(f"笛卡积形式的参数化中 a={a} , b={b}")
对于ids重新对用例命名会出现中文无法显示的情况,那是因为默认的编码格式不是utf-8,需要在项目中建一个conftest.py文件,文件内容如下:
# 创建conftest.py 文件 ,将下面内容添加进去,运行脚本
def pytest_collection_modifyitems(items):
"""
测试用例收集完成时,将收集到的用例名name和用例标识nodeid的中文信息显示在控制台上
"""
for i in items:
i.name=i.name.encode("utf-8").decode("unicode_escape")
i._nodeid=i.nodeid.encode("utf-8").decode("unicode_escape")
pytest -s test_mark_zi_09.py -m=webtest
pytest -s test_mark_zi_09.py -m apptest
pytest -s test_mark_zi_09.py -m "not ios"
import pytest
@pytest.mark.webtest
def test_case():
assert 1 ==1
@pytest.mark.apptest
def test_case1():
assert 'abc' in 'acbccc'
@pytest.mark.iostest
def test_case2():
assert 5+6 > 6
warning是无法识别到iostest的标签,这个没有关系,如果想要pytest识别它们,可以新增一个pytest.ini文件,文件内容如下:
[pytest]
markers = webtest
apptest
iostest
"""
调试时不想运行这个用例
标记无法在某些平台上运行的测试功能
在某些版本中执行,其他版本中跳过
比如:当前的外部资源不可用时跳过
如果测试数据是从数据库中取到的,
连接数据库的功能如果返回结果未成功就跳过,因为执行也都报错
解决 1:添加装饰器
@pytest.mark.skip
@pytest.mark.skipif
解决 2:代码中添加跳过代码
pytest.skip(reason)
xfail 使用场景
与 skip 类似 ,预期结果为 fail ,标记用例为 fail
用法:添加装饰器@pytest.mark.xfail
"""
import sys
import pytest
@pytest.mark.skip(reason="代码未开发完")
def test_aaa():
print("代码未开发完")
assert 1==2
def check_login():
return False
def test_a():
print("start")
if not check_login():
pytest.skip("kkkkkk")
print("end")
@pytest.mark.skipif(sys.platform=='win32', reason='does not run on windows')
def test_case():
assert True
@pytest.mark.skipif(sys.platform=='darwin', reason="does not run on mac")
def test_case1():
assert True
@pytest.mark.skipif(sys.version_info <(3,6), reason="requires python3.6 or higher")
def test_case2():
assert True
@pytest.mark.xfail
def test_case3():
print("test_xfail 方法执行")
assert 1 ==2
xfail = pytest.mark.xfail
@xfail(reason="bug 120")
def test_case4():
assert 1==2
def test_xfail():
print("******开始测试********")
pytest.xfail(reason="该功能尚未完成")
print("测试过程")
assert 1==1
pytest 文件名.py
--lf(--last-failed)
只重新运行故障。--ff(--failed-first)
先运行故障然后再运行其余的测试—help 可以查看pytest所有的命令参数
-x 用例一旦失败(fail/error),就立刻停止执行
--maxfail=num 用例达到
-m 标记用例
-k 执行包含某个关键字的测试用例
-v 打印详细日志
-s 打印输出日志(一般-vs一块儿使用)
—collect-only(测试平台,pytest 自动导入功能 )
if __name__ == '__main__':
# 1、运行当前目录下所有符合规则的用例,包括子目录(test_*.py 和 *_test.py)
pytest.main()
# 2、运行test_mark1.py::test_dkej模块中的某一条用例
pytest.main(['test_mark1.py::test_dkej','-vs'])
# 3、运行某个 标签
pytest.main(['test_mark1.py','-vs','-m','dkej'])
运行方式
`python test_*.py `
def test_raise():
with pytest.raises(ValueError):
raise ValueError("value msut be 0 or None")
def test_raise1():
with pytest.raises(ValueError) as exc_info:
raise ValueError("value must be 42")
assert exc_info.type is ValueError
assert exc_info.value.args[0] == 'value must be 42'