AI测试 UI 测试方法之图像对比

小小 · 2023年05月09日 · 最后由 小小 回复于 2023年05月10日 · 7607 次阅读

一、痛点解决
1、校验页面,类似 BVT。
2、脚本的维护。
二、工具实现
1、涉及相关知识点
1)playwright(官网- https://playwright.bootcss.com/
2)图像对比实现用到 python 库:imutils,skimage.metrics,cv2,numpy
2、设计思路

考虑到 UI 测试比较滞后,一般会在功能测试之后进行,这里便用到了已有(系统)图片的办法,
1)预期结果的处理,由于是通过系统(原)图片,这里可以通过系统(自动化/爬虫)抓取或人工截图,方式方法根据自身情况而定;
2)对图片的处理,现成的图片对比方法模型很多,这里采用的是 ssim 这种,
(2.1)修改图片大小,使对比图片与原图片大小一致,不然无法计算对比;
(2.2)计算两个灰度图像之间的结构相似度指数 [0,1],数值越高相似度越接近原图;
(2.3)找到不同点的轮廓,标识为 “不同” 的区域周围放置矩形,找到一系列区域,在区域周围放置矩形;
(2.4)分别在原图片上与对比图片上各标出差异;
(2.5)保存差异图片。
3)测试时,通过 playwright,抓取图片,即对比图片,然后与之前的(系统)预期图片进行对比,得到计算分数和差异图片,
通过分数可以分别进行判断,
等于 1 的,图片一致;
小于 1,大于等于 0.9 的,根据需要可以看下;
小于 0.9 的,分数越低越要特别注意,可能是页面有大问题。
3、代码
主要图片对比代码

import imutils
from skimage.metrics import structural_similarity as compare_ssim
import cv2
import numpy as np

class MarkDiffImg:
    @staticmethod
    def cv_imread(file_path):
        """
        读取图片(解决路径中含有中文无法读取的问题),一般是直接cv2.imread(filea_path)
        :param file_path:图片的路径
        :return:
        """
        cv_img = cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), -1)
        return cv_img

    def mark_diff_img(self, result, basesnapshot_png, runningsnapshot_png, DiffSnapshot_Dir, casename, name):
        """
        对比图片并标出差异,保存差异图片
        :param basesnapshot_png:原始图片
        :param runningsnapshot_png:对比的图片
        :param DiffSnapshot_Dir:对比后存放图片的路径
        :param casename:(存在的)文件夹名称
        :param name:图片名称
        :return:
        """
        # 加载两张图片并将他们转换为灰度:
        image_a = self.cv_imread(basesnapshot_png)
        image_b = self.cv_imread(runningsnapshot_png)

        # 修改图片大小
        height, width = image_a.shape[:2]
        size = (int(width * 1), int(height * 1))
        image_b = cv2.resize(image_b, size, interpolation=cv2.INTER_AREA)

        gray_a = cv2.cvtColor(image_a, cv2.COLOR_BGR2GRAY)
        gray_b = cv2.cvtColor(image_b, cv2.COLOR_BGR2GRAY)

        # 计算两个灰度图像之间的结构相似度指数:
        (score, diff) = compare_ssim(gray_a, gray_b, full=True)
        diff = (diff * 255).astype("uint8")
        # print("SSIM:{}".format(score))

        # 找到不同点的轮廓以致于我们可以在被标识为“不同”的区域周围放置矩形:
        thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
        cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        cnts = imutils.grab_contours(cnts)

        # 找到一系列区域,在区域周围放置矩形:
        for c in cnts:
            x, y, w, h = cv2.boundingRect(c)
            cv2.rectangle(image_a, (x, y), (x + w, y + h), (255, 0, 0), 2)
            cv2.rectangle(image_b, (x, y), (x + w, y + h), (255, 0, 0), 2)

        # 基础快照标出与运行时快照的差异 图片
        diffsnapshot_png_a = DiffSnapshot_Dir + casename + '/' + name + '_base.png'
        # 运行时快照标出与基础快照的差异 图片
        diffsnapshot_png_b = DiffSnapshot_Dir + casename + '/' + name + '_running.png'
        # 保存差异图片
        cv2.imencode('.png', image_a)[1].tofile(diffsnapshot_png_a)
        cv2.imencode('.png', image_b)[1].tofile(diffsnapshot_png_b)
        # result,map类型
        result["对比快照-基础快照路径"] = diffsnapshot_png_a
        result["对比快照-运行时快照路径"] = diffsnapshot_png_b

        return result, score


if __name__ == "__main__":
    md = MarkDiffImg()

    map = {}
    ret, score = md.mark_diff_img(map, "../../../baseImage/企业/企业版-能源总览-用电.png", "../../../runningImage/企业版-能源管理-用电.png",
                                  "../../../",
                                  "image",
                                  "测试图像对比_能源总览")

    print(ret, score)

playwright 测试某模块的代码

def test_electric(login_page: Page):
      """
      名称:用电
      步骤:
      1、打开浏览器
      2、查看用电各个模块
      检查点:
      * 检查页面模块和数据。
      """

      page = login_page
      # Click a:has-text("能源总览")
      page.locator("a:has-text(\"能源总览\")").click()
      page.wait_for_url("https://nygl.jd.com/comp/overview")
      # Click span:has-text("用电")
      page.locator(selector="span:has-text(\"用电\")").click()


      # 修改浏览器大小
      viewport_size = {"width": 2560, "height": 1305}
      page.set_viewport_size(viewport_size)

      runningImage_path = "../../../runningImage/企业版-能源管理-用电.png"
      page.screenshot(path=runningImage_path)

      mdi = MarkDiffImg()
      baseImage_path = "../../../baseImage/企业/企业版-能源总览-用电.png"
      map = {}
      retImage, score = mdi.mark_diff_img(map, baseImage_path, runningImage_path, "../../../",
                                          "diffImage",
                                          "测试图像对比_企业版-能源总览-用电")
      if score == 1.0:
          assert True
          print("展示的页面与数据一致")
      elif score >= 0.9 and score != 1.0:
          print("展示的页面与数据基本一致,score: ", score)
          assert True
      else:
          print("展示的页面与数据部分一致,score: ", score)
          assert False

4、结果

共收到 2 条回复 时间 点赞

只能做静态图片吧?动态图片呢,比如图都是一样的,只是图表的数值,列表内容有变化?

LDX 回复

预期结果拿到的图片是静态的,实际结果拿到的也是静态的图片,如果几乎完全一致的话,有点难度,如果想到欢迎交流。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册