前言
工作过程中,我们接触到需求后第一要务是 熟悉需求并且输出测试用例,针对接口测试的入参测试,需要校验大量入参的组合场景,这时我们通常采用正交法来设计测试用例,在减少测试用例的数量时,同时保障测试用例的有效覆盖性。
正交法实验
正交试验法是分析多因素、多水准的一种实验法,它是借助正交表来对实验进行设计,依据少数的实验取代全面实验
在一项实验中,把影响实验结果的量称之为实验因素(因子),简称因素。因素可以理解为实验过程中的自变量,实验结果可以看成因素的函数。在实验过程中,每一个因素可以处于不同的状态或状况,把因素所处的状态或状况,称之为因素的水准,简称水准。
举个例子:
某所大学通信系共2个班级,刚考完某一门课程,想依据“性别”、“班级”和“成绩”这三个查询条件对通信系这门课程的成绩分布,男女比例或班级比例进行人员查询:
依据“性别”=“男,女”进行查询
依据“班级”=“1班,2班”查询
依据“成绩”=“及格,不及格”查询
按照传统设计——全部检测
分析上述检测需求,有3个被测元素,被测元素我们称之为因素,每个因素有两个取值,我们称之为水准值(也就是2)。
如果是普通的全面检测,则如下(2^3=8次)
如果是正交法,则如下(2^2=4次)
如果入参数量更多正交法的收益就越大
自动生成正交用例
手动将入参情况列出来总归是繁琐的,我们可以通过正交表来自动输出相关组合场景,正交表规则取自
http://support.sas.com/techsup/technote/ts723_Designs.txt
# encoding: utf-8
from itertools import groupby
from collections import OrderedDict
def dataSplit(data):
ds = []
mb = [sum([k for m, k in data['mk'] if m <= 10]), sum([k for m, k in data['mk'] if m > 10])]
for i in data['data']:
if mb[1] == 0:
ds.append([int(d) for d in i])
elif mb[0] == 0:
ds.append([int(i[n * 2:(n + 1) * 2]) for n in range(mb[1])])
else:
part_1 = [int(j) for j in i[:mb[0]]]
part_2 = [int(i[mb[0]:][n * 2:(n + 1) * 2]) for n in range(mb[1])]
ds.append(part_1 + part_2)
return ds
class OAT(object):
def __init__(self, OAFile='./ts723_Designs.txt'):
"""
初始化解析构造正交表对象,数据来源:http://support.sas.com/techsup/technote/ts723_Designs.txt
"""
self.data = {}
# 解析正交表文件数据
with open(OAFile, ) as f:
# 定义临时变量
key = ''
value = []
pos = 0
for i in f:
i = i.strip()
if 'n=' in i:
if key and value:
self.data[key] = dict(pos=pos,
n=int(key.split('n=')[1].strip()),
mk=[[int(mk.split('^')[0]), int(mk.split('^')[1])] for mk in
key.split('n=')[0].strip().split(' ')],
data=value)
key = ' '.join([k for k in i.split(' ') if k])
value = []
pos += 1
elif i:
value.append(i)
self.data[key] = dict(pos=pos,
n=int(key.split('n=')[1].strip()),
mk=[[int(mk.split('^')[0]), int(mk.split('^')[1])] for mk in
key.split('n=')[0].strip().split(' ')],
data=value)
self.data = sorted(self.data.items(), key=lambda i: i[1]['pos'])
def get(self, mk):
"""
传入参数:mk列表,如[(2,3)],[(5,5),(2,1)]
1. 计算m,n,k
m=max(m1,m2,m3,…)
k=(k1+k2+k3+…)
n=k1*(m1-1)+k2*(m2-1)+…kx*x-1)+1
2. 查询正交表
这里简单处理,只返回满足>=m,n,k条件的n最小数据,未做复杂的数组包含校验
"""
mk = sorted(mk, key=lambda i: i[0])
m = max([i[0] for i in mk])
k = sum([i[1] for i in mk])
n = sum([i[1] * (i[0] - 1) for i in mk]) + 1
query_key = ' '.join(['^'.join([str(j) for j in i]) for i in mk])
for data in self.data:
# 先查询是否有完全匹配的正交表数据
if query_key in data[0]:
return dataSplit(data[1])
# 否则返回满足>=m,n,k条件的n最小数据
elif data[1]['n'] >= n and data[1]['mk'][0][0] >= m and data[1]['mk'][0][1] >= k:
return dataSplit(data[1])
# 无结果
return None
def genSets(self, params, mode=0, num=1):
"""
传入测试参数OrderedDict,调用正交表生成测试集
mode:用例裁剪模式,取值0,1
0 宽松模式,只裁剪重复测试集
1 严格模式,除裁剪重复测试集外,还裁剪含None测试集(num为允许None测试集最大数目)
"""
sets = []
# 根据因素水平数量进行排序
params = OrderedDict(sorted(params.items(), key=lambda x: len(x[1])))
mk = [(k, len(list(v))) for k, v in groupby(params.items(), key=lambda x: len(x[1]))]
data = self.get(mk)
for d in data:
# 根据正则表结果生成测试集
q = OrderedDict()
for index, (k, v) in zip(d, params.items()):
try:
q[k] = v[index]
except IndexError:
# 参数取值超出范围时,取None
q[k] = None
if q not in sets:
if mode == 0:
sets.append(q)
elif mode == 1 and (len(list(filter(lambda v: v is None, q.values())))) <= num:
# 测试集裁剪,去除重复及含None测试集
sets.append(q)
return sets
生成上述测试用例
if __name__ == "__main__":
oat = OAT()
case1 = OrderedDict([
("性别", ["男 ", "女"]),
("班级", ["1班", "2班"]),
("成绩", ["及格","不及格"])
])
print(json.dumps(oat.genSets(case1), ensure_ascii=False))
结果如下
这样就可以自动生成正交用例了
示例代码参考:https://github.com/lovesoo/OrthogonalArrayTest