用 Python 为接口测试自动生成用例

基于属性的测试会产生大量的、随机的参数,特别适合为单元测试和接口测试生成测试用例

尽管早在 2006 年 haskell 语言就有了 QuickCheck 来进行“基于属性的测试 ”,但是目前来看这依然是一个比较小众的领域。参考资料有限,本文若有不足,欢迎指正。

目录

1. 基于表的测试

2. 基于属性的测试

3. 如何进行基于属性的测试

4. 在接口测试中自动生成用例


1. 基于表的测试

在过去的实践中,执行测试时通常需要明确的内容(Value):

  1. 条件

  2. 输入

  3. 结果

这些内容可以通过 “判定树” 或者 “判断表” 来表示,然后测试的执行过程变成了这样:

“给定输入 X,我们期望 Y”

可以称为 基于表的测试

在最初,这给了我们测试的方向,但是缺点也非常明显:

你要足够多的 "X->Y" 才能可能覆盖到隐蔽的 bug。

这里请大家思考几个问题:

  1. 你是否已经为每一个测试编写了足够多的用例,以至于你十分确定真的不需要再增加用例了

  2. 这么多的用例,你编写得是否开心?是否高效?是否愿意继续坚持?

如果以上问题的答案不是 yes,那么基于属性的测试就是你需要掌握的东西!

2. 基于属性的测试

基于属性的测试基于表的测试,最大的区别可以这样描述:

“给定输入 X 值,我们期望 Y 值”

vs

“给定输入 X 类,我们期望 Y 类”

于是利用工具生成大量的 X 类数据,进行测试,并验证结果是否 Y 类。

值得注意的是:

  1. 关注输入的类型,而不是输入的值

  2. 根据类型自动生成大量的、随机的输入值

换言之,它在自动的生成测试用例,虽然输入值是随机的,但是值类型符合规范要求

3. 如何进行基于属性的测试

在不同的语言中有不同的工具来实现,比如:

  • haskell 中的 QuickCheck

  • java 中的 quicktheories

  • python 中的 hypothesis

本文以 python 为例进行演示:

假设有 add 函数,接收两个类型整数参数,并返回它们的相加结果

def add(a: int, b: int) -> int:    pass

首先写出一个简单的测试用例​​​​​​​

def test_add():    assert 3 == add(1, 2)

正如前面所说,一个这样的用例,根本没信心覆盖全部的场景,例如:

  • 参数是 0

  • 参数是负数

  • 参数值特别大

  • 其他...

所以接下来怎么办?

改为基于属性的测试:​​​​​​​

import hypothesis.strategies as stfrom hypothesis import given
@given(st.integers(), st.integers())def test_add(a, b):    c = add(a, b)    print(f"{a=},{b=}, {c=}")    assert isinstance(c, int)    assert c == a + b

执行结果​​​​​​​

a=0,b=0, c=0a=0,b=0, c=0a=0,b=0, c=0a=16926,b=11053, c=27979a=0,b=0, c=0a=0,b=0, c=0a=21010,b=-2732672789497425072, c=-2732672789497404062a=0,b=0, c=0a=14554,b=-15956, c=-1402a=15597,b=0, c=15597a=15597,b=13, c=15610a=15597,b=0, c=15597a=15597,b=15597, c=31194a=28965,b=-36, c=28929a=113,b=-36, c=77a=28929,b=-36, c=28893a=28929,b=9356, c=38285a=9356,b=9356, c=18712a=10278,b=-62, c=10216a=-62,b=-62, c=-124a=1625,b=-68244995710046113596363052355575247332, c=-68244995710046113596363052355575245707a=1625,b=-13143, c=-11518a=1625,b=1625, c=3250a=-1940,b=-31868, c=-33808a=-7,b=1, c=-6a=-1,b=0, c=-1a=-1,b=0, c=-1a=13,b=7245, c=7258a=13,b=13, c=26a=-99,b=-18, c=-117a=-30172,b=66, c=-30106a=-30172,b=-16940, c=-47112a=-16940,b=-16940, c=-33880a=-1233214851,b=-5152, c=-1233220003a=-5152,b=-5152, c=-10304a=-16,b=-29706, c=-29722a=-29706,b=-29706, c=-59412a=-29706,b=116, c=-29590a=29696,b=90, c=29786a=29696,b=29696, c=59392a=-11446,b=-21185, c=-32631a=-21185,b=-21185, c=-42370a=-12,b=16437, c=16425a=-12,b=16437, c=16425a=3202,b=53, c=3255a=3202,b=3202, c=6404a=-98,b=3, c=-95a=82691970030325711417874227410289695610,b=1316378701, c=82691970030325711417874227411606074311a=82691970030325711417874227410289695610,b=82691970030325711417874227410289695610, c=165383940060651422835748454820579391220a=24100,b=14385, c=38485a=24100,b=14385, c=38485a=24100,b=24100, c=48200a=12293,b=-106, c=12187a=12293,b=27280, c=39573a=27280,b=27280, c=54560a=-18887,b=-5530, c=-24417a=-18887,b=-18887, c=-37774a=4738,b=122351151658095310625663643505383743930, c=122351151658095310625663643505383748668a=-24601163521689169516616964879873921492,b=565838202, c=-24601163521689169516616964879308083290a=4738,b=122351151658095310625663643281738736058, c=122351151658095310625663643281738740796a=-96098295006598318424285019062007505,b=25207226, c=-96098295006598318424285019036800279a=-96098295006598318424285019062007505,b=-1997122225172868107163535967078611096, c=-2093220520179466425587820986140618601a=-96098295006598318424285019062007505,b=-96098295006598318424285019062007505, c=-192196590013196636848570038124015010a=-36,b=1190, c=1154a=-36,b=-4, c=-40a=-36,b=-36, c=-72a=2047897602,b=-4641, c=2047892961a=2047897602,b=2047897602, c=4095795204a=-1307873608,b=11753, c=-1307861855a=-1307873608,b=-3308225400997338452, c=-3308225402305212060a=-1307873608,b=770256249, c=-537617359a=-1307873608,b=-1307873608, c=-2615747216a=-4715910568460396013,b=-18622, c=-4715910568460414635a=16754,b=-6053, c=10701a=-6053,b=-6053, c=-12106a=-6053,b=-6053, c=-12106a=-22264,b=44, c=-22220a=-22264,b=-22264, c=-44528a=-86,b=-86, c=-172a=-86,b=-86, c=-172a=1794,b=28170, c=29964a=1794,b=28170, c=29964a=-93,b=482, c=389a=-1,b=482, c=481a=-1,b=-1, c=-2

由结果可知,工具根据参数是整数这一规范,自动生成、执行了大量的测试用例

4. 在接口测试中自动生成用例

接口测试和函数的单元测试非常相似:

  • 输入数据

  • 返回数据

此外接口文档作为前后端、甚至测试开发的对接窗口,对参数的要求约定的更加细致,

以 OpenAPI 为例,每个参数可以有这些属性:

  • type:数字还是字符串?

  • format:密码还是电子邮箱?

  • maxLength:长度不超过多少?

  • required:是否必填?

  • in:参数通过什么传递?

  • 其他...

于是为接口生成符合要求的参数就变得非常可行,

再根据OpenAPI中关于响应结果的描述,可以生成断言

一批测试用例就这样自动完成了

举个例子:

 import unittest
import requests
from api_tools import APITestCase
class UserCase(APITestCase):    schema = "http://127.0.0.1:7600/openapi.json"
if __name__ == "__main__":    unittest.main()

这是以 unittest 为例进行封装的结果,只需要在 TestCase 中指定 openapi 的内容(或路径),

启动测试框架时,会自动读取、解析接口文档,并生成测试用例

下面是部分执行日志,可以看到:

对接口发送了随机参数,并获得返回值

INFO     2022-04-10 01:02:59,223 : 执行用例 login_login_access_token_postINFO     2022-04-10 01:02:59,223 : -----调用接口:login_login_access_token_post-----INFO     2022-04-10 01:02:59,223 : 发送请求>>>   :请求数据 = {'userin': {'password': 'bEYtwDZUxdBVThEFOqTz', 'email': 'KEfQnAtTNtwZOXRjoIjp'}}INFO     2022-04-10 01:02:59,578 : 接收响应   <<<:<Response [400]>INFO     2022-04-10 01:02:59,578 : -----调用完毕:login_login_access_token_post-----INFO     2022-04-10 01:02:59,578 : 执行用例 test_token_login_test_token_postINFO     2022-04-10 01:02:59,578 : -----调用接口:test_token_login_test_token_post-----INFO     2022-04-10 01:02:59,578 : 发送请求>>>   :请求数据 = {}INFO     2022-04-10 01:03:00,035 : 接收响应   <<<:<Response [200]>INFO     2022-04-10 01:03:00,035 : -----调用完毕:test_token_login_test_token_post-----INFO     2022-04-10 01:03:00,035 : 执行用例 sign_up_login_sign_up_postINFO     2022-04-10 01:03:00,035 : -----调用接口:sign_up_login_sign_up_post-----INFO     2022-04-10 01:03:00,035 : 发送请求>>>   :请求数据 = {'userin': {'password': 'GgzYcOwapTwnkkKVTraE', 'email': 'EJrXlCSNKKjdiVvAOnTM'}}INFO     2022-04-10 01:03:00,657 : 接收响应   <<<:<Response [200]>INFO     2022-04-10 01:03:00,657 : -----调用完毕:sign_up_login_sign_up_post-----INFO     2022-04-10 01:03:00,657 : 执行用例 todo_list_todo_getINFO     2022-04-10 01:03:00,657 : -----调用接口:todo_list_todo_get-----INFO     2022-04-10 01:03:00,657 : 发送请求>>>   :请求数据 = {}INFO     2022-04-10 01:03:01,233 : 接收响应   <<<:<Response [200]>INFO     2022-04-10 01:03:01,233 : -----调用完毕:todo_list_todo_get-----INFO     2022-04-10 01:03:01,233 : 执行用例 todo_post_todo_postINFO     2022-04-10 01:03:01,233 : -----调用接口:todo_post_todo_post-----INFO     2022-04-10 01:03:01,233 : 发送请求>>>   :请求数据 = {'todoin': {'title': '', 'is_done': False}}INFO     2022-04-10 01:03:01,594 : 接收响应   <<<:<Response [200]>INFO     2022-04-10 01:03:01,594 : -----调用完毕:todo_post_todo_post-----INFO     2022-04-10 01:03:01,594 : 执行用例 todo_delete_all_todo_deleteINFO     2022-04-10 01:03:01,594 : 执行用例 todo_get_todo__todo_id__getINFO     2022-04-10 01:03:01,594 : -----调用接口:todo_get_todo__todo_id__get-----INFO     2022-04-10 01:03:01,594 : 发送请求>>>   :请求数据 = {'todo_id': 2451}INFO     2022-04-10 01:03:02,026 : 接收响应   <<<:<Response [404]>INFO     2022-04-10 01:03:02,026 : -----调用完毕:todo_get_todo__todo_id__get-----INFO     2022-04-10 01:03:02,026 : 执行用例 todo_put_todo__todo_id__putINFO     2022-04-10 01:03:02,026 : -----调用接口:todo_put_todo__todo_id__put-----INFO     2022-04-10 01:03:02,026 : 发送请求>>>   :请求数据 = {'todo_id': 1519, 'todoin': {'title': '', 'is_done': False}}INFO     2022-04-10 01:03:02,423 : 接收响应   <<<:<Response [404]>INFO     2022-04-10 01:03:02,423 : -----调用完毕:todo_put_todo__todo_id__put-----INFO     2022-04-10 01:03:02,423 : 执行用例 todo_delete_todo__todo_id__delete


鉴于篇幅有限,暂时介绍这么多,

如果你对这个话题依旧有兴趣,可在公众号窗口中回复:PBT

原创不易,喜欢请星标+点赞+在看,关注本公众号,不错过技术干货,谢谢鼓励!


欢迎关注我的公众号“ 测试开发研习社”,专注Python开发及测试技术

  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Java可以使用正则表达式来匹配和生成测试数据。下面是一个简单的示例,展示了如何使用正则表达式来生成测试数据。 首先,我们需要导入Java的正则表达式包java.util.regex。然后,我们创建一个正则表达式模式,并将其编译为模式对象。 ```java import java.util.regex.Matcher; import java.util.regex.Pattern; public class TestDataGenerator { public static void main(String[] args) { String regex = "[a-z]{5}[0-9]{3}"; Pattern pattern = Pattern.compile(regex); // 生成10个测试数据 for (int i = 0; i < 10; i++) { String testData = generateTestData(pattern); System.out.println(testData); } } public static String generateTestData(Pattern pattern) { StringBuilder testData = new StringBuilder(); Matcher matcher = pattern.matcher(""); // 生成测试数据直到满足正则表达式 do { testData.setLength(0); // 清空数据 for (int i = 0; i < pattern.pattern().length(); i++) { char character = pattern.pattern().charAt(i); if (Character.isLetter(character)) { testData.append((char) (Math.random() * 26 + 97)); // 生成小写字母 } else if (Character.isDigit(character)) { testData.append((char) (Math.random() * 10 + 48)); // 生成数字 } else { testData.append(character); } } matcher.reset(testData.toString()); } while (!matcher.matches()); return testData.toString(); } } ``` 在这个示例中,我们定义了一个正则表达式模式`[a-z]{5}[0-9]{3}`,表示匹配一个由小写字母开头的5个字符长度的字符串,后跟3个数字。 然后,在`generateTestData()`方法中,我们首先创建一个空的StringBuilder对象和一个空的Matcher对象。然后,我们使用一个do-while循环,生成随机的测试数据,直到满足正则表达式。 循环体内部,我们遍历正则表达式模式中的每个字符。如果字符是字母,我们使用随机数生成一个小写字母;如果字符是数字,我们使用随机数生成一个数字;否则,我们将字符直接添加到测试数据中。 最后,我们使用Matcher对象的`matches()`方法来检查生成的测试数据是否与正则表达式匹配。如果不匹配,我们继续生成新的随机测试数据,直到满足正则表达式要求。 运行示例代码,将会生成满足正则表达式`[a-z]{5}[0-9]{3}`的10个测试数据,如下所示: ``` qmpow259 hdcrx102 apojg621 nlzxh871 bfksr682 ljmrs238 gnypa935 dnoyx916 rvesz653 vrymk789 ``` 希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值