基于属性的测试会产生大量的、随机的参数,特别适合为单元测试和接口测试生成测试用例 尽管早在2006年haskell语言就有了QuickCheck来进行基于属性的测试,但是目前来看这依然是一个比较小众的领域,参考资料有限,本文如有不足,欢迎指正。1。基于表的测试 在过去的测试实践中,执行测试时通常需要明确的内容(Value):条件输入结果 这些内容可以通过判定树或者判断表来表示,然后测试的执行过程变成了这样 给定输入X,我们期望Y 可以称为基于表的测试 在最初,这给了我们测试的方向,但是缺点也非常明显: 你要足够多的XY才能可能覆盖到隐蔽的bug。 这里请大家回答几个问题: 你是否已经为每一个测试编写了足够多的用例,以至于你十分确定真的不需要再增加用例了这么多的用例,你编写的是否开心?是否高效?是否愿意继续坚持? 如果以上问题的答案不是yes,那么基于属性的测试就是你需要掌握的东西!2。基于属性的测试 基于属性的测试和基于表的测试,最大的区别可以这样描述: 给定输入X值,我们期望Y值 vs 给定输入X类,我们期望Y类 于是利用工具生成大量的X类数据,进行测试,并验证结果是否Y类。 值得注意的是:关注输入的类型,而不是输入的值根据类型自动生成大量的、随机的输入值 换言之,它在自动的生成测试用例,虽然输入值是随机的,但是值得类型符合规范要求3。如何进行基于属性的测试 在不同的语言中有不同的工具来实现,比如:haskell中的QuickCheck、java中的quicktheoriespython中的hypothesis 本文以python为例进行演示: 假设有add函数,接收两个类型整数参数,并返回它们的相加结果defadd(a:int,b:int)int:pass 首先写出一个简单的测试用例deftestadd():assert3add(1,2) 正如前面所说,一个这样的用例,根本没信心覆盖全部的场景,例如:参数是0参数是负数参数值特别大其他。。。 所以接下来怎么办? 改为基于属性的测试importhypothesis。strategiesasstfromhypothesisimportgivengiven(st。integers(),st。integers())deftestadd(a,b):cadd(a,b)print(f{a},{b},{c})assertisinstance(c,int)assertcab 执行结果a0,b0,c0a0,b0,c0a0,b0,c0a16926,b11053,c27979a0,b0,c0a0,b0,c0a21010,b2732672789497425072,c2732672789497404062a0,b0,c0a14554,b15956,c1402a15597,b0,c15597a15597,b13,c15610a15597,b0,c15597a15597,b15597,c31194a28965,b36,c28929a113,b36,c77a28929,b36,c28893a28929,b9356,c38285a9356,b9356,c18712a10278,b62,c10216a62,b62,c124a1625,b68244995710046113596363052355575247332,c68244995710046113596363052355575245707a1625,b13143,c11518a1625,b1625,c3250a1940,b31868,c33808a7,b1,c6a1,b0,c1a1,b0,c1a13,b7245,c7258a13,b13,c26a99,b18,c117a30172,b66,c30106a30172,b16940,c47112a16940,b16940,c33880a1233214851,b5152,c1233220003a5152,b5152,c10304a16,b29706,c29722a29706,b29706,c59412a29706,b116,c29590a29696,b90,c29786a29696,b29696,c59392a11446,b21185,c32631a21185,b21185,c42370a12,b16437,c16425a12,b16437,c16425a3202,b53,c3255a3202,b3202,c6404a98,b3,c95a82691970030325711417874227410289695610,b1316378701,c82691970030325711417874227411606074311a82691970030325711417874227410289695610,b82691970030325711417874227410289695610,c165383940060651422835748454820579391220a24100,b14385,c38485a24100,b14385,c38485a24100,b24100,c48200a12293,b106,c12187a12293,b27280,c39573a27280,b27280,c54560a18887,b5530,c24417a18887,b18887,c37774a4738,b122351151658095310625663643505383743930,c122351151658095310625663643505383748668a24601163521689169516616964879873921492,b565838202,c24601163521689169516616964879308083290a4738,b122351151658095310625663643281738736058,c122351151658095310625663643281738740796a96098295006598318424285019062007505,b25207226,c96098295006598318424285019036800279a96098295006598318424285019062007505,b1997122225172868107163535967078611096,c2093220520179466425587820986140618601a96098295006598318424285019062007505,b96098295006598318424285019062007505,c192196590013196636848570038124015010a36,b1190,c1154a36,b4,c40a36,b36,c72a2047897602,b4641,c2047892961a2047897602,b2047897602,c4095795204a1307873608,b11753,c1307861855a1307873608,b3308225400997338452,c3308225402305212060a1307873608,b770256249,c537617359a1307873608,b1307873608,c2615747216a4715910568460396013,b18622,c4715910568460414635a16754,b6053,c10701a6053,b6053,c12106a6053,b6053,c12106a22264,b44,c22220a22264,b22264,c44528a86,b86,c172a86,b86,c172a1794,b28170,c29964a1794,b28170,c29964a93,b482,c389a1,b482,c481a1,b1,c2 由结果可知,工具根据参数是整数这一规范,自动生成、执行了大量的测试用例4。在接口测试中自动生成用例 接口测试和函数的单元测试非常相似:输入数据返回数据 此外接口文档作为前后端、甚至测试开发的对接窗口,对参数的要求约定的更加细致, 以OpenAPI为例,每个参数可以有以下属性:type:数字还是字符串?format:密码还是电子邮箱?maxLength:长度不超过多少?required:是否必填?in:参数通过什么传递?其他。。。 于是为接口生成符合要求的参数就变得可行了,举个例子:importloggingimportunittestimportrequestsfromapitoolsimportAPITestCaseclassUserCase(APITestCase):schemahttp:127。0。0。1:7600openapi。jsonifnamemain:unittest。main() 这是以unittest为例进行封装的结果,只需要在TestCase中指定openapi的内容(或路径), 启动测试框架时,会自动读取、解析接口文档,并生成测试用例 下面是部分执行日志,可以看到对接口发送了随机参数,并获得返回值INFO2022041001:02:59,223:执行用例loginloginaccesstokenpostINFO2022041001:02:59,223:调用接口:loginloginaccesstokenpostINFO2022041001:02:59,223:发送请求:请求数据{userin:{password:bEYtwDZUxdBVThEFOqTz,email:KEfQnAtTNtwZOXRjoIjp}}INFO2022041001:02:59,578:接收响应:Response〔400〕INFO2022041001:02:59,578:调用完毕:loginloginaccesstokenpostINFO2022041001:02:59,578:执行用例testtokenlogintesttokenpostINFO2022041001:02:59,578:调用接口:testtokenlogintesttokenpostINFO2022041001:02:59,578:发送请求:请求数据{}INFO2022041001:03:00,035:接收响应:Response〔200〕INFO2022041001:03:00,035:调用完毕:testtokenlogintesttokenpostINFO2022041001:03:00,035:执行用例signuploginsignuppostINFO2022041001:03:00,035:调用接口:signuploginsignuppostINFO2022041001:03:00,035:发送请求:请求数据{userin:{password:GgzYcOwapTwnkkKVTraE,email:EJrXlCSNKKjdiVvAOnTM}}INFO2022041001:03:00,657:接收响应:Response〔200〕INFO2022041001:03:00,657:调用完毕:signuploginsignuppostINFO2022041001:03:00,657:执行用例todolisttodogetINFO2022041001:03:00,657:调用接口:todolisttodogetINFO2022041001:03:00,657:发送请求:请求数据{}INFO2022041001:03:01,233:接收响应:Response〔200〕INFO2022041001:03:01,233:调用完毕:todolisttodogetINFO2022041001:03:01,233:执行用例todoposttodopostINFO2022041001:03:01,233:调用接口:todoposttodopostINFO2022041001:03:01,233:发送请求:请求数据{todoin:{title:,isdone:False}}INFO2022041001:03:01,594:接收响应:Response〔200〕INFO2022041001:03:01,594:调用完毕:todoposttodopostINFO2022041001:03:01,594:执行用例tododeletealltododeleteINFO2022041001:03:01,594:执行用例todogettodotodoidgetINFO2022041001:03:01,594:调用接口:todogettodotodoidgetINFO2022041001:03:01,594:发送请求:请求数据{todoid:2451}INFO2022041001:03:02,026:接收响应:Response〔404〕INFO2022041001:03:02,026:调用完毕:todogettodotodoidgetINFO2022041001:03:02,026:执行用例todoputtodotodoidputINFO2022041001:03:02,026:调用接口:todoputtodotodoidputINFO2022041001:03:02,026:发送请求:请求数据{todoid:1519,todoin:{title:,isdone:False}}INFO2022041001:03:02,423:接收响应:Response〔404〕INFO2022041001:03:02,423:调用完毕:todoputtodotodoidputINFO2022041001:03:02,423:执行用例 文章来自https:www。cnblogs。comdongfangtianyupapitestbypbt。html