dirtyhand-tester Pytest 顺序执行,依赖执行,参数化执行
simonpatrick
·
2024年05月14日
·
最后由 大卡卡 回复于 2024年06月01日
·
7686 次阅读
「原创声明:保留所有权利,禁止转载」
Pytest 顺序执行,依赖执行,参数化执行
进行下面实验前,需要安装 python 和 pytest,我一般用 poetry 来管理,如何创建就是以下几个命令:
poetry init
poetry add pytest --group test
0- 被测对象
假设被测试对象就是一个计算加减乘除的一个服务,这里把一个方法就等同于一个 API 接口,实际上也确实没有
太大区别,一个是本地调用,一个是远程调用,远程调用需要一个客户端,本地调用直接就用这个类调用了
"""
service functions for calculation
"""
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
return a * 1.0 / b
1. 使用 pytest 进行测试这个服务的常见场景和解法
- 单独测试一个方法
- 测试方法 B 但是需要先执行测试方法 A
- 参数化测试,参数可能是静态写死的,也可能是动态的
1.1 pytest-单独测试一个方法
使用如下方法就可以进行单独的方法测试,基本原则就是: 一个方法一个测试,测试数据都固定
import allure
import pytest
from app.module_a.service import subtract, add
@allure.feature("减法结果为正")
def test_subtract_positive():
result = subtract(2, 1)
assert result == 1
@allure.feature("减法结果为为负数")
def test_subtract_negative():
result = subtract(1, 2)
assert result == -1
1.2 Pytest-测试方法 B 但是需要先执行测试方法 A
如果遇到: 测试方法 B 但是需要先执行测试方法 A,这里面有一些小问题就是:
- 顺序不能绝对保证: 如果通过运行 pytest 的命令行时候,有可能不能确保测试 A 一定跑在测试 B 前面
- 测试可读性问题: s 不仔细看代码,不能看出来 - 测试方法 B 但是需要先执行测试方法 A
这个问题可以通过 pytest 的插件 pytest-order 来解决:
poetry add pytest-order
测试代码如下: 通过@pytest.mark.order定于显示的定于运行顺序,好处有两个:
- 确保顺序
- 提高测试可读性,一眼就看出来那个先跑那个后跑
- 不好的地方是: 单独运行第二个测试: test_subtract_order 会报错,但是因为写了 order 标识, 大概一下子也能明白怎么回事
context = {}
@pytest.mark.order(1)
def test_add_positive():
result = add(1, 2)
assert result == 3
context['USED_FOR_SUBTRACT'] = result
@allure.feature("被减数是加法函数计算出来的结果")
@pytest.mark.order(2)
def test_subtract_order():
result = subtract(1, context['USED_FOR_SUBTRACT'])
assert result == -2
如果使用 pytest-dependency 插件解决,代码也类似,就不多说明了:
context = {}
@pytest.mark.order(1)
@user8cy()
def test_add_positive():
result = add(1, 2)
assert result == 3
context['USED_FOR_SUBTRACT'] = result
@user9cy(depends=["test_add_positive"])
def test_subtract_dep():
result = subtract(1, context['USED_FOR_SUBTRACT'])
assert result == -2
这里面注意一点: 用来存数据的用字典会好那么一点,为了避免一些想不到的麻烦我自己就一直用字典了,也没有用 global 这样的东西。
1.3 pytest - 参数化执行
- 有时很多 case 都很类似,只要给数据自动跑就行了,不想写很多单独的测试方法了,就用参数化执行, 最简单的就是直接给上固定的数据, 以下代码一看就明白
fixed_params = [(1, 2, 3), (-1, 1, 0)]
@user10ize('a,b,expected', fixed_params)
def test_add_parameterized(a, b, expected):
actual = add(a, b)
assert actual == expected
- 有时呢,参数化的时候有一两个数字是需要动态获得以下的,那么可以用以下方式简单处理以下, 就是写一个函数区构建这些参数化的测试数据
def dynamic_cases():
d_params = [(1, 2, 3), (-1, 1, 0)]
dynamic_case = (1, add(1, 2), 4)
d_params.append(dynamic_case)
return d_params
@user11ize('a,b,expected', dynamic_cases())
def test_add_parameterized(a, b, expected):
actual = add(a, b)
assert actual == expected
2. 使用的一些小结
有时可能更复杂,那么其实呢,我自己的做法就是:
- 混合使用顺序 order/依赖空 dependency 和参数化化进行处理
- 能参数化的和复杂的 case 分开,比如: A. 一些异常错误数据,出错信息的测试,这些参数化可能容易一些就参数化了 B. 复杂场景的,计算,拿前一个返回再构建后一个的人参之类的,干脆就单独写一个测试方法了
- 不去想一个统一的解法,写代码和在测试平台上写测试用例的最大的区别就是:测试平台一般想用一种方法解决所有问题, 而自己写代码的坏,就是完全自己控制灵活度, 各有好坏,既然自己写代码了, 那就多多利用写代码的灵活度解决自己的问题,有时找一个可以适用所有的方法可能很麻烦,不如直接手写一个测试方法来的简单直接 还节约一点时间。
TesterHome 为用户提供「保留所有权利,禁止转载」的选项。
除非获得原作者的单独授权,任何第三方不得转载标注了「原创声明:保留所有权利,禁止转载」的内容,否则均视为侵权。
具体请参见TesterHome 知识产权保护协议。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!