自动化测试神器:Python之Pytest库入门使用
自动化测试神器:Python之Pytest库入门使用
本文写作思路和建议:
1、本文从Python的Pytest库基本安装使用开始讲起,详细说明pytest如何操作,如何快速入门?
2、同时说明了pytest的常用特性,如何在自动化测试中进行应用,达到应有的测试效率?
3、建议:阅读本文基本可以掌握Pytest的用法,另外学习的时候建议和Unittest库进行对比着使用,将会达到更好的效果。
1 pytest简介、安装和用例运行
1.1 pytest简介
① 对case可以进行设置跳过,也可以进行标记(比如失败等);
② 可以重复执行失败的case;
③ 可以兼容执行unittest编写的case;
④ 有很多第三方的插件,比如报告allure等;
⑤ 支持持续集成;
⑥ 和unittest一样支持参数化,但Pytest方法更多,更灵活;
。。。
1.2 Pytest安装
pip install -U pytest
pip show pytest
1.3 pytest如何运行用例?
A、文件名规则:test_*.py和 *_test.py命名的函数;
B、函数名规则:以test_开头的函数;
C、python包的规则:同python一样,包需要有__init__.py文件;
D、静默运行:以-q或-quiet参数进行静默运行函数;
1、通过文件名去运行,比如在代码中加if __name **== ‘**main__’:
pytest.main(“-s”, “test_mm.py”);
2、通过命令行调用运行,直接用pytest -s test_mm.py;
3、直接匹配某个目录下所有符合规则的case去运行,比如pytest -vs,或者将规则写入pytest.ini文件中。
# test_mm.py
# -*- coding:utf-8 -*-
import pytest
class TestClass:
def test_you(self):
y = "you"
assert "y" in y
def test_hai(self):
h = "hai"
assert "gg" not in h
if __name__ == '__main__':
pytest.main(["-s", "test_mm.py"])
# 输出:
# test_mm1.py ..
# ============================== 2 passed in 0.08s ==============================
2 pytest的setup、teardown特性
方法 | 运行级别 | 说明 |
---|---|---|
setup_module() | 模块级别 | 整个.py模块开始前只执行一次,如打开一次浏览器 |
teardown_module() | 模块级别 | 整个.py模块结束后只执行一次,如关闭一次浏览器 |
setup_function() | 函数级别 | 每个函数级别用例开始前都执行,此方法不在类中 |
teardown_function() | 函数级别 | 每个函数级别用例结束后都执行,此方法不在类中 |
setup_class() | 类级别 | 整个测试类开始前只执行一次,和Unittest基本一样 |
teardown_class() | 类级别 | 整个测试类结束后只执行一次,和Unittest基本一样 |
setup_method() | 方法级别 | 类里面每个用例执行前都会执行 |
teardown_method() | 方法级别 | 类里面每个用例结束后都会执行 |
setup() | 方法细化级别 | 类里面每个用例执行前都会执行 |
teardown() | 方法细化级别 | 类里面每个用例结束后都会执行 |
# -*- coding:utf-8 -*-
import pytest
def setup_module():
print("setup_module:整个.py模块开始前只执行一次")
def teardown_module():
print("teardown_module:整个.py模块结束后只执行一次")
def setup_function():
print("setup_function:每个函数级别用例开始前都执行")
def teardown_function():
print("teardown_function:每个函数级别用例结束后都执行")
def test_one():
print("用例1")
def test_two():
print("用例2")
class TestOne():
def setup_class(self):
print("setup_class:整个测试类开始前只执行一次")
def teardown_class(self):
print("teardown_class:整个测试类结束后只执行一次")
def setup_method(self):
print("setup_method:类里面每个用例执行前都会执行")
def teardown_method(self):
print("teardown_method:类里面每个用例结束后都会执行")
def setup(self):
print("setup:类里面每个用例执行前都会执行")
def teardown(self):
print("teardown:类里面每个用例结束后都会执行")
def test_thr(self):
print("用例3")
def test_fo(self):
print("用例4")
if __name__ == "__main__":
pytest.main(["-s", "test_set_tear.py"])
# 输出为:
# setup_module:整个.py模块开始前只执行一次
# setup_function:每个函数级别用例开始前都执行
# 用例1
# .teardown_function:每个函数级别用例结束后都执行
# setup_function:每个函数级别用例开始前都执行
# 用例2
# .teardown_function:每个函数级别用例结束后都执行
# setup_class:整个测试类开始前只执行一次
# setup_method:类里面每个用例执行前都会执行
# setup:类里面每个用例执行前都会执行
# 用例3
# .teardown:类里面每个用例结束后都会执行
# teardown_method:类里面每个用例结束后都会执行
# setup_method:类里面每个用例执行前都会执行
# setup:类里面每个用例执行前都会执行
# 用例4
# .teardown:类里面每个用例结束后都会执行
# teardown_method:类里面每个用例结束后都会执行
# teardown_class:整个测试类结束后只执行一次
# teardown_module:整个.py模块结束后只执行一次
4 passed in 0.14s
3 pytest如何进行用例断言?
断言 | 说明 |
---|---|
assert a | 判断 a为真 |
assert not a | 判断 a不为真 |
assert a in b | 判断 b 包含 a |
assert a == b | 判断 a 等于 b |
assert a != b | 判断 a 不等于 b |
# -*- coding:utf-8 -*-
import pytest
class TestU():
def test_f(self):
a = 3
b = 4
s = a + b
return s
def test_func(self):
assert self.test_f() == 7, "计算结果不是7"
if __name__ == "__main__":
pytest.main(["-s", "test_assert.py"])
# 输出为:
# test_assert.py .. [100%]
# ================== 2 passed in 0.31s ====================
4 pytest中的fixture特性
4.1 fixture简介
4.2 fixture参数
参数原型:fixture(scope=“function”, params=None, autouse=False, ids=None, name=None):
参数 | 说明 |
---|---|
scope | 默认:function,还有class、module、package、session |
autouse | 默认:False,手动调用该fixture;为True,所有作用域内的测试用例都会自动调用该fixture |
params | 一个可选的参数列表 |
ids | 每个字符串id的列表 |
name | fixture的名称, 默认为装饰函数的名称,同一模块的fixture相互调用建议写个不同的name |
4.3 示例说明
# -*- coding:utf-8 -*-
import pytest
# 不带参数时默认scope="function"
@pytest.fixture
def case():
print("这个是登陆功能!")
def test_one(case):
print("用例1需要登陆,然后进行操作one")
def test_two():
print("用例2不需要登陆,直接操作two")
@pytest.fixture
def case1():
print("输入验证码")
def test_three(case):
print("用例3需要登陆,然后操作three")
@pytest.mark.usefixtures("case", "case1")
def test_four(case1):
print("先登录,再输入验证码,最后操作four")
@pytest.fixture(autouse=True)
def case2():
print("所有用例都会调用case2")
if __name__ == "__main__":
pytest.main(["-s", "test_mfixture.py"])
# 输出为:
# test_mfixture.py::test_one 所有用例都会调用case2
# 这个是登陆功能!
#PASSED [ 25%]用例1需要登陆,然后进行操作one
#test_mfixture.py::test_two 所有用例都会调用case2
#PASSED [ 50%]用例2不需要登陆,直接操作two
#test_mfixture.py::test_three 所有用例都会调用case2
# 这个是登陆功能!
#PASSED [ 75%]用例3需要登陆,然后操作three
#test_mfixture.py::test_four 所有用例都会调用case2
# 这个是登陆功能!
# 输入验证码
#PASSED [100%]先登录,再输入验证码,最后操作four
#=========== 4 passed in 0.03s ==========
5 pytest如何跳过用例执行?
和Python中break 跳出循环类似。
# -*- coding:utf-8 -*-
import pytest
import time
@pytest.fixture()
def start():
print("打开浏览器,输入用户名和密码登陆")
yield
print("关闭浏览器")
def test_1(start):
print("用例1......")
i = 1
while True:
print(time.time())
i += 1
if i == 6:
pytest.skip("打印5次时间后,第六次不再打印了~")
if __name__ == '__main__':
pytest.main(["-s", "test_pytest_skip1.py"])
# 输出为:
# test_pytest_skip1.py::test_1 打开浏览器,输入用户名和密码登陆
# SKIPPED (打印5次时间后,第六次不再打印了~) [100%]用例1......
# 1668677189.0525532
# 1668677189.0525532
# 1668677189.0525532
# 1668677189.0525532
# 1668677189.0525532
# Skipped: 打印5次时间后,第六次不再打印了~
# 关闭浏览器
# ======== 1 skipped in 0.02s ===========
6 pytest的mark特性
@pytest.mark.自定义名称
# -*- coding:utf-8 -*-
import pytest
@pytest.mark.login
def test_login():
print("用户登陆")
@pytest.mark.case_a
def test_case_a():
print("执行用例a")
@pytest.mark.case_b
def test_case_b():
print("执行用例b")
@pytest.mark.quit
def test_quit():
print("用户退出")
运行规则如下:
1、运行一个标记:pytest -s -m login test_mark.py;
2、运行多个标记:pytest -s -m “login or case_a or case_b or quit” test_mark.py;
3、不运行某个标记,直接取反即可:pytest -s -m “not quit” test_mark.py
7 pytest参数化如何使用?
7.1 parametrize方法
parametrize(argnames, argvalues, indirect=False, ids=None, scope=None);
参数 | 说明 | 格式 | 备注 |
---|---|---|---|
argnames | 参数名称 | 字符串"arg1,arg2,arg3" | 也可以list或者tuple |
argvalues | 参数值列表 | val1,val2,val3 | 多参数用元组存放 (val1,val2), (val3, val4) |
indirect | 设置成True,则把传进来的参数当函数执行,而不是一个参数 | / | / |
ids | 用例的ID | 字符串列表 | ids的长度需要与测试数据列表的长度一致 |
scope | 用于控制Fixture的作用范围 | / | 默认"function" |
7.2 使用方法
# -*- coding:utf-8 -*-
import pytest
@pytest.mark.parametrize("num, result", [("10 + 10", 20),
("30 - 10", 20),
("4 * 5", 20),
("40 / 2", 20)])
def test_case(num, result):
print(f"num:{num}")
print(f"result:{result}")
assert eval(num) == result
if __name__ == '__main__':
pytest.main(["-s", "test_pytest_parametrize.py"])
# 输出为:
# test_pytest_parametrize.py
# num:10 + 10
# result:20
# .
# num:30 - 10
# result:20
# .
# num:4 * 5
# result:20
# .
# num:40 / 2
# result:20
# .
8 总结及说明
除了以上提及的,pytest还有其他很多特性,或者很多第三方插件,这里不再赘述了,比如有;
作者:虫无涯