功能測試用例自動生成算法 Pairwise
Pairwise算法是什么
對于以下測試場景:
- 瀏覽器:M,O,P
- 操作平臺:W(windows),L(linux),i(ios)
- 語言:C(chinese),E(english)
該如何設(shè)計功能測試用例呢?
根據(jù)數(shù)學(xué)統(tǒng)計分析,73%的缺陷(單因子是35%,雙因子是38%)是由單因子或2個因子相互作用產(chǎn)生的。19%的缺陷是由3個因子相互作用產(chǎn)生的。也就是說,大多數(shù)的bug都是條件的兩兩組合造成的。
Pairwise算法是L. L. Thurstone在1927年首先提出來的,他是美國的一位心理統(tǒng)計學(xué)家。Pairwise算法基于兩兩組合,過濾出性價比高的用例集。它的思路是:如果某一組用例的兩兩組合結(jié)果,在其他組合中均出現(xiàn),就刪除該組用例,從而精簡用例。
對于上述測試場景,可以通過笛卡爾積設(shè)計18條兩兩組合的測試用例:
- 1,M W C
- 2,M W E
- 3,M L C
- 4,M L E
- 5,M I C
- 6,M I E
- 7,O W C
- 8,O W E
- 9,O L C
- 10,O L E
- 11,O I C
- 12,O I E
- 13,P W C
- 14,P W E
- 15,P L C
- 16,P L E
- 17,P I C
- 18,P I E
對于第18條用例P I E來說,兩兩組合是PI ,PE ,IE,PI在17號,PE在16號,IE在12號出現(xiàn)過,所以第18條用例可以過濾掉。按照這個算法繼續(xù)過濾,最終剩下9條用例:
- 1,M W C
- 4,M L E
- 6,M I E
- 7,O W E
- 9,O L C
- 11,O I C
- 14,P W E
- 15,P L C
- 17,P I C
用例減少了50%!而且維度越多越明顯,當(dāng)有10個維度的時候4*4*4*4*3*3*3*2*2*2=55296個測試case,pairwise為24個,是原始測試用例規(guī)模的0.04%。
Python實現(xiàn)
源碼已上傳:https://github.com/dongfanger/python-tools/blob/main/pairwise.py
- #!/usr/bin/python
- # encoding=utf-8
- """
- @Author : Don
- @Date : 2021/11/03 20:34
- @Desc :
- """
- import copy
- import itertools
- from sys import stdout
- from loguru import logger
- def parewise(option):
- """pairwise算法"""
- cp = [] # 笛卡爾積
- s = [] # 兩兩拆分
- for x in eval('itertools.product' + str(tuple(option))):
- cp.append(x)
- s.append([i for i in itertools.combinations(x, 2)])
- logger.info('笛卡爾積:%s' % len(cp))
- del_row = []
- bar(0)
- s2 = copy.deepcopy(s)
- for i in range(len(s)): # 對每行用例進行匹配
- if (i % 100) == 0 or i == len(s) - 1:
- bar(int(100 * i / (len(s) - 1)))
- t = 0
- for j in range(len(s[i])): # 對每行用例的兩兩拆分進行判斷,是否出現(xiàn)在其他行
- flag = False
- for i2 in [x for x in range(len(s2)) if s2[x] != s[i]]: # 找同一列
- if s[i][j] == s2[i2][j]:
- t = t + 1
- flag = True
- break
- if not flag: # 同一列沒找到,不用找剩余列了
- break
- if t == len(s[i]):
- del_row.append(i)
- s2.remove(s[i])
- res = [cp[i] for i in range(len(cp)) if i not in del_row]
- logger.info('過濾后:%s' % len(res))
- return res
- def bar(i):
- """進度條"""
- c = int(i / 10)
- jd = '\r %2d%% [%s%s]'
- a = '■' * c
- b = '□' * (10 - c)
- msg = jd % (i, a, b)
- stdout.write(msg)
- stdout.flush()
- if __name__ == '__main__':
- pl = [['M', 'O', 'P'], ['W', 'L', 'I'], ['C', 'E']]
- a = parewise(pl)
- print()
- for i in a:
- print(i)
輸出結(jié)果:
- 100% [■■■■■■■■■■]
- ('M', 'W', 'E')
- ('M', 'L', 'E')
- ('M', 'I', 'C')
- ('O', 'W', 'E')
- ('O', 'L', 'E')
- ('O', 'I', 'C')
- ('P', 'W', 'C')
- ('P', 'L', 'C')
- ('P', 'I', 'E')
- 2021-11-07 11:38:56.850 | INFO | __main__:parewise:24 - 笛卡爾積:18
- 2021-11-07 11:38:56.850 | INFO | __main__:parewise:45 - 過濾后:9
參考資料:
https://blog.csdn.net/ztf312/article/details/78792906
https://www.cnblogs.com/df888/p/11747616.html