该文章为测试学习笔记
- 自动化测试前:提前准备好数据,测试完成后,自动清理脏数据,有没有更好的框架?
- 自动化测试中,需要使用多套测试数据实现用例的参数化,有没有更便捷的方式?
- 自动化测试后,需要自动生成优雅、简洁的测试报告,有没有更好的生成方法?
Pytest
- 支持简单的单元测试和复杂的功能测试;
- 结合Requests实现接口测试;结合Selenium、Appium实现自动化功能测试;
- 使用pytest结合Allure集成到Jenkins中可以实现持续集成;
- 支持315种以上插件;
- 兼容unittest
- 定制化插件开发
一个简单的test :用assert来确定是否符合
def inc(x):
return x + 1
def test_answer():
assert inc(3) == 5
Pytest环境安装
- 前提:Python环境 > 3.6 有对应pip
- 第一种方式 pip install pytest
- 第二种方式 PyCharm直接安装
可能出错的地方
- 找不到pip python路径有错误或没有存入 +"\Scripts"的地址
运行第一个脚本
- 进入py文件存储的文件夹下
- 输入pytest [名字]
pytest demo1.py
- 错误情况下
- 正确情况下
Pytest格式要求
- 文件名
- 类名
- 方法/函数名(类内用def定义的为方法,在类外的为函数)
| 类型 | 规则 |
|---|---|
| 文件 | test_开头或者_test结尾 |
| 类 | Test开头 |
| 方法/函数 | test_开头 |
| 包 | 无要求 |
注意:测试类中不可以_init_构造函数, 添加该函数后系统就不认为其为测试类了,不会运行方法了
Pycharm 配置与界面化运行
Pycharm默认测试执行器为Pytest
- 创建一个项目 同时选择一个虚拟环境
- 点击图示加号
- 搜索pytest,下载(左下角)
- 重新进入Setting界面(前几步下载好的pytest才会被识别到,避免报错),进入Tools->Python Intergrated Tools
5. 选择Default test runner 为pytest
用例结构
- 用例名称
- 用例步骤
- 用例断言
示例
class TestXXX:
def setup(self):
# 资源准备
pass
def teardown(self):
# 资源销毁 关闭页面,关闭数据库连接等等
pass
def test_XXX(self):
# 测试步骤1
# 测试步骤2
# 断言 实际结果 对比 预期结果
assert ActualResult == ExpectedResult
断言Assert
通过 or AssertionError异常
写法
Pytest测试框架结构(setup/teardown)
| 类型 | 规则 |
|---|---|
| setup_module/teardown_module | 全局模块级 |
| setup_class/teardown_class | 类级,只在类中前后运行一次 |
| setup_function/teardown_function | 函数级,在类外 |
| setup_method/teardown_method | 方法级,类中的每个方法执行前后 |
| setup/teardown | 在类中,运行在调用方法的前后 |
运行某个模块
# 模块级别 只被调用一次
def setup_module():
print("资源准备:setup module")
def teardown_module():
print("资源准备:teardown module")
def test_case1():
print("case1")
def test_case2():
print("case2")
def setup_function():
print("资源准备:setup function")
def teardown_function():
print("资源准备:teardown function")
下图中左上角的两个按钮需要勾选,才能显示完整信息
控制台信息显示如下
资源准备:setup module
资源准备:setup function
PASSED [ 50%]case1
资源准备:teardown function
test_first.py::test_case2 资源准备:setup function
PASSED [100%]case2
资源准备:teardown function
资源准备:teardown module
============================== 2 passed in 0.02s ==============================
Process finished with exit code 0
运行单个方法
加入以下代码, 点击 三角 运行
class TestDemo:
# 执行类 前后分别执行setup_class teardown_class
def setup_class(self):
print("TestDemo setup_class")
def teardown_class(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")
参数化
参数化设计方法是将模型中的定量信息 变量化,使之成为任意调整的参数。对于变量化参数赋予不同数值,可得到不同大小和形状的零件模型。
Mark:参数化测试函数使用
- 单参数
- 多参数
- 用例重命名
- 笛卡尔积
最底层的对勾数量为用例个数
eval的使用方法
出错时:
- 例子1
import pytest
search_list = ['appium', 'selenium', 'pytest']
@pytest.mark.parametrize('name', search_list)
def test_search(name):
assert name in search_list
2. 例子2
import pytest
@pytest.mark.parametrize("test_input, expected", [
("3+5", 8), ("2+5", 7), ("7+5", 12)
])
def test_search(test_input, expected):
assert eval(test_input) == expected
3. ids的作用
import pytest
# ids的个数 == 传递的数据个数
@pytest.mark.parametrize("test_input, expected", [
("3+5", 8), ("2+5", 7), ("7+5", 12)
], ids=["number1", "number2", "number3"])
def test_search(test_input, expected):
assert eval(test_input) == expected
- 笛卡尔积
import pytest
# ids的个数 == 传递的数据个数
@pytest.mark.parametrize("test_a", ["a1", "a2", "a3"])
@pytest.mark.parametrize("test_b", ["b1", "b2", "b3", "b4"])
def test_search(test_a, test_b):
print(f"test_a {test_a}, test_b {test_b}")
# PASSED [ 8%]test_a a1, test_b b1
# PASSED [ 16%]test_a a2, test_b b1
# PASSED [ 25%]test_a a3, test_b b1
# PASSED [ 33%]test_a a1, test_b b2
# PASSED [ 41%]test_a a2, test_b b2
# PASSED [ 50%]test_a a3, test_b b2
# PASSED [ 58%]test_a a1, test_b b3
# PASSED [ 66%]test_a a2, test_b b3
# PASSED [ 75%]test_a a3, test_b b3
# PASSED [ 83%]test_a a1, test_b b4
# PASSED [ 91%]test_a a2, test_b b4
# PASSED [100%]test_a a3, test_b b4
内部逻辑代码:点击.parametrize查看
Pytest 标记测试用例
import pytest
def double(a):
return a * 2
# 测试数据:整型
@pytest.mark.int
def test_double_int():
print("test double int")
assert 2 == double(1)
# 测试数据:负数
@pytest.mark.minus
def test_double_minus():
print("test double minus")
assert -2 == double(-1)
# 测试数据:浮点数
# 名字相同时,使两个两个同时进行
@pytest.mark.float
def test_double_float():
assert 0.2 == double(0.1)
@pytest.mark.float
def test_double2_minus():
assert -0.2 == double(-0.1)
@pytest.mark.zero
def test_double_0():
assert 0 == double(0)
@pytest.mark.bignum
def test_double_bignum():
assert 200 == double(100)
@pytest.mark.str
def test_double_str():
assert 'aa' == double('a')
def test_double_str1():
assert 'a$a$' == double('a$')
1.
- 输入
# pytest 文件名字.后缀 -vs -m "某一个标签,例如str add等 需要双引号括住"
pytest test_first.py -vs -m "str"
3. 消除warning
-
查看warning,发现还有一个警告,再查看上方的文字,发现是注释的zero未添加上去
-
警告减少了,再次运行 显示总共8个数据,7个未被选中,1个被选中
Mark:跳过(Skip)及预期失败(xFail)
pytest内置标签,可以处理一些特殊or不能成功运行(某功能未实现,先不检测)的测试用例
Skip
共两种解决方案
添加装饰器
- @pytest.mark.skip
import pytest
@pytest.mark.skip
def test_aaa():
print("start")
assert True
def test_bbb():
print("end")
assert True
执行结果如上图所示
添加reson后:
import pytest
@pytest.mark.skip(reason="存在bug")
def test_aaa():
print("start")
assert True
def test_bbb():
print("end")
assert True
- @pytest.mark.skipif
因为运算的电脑是win32,所以第二个用例不执行
import pytest
import sys
print(sys.platform)
@pytest.mark.skipif(sys.platform == 'darwin', reason="does not run on mac")
def test_case1():
assert True
@pytest.mark.skipif(sys.platform.__contains__('win') , reason="does not run on windows")
def test_case2():
assert True
@pytest.mark.skipif(sys.version_info < (3, 6), reason='requires python3.6 or higher')
def test_case3():
assert True
代码中添加跳过代码
import pytest
def login():
return True
def test_login():
print("start")
# 符合,则跳过下列步骤
if not login():
pytest.skip("no login")
print("end")
点击左侧三角,执行,如下图
控制台输出如下图
- 将方法中Ture改为False,执行
import pytest
def login():
return False
def test_login():
print("start")
# 符合,则跳过下列步骤
if not login():
pytest.skip("no login")
print("end")
后续不执行
xfail
使用场景:预期结果为fail
@pytest.mark.xfail
import pytest
@pytest.mark.xfail
def test_case1():
assert 1 == 2
xfail = pytest.mark.xfail
@xfail(reason="bug 110")
def test_case2():
assert 1 == 1
运行用例
- 方法
- 类
- 多个类 多选即可
- 模块
两种方法:
- 可视化界面操作
- 命令
pytest # ls下所有的可执行文件都执行
pytest test_first.py # 执行某一个文件
pytest test_first.py::test_case1 # 执行某个类或者方法pytest test_first.py::test_case1 -v # 查看具体哪一条被执行了
pytest test_first::class::test_case1 # 执行某个类中的某个用例
pytest -v # 执行当前目录下的所有目录
运行结果
- 常见:fail失败、error代码不符合规范,需要修改后才能执行、pass通过,一致
- 特殊:
- warning警告,提示作用
- deselect未被选中