自动化测试的技术发展方向

2557 篇文章 2 订阅
2394 篇文章 14 订阅

新一代自动化测试:图文识别

传统的 UI 层自动化测试,一般依靠文档结构和属性来查找、定位页面元素。这种方式带来问题是,脚本的可靠性比较弱,相应的编写成本又很高,因此 UI 层自动化一直被很多测试人诟病,认为它是 ROI 非常低的一项活动。而图文识别技术则给 UI 层自动化测试带来了一丝新的曙光。

其实早在 2010 年之前,就已经有一些支持图像比对的自动化测试工具出现。只不过当时的比对方式大多基于全像素的比较,缺乏相关算法的支持,以致它在宽容度上的表现,并不比以往的元素识别方式好多少,加上其缺乏页面交互的能力,当时也就没有引起太大的反响。

而后随着 OCR 技术的进步,图文识别在自动化测试上的应用重新得到重视。其大致应用过程是:获取当前屏幕截图 -> 通过 OCR 得到目标的座标 -> 通过自动化框架进行操作 -> 获取新的屏幕截图。因为不依赖屏幕尺寸和元素样式,所以 OCR 能够较为完美地解决宽容度的问题。

我们常用的 Python 就有一些免费 OCR 库可供使用,比如 tesseract 和 easyocr,网上相关学习资料很多,入门门槛还是比较低的。但是它们对中文字符的识别不太理想,如果在这方面有更高的要求,也可以考虑商业产品。以下我们就以百度的首页为例,演示如何利用 python + easyocr 找到“百度一下”的按钮座标:

import easyocr
reader = easyocr.Reader(["ch_sim"])
result = reader.readtext("/Users/xxxx/baidu.png")
for tup in result:
     if tup[1] == "百度一下":
          print(tup[0])

OCR 虽然能够应对大部分场景,但在某些方面仍然力不从心(比如目标不含字符)。于是在此之上,我们可以进一步引入图像处理技术。相较古老的静态比较方式,新的图形算法能够计算模板与目标的相似度,找到匹配度最高的位置,这极大提升了自动化脚本在不同环境下的稳定性。

同样的,Python 也有这方面的库可以支持,比如 opencv-python 就是一个不错的选择。我们仍然以百度的首页为例,演示如何以图片的形式找到(方框圈出)“百度一下”按钮的位置,为了模拟样式上可能的差异,模板图片事先做了少许拉伸:

import cv2
source = cv2.imread("/Users/xxxx/baidu.png")
template = cv2.imread("/Users/xxxx/template.png")
height, width = template.shape[:2]
result = cv2.matchTemplate(source, template, cv2.TM_CCORR_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
target = source.copy()
top_left = max_loc
bottom_right = (top_left[0] + width, top_left[1] + height)
cv2.rectangle(target, top_left, bottom_right, 255, 2)
cv2.imwrite("/Users/xxxx/Desktop/target.png", target)

 图文识别技术比传统的识别方式有两项进步:第一是能够解决一些图像层的自动化问题(比如随机验证码);第二是提升了界面变动情况下(比如元素显示位置改变)的脚本稳定性。但本质上它还是通过元素的精确识别和定位来实现,只不过识别方式由文档特征转为了图文,那么有没可能对元素的描述不需要这么精确呢?

下一代自动化测试:自然语言处理

我们再次回顾第一篇里说的,自动化最重要的突破,都是在解决成本问题。图文识别技术更为优秀的原因是:它对元素的描述比以往更加“泛化”。比如原本查找一个 button 的描述是 “id=submit”,现在只要通过 "提交" 两个字就能找到,因而自动化的编写成本和后期维护成本均能有明显下降。

遵循这个思路,我们可以进一步寻找这类“泛化”的可能性。比如对元素的描述变成“表单里的按钮”或者“蓝色按钮”,那么自动化脚本甚至都可以不再通过代码来完成,而是基于自然语言来实现。这看起来与早前的 BDD 有些类似,只不过 BDD 的初衷是解决易用性的问题,并未考虑到成本问题。

自然语言处理其实并不算是新鲜事物,早几年的 NLP 技术在常规情况下,已经能够识别大部分的测试场景。以 python + NLTK(NLTK 没有官方的中文语料库,所以代码中用了英文)为例,我们可以从一句话中提取出动作和对象,再通过一个脚本转译器转换成所需要的测试脚本,例如:

import nltk

def nltk_analyze(sentence):
    tokens = nltk.word_tokenize(sentence)
    tagged_words = nltk.pos_tag(tokens)
    action = None
    element = []
    for word, tag in tagged_words:
        if "VB" in tag:
            action = word
            element = [word for word, tag in tagged_words[tagged_words.index((word, tag)) + 1:] if "NN" in tag]
            break
    return action, element

# 输出:('click', ["'百度一下", 'button'])
print(nltk_analyze("click the '百度一下' button"))

至于对象的泛化描述,可以结合 easyocr 和 opencv 来实现。比如接着前一节的例子,我们利用 K 均值聚类算法来获得“百度一下”区块的主体颜色(无文本的区域识别涉及图像形态的处理,我们放到下一节一起讲),即可用于判断是否在我们预期的色值范围内(比如是不是一个蓝色按钮),例如:

element = cv2.cvtColor(target[max_loc[1]:max_loc[1] + height, max_loc[0]:max_loc[0] + width], cv2.COLOR_BGR2HSV)
kmeans = KMeans(n_clusters=1)
kmeans.fit(element.reshape(-1, 3))

# 输出 (84.03025568181818, 117.52163825757576, 240.0319128787879, 0.0)
print(cv2.mean(element))

如今大语言模型的突破,给基于自然语言描述的自动化测试带来了新的可能。理论上我们在不远的将来,完全可以以极低的成本和极高的准确率获得 UI 层的自动化测试能力。从文档定位到图文识别,再到自然语言处理,我们对测试对象的泛化似乎已经走到了尽头,那么未来的自动化测试又会是什么样?

智能时代的自动化测试:脚本自动生成

在全民 AI 的时代,测试人同样也在探索智能化能够给我们带来什么。当前大部分团队的研究方向都集中在测试用例的自动生成上,我们团队也做过一些相关的可行性调研。然而由于近来众所周知的一些变动,我们没有资源能够继续投入,因此不得不暂时中止在这方面的探索。那么这一节就来介绍我们在这方面的一些理论研究结果。

继续上面的思路,我们还是要把测试目标做进一步的泛化,比如对于一个页面,是否能用某种方式来自动识别出其中的全部区块(包括文字)?这就涉及到图像形态学的算法了。前面例子中的百度首页,页面元素相对较少,为了更好地说明问题,这次我们把示例换成阿里云的首页:

图片

要得到这张图片上的所有元素区块,需要用到这么几个步骤:灰度处理、高斯模糊、梯度计算、二值化、闭运算、边缘计算。还有一些暂时没有用到的形态学算法,比如开运算、腐蚀、膨胀等,感兴趣的同学可以自行了解。上述步骤的相关代码和处理结果如下:

import cv2
import numpy as np
source = cv2.imread("/Users/xxxx/aliyun.png")
gray = cv2.cvtColor(source, cv2.COLOR_BGR2GRAY)
cv2.imwrite("/Users/xxxx/Desktop/gray.png", gray)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
cv2.imwrite("/Users/xxxx/Desktop/blurred.png", blurred)
gradient = cv2.morphologyEx(blurred, cv2.MORPH_GRADIENT, np.ones((3, 3), np.uint8))
cv2.imwrite("/Users/xxxx/Desktop/gradient.png", gradient)
_, binary = cv2.threshold(gradient, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)))
cv2.imwrite("/Users/xxxx/Desktop/closed.png", closed)
contours, _ = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
target = cv2.drawContours(source.copy(), contours, -1, (0, 255, 0), 2)
cv2.imwrite("/Users/xxxx/Desktop/target.png", target)

图片

从图中可以看到,我们已经能够准确获得各个区域的边缘点阵,下一步就是将它转换成自动化测试脚本需要的矩形区域。续接上面的代码,计算出边缘之后,通过 minAreaRect 得到最小外接矩形座标(即代码中的 box 变量,为了使区域更连贯,梯度的参数我做了调整,小伙伴们可以自行尝试):

target2 = source.copy()
for contour in contours:
    rect = cv2.minAreaRect(contour)
    box = np.intp(cv2.boxPoints(rect))
    cv2.drawContours(target2, [box], 0, (0, 0, 255), 3)
cv2.drawContours(target2, contours, -1, (255, 0, 0), 1)
cv2.imwrite("/Users/xxxx/Desktop/target2.png", target2)

图片

至此我们拥有了自动获取页面所有可点击区域的能力,脚本自动生成的基础已经具备,接下来要考虑的就是如何来利用这些“未知”区块。

大伙应该都知道 Monkey 测试,通过产生随机的点击行为,而后匹配特定的错误场景,达到无人自动化测试目的。它的优点是事先不需要预置页面的内容、行为和路径;缺点是效率很低、时长不好控制,并且不能附带业务属性。所以我们要结合上面得到的区块分析结果,让 Monkey 的行为变得更加“可控”。

首先考虑的是深度优先遍历算法:给定一个起始页面,通过图形分析得到页面的全部区块(不用管它是什么),逐个点击并记录其路径。如此一来,就初步得到应用的整体页面结构和访问路径,并且可以按照这个规则进行“有目的”的测试,这就是我们脚本自动生成的核心逻辑。

但以上做法的效率仍然不够高,因为我们“一视同仁”地对待所有可能的路径,所以会出现测试过程在某个页面内过度纠缠(比如一些输入的组合会被误认为是新页面)的情况。因此,我们还要适当地引入一些“奖励机制”,即强化学习算法。

图片

为了进一步达到比较理想的测试效果,当探索出现两种结果时,我们给予对应的操作正向奖励:一是发现新的区块(即优先测试不同的页面);二是出现特定内容(即重点测试某类业务)。这相当于人为给页面和路径设定了一定的权重。经过多轮训练,强化学习完全可以达到令人满意的结果。

以上内容即是目前得到的全部可行性理论研究结果,我们有理由相信,端到端智能化无人自动化测试能力,的确存在较高的可能性。我个人认为未来的自动化测试,一定会按照这个方式来进化,这就有待将来证实吧。

好了,自动化测试概论全篇到此结束,感谢一些读者发来的支持,后续接着讨论其他质量策略,下周再见。

最后:下方这份完整的软件测试视频教程已经整理上传完成,需要的朋友们可以自行领取【保100%免费】

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值