AI测试 基于 AI 大模型的精准测试分享

AMEAME · 2024年04月23日 · 最后由 AMEAME 回复于 2024年08月13日 · 42405 次阅读
本帖已被设为精华帖!

一、问题提出

  1. 如何使用大模型解决日常工作中难以解决的问题?

  2. 大模型在自动化测试领域可以发挥什么作用?

  3. 如何利用大模型提前发现故障,并提升产品质量?

  4. 如何发现日常工作中难以察觉的故障?

团队现状:

A. 经常性的泄露一些修改 java 依赖引发的故障,在 maven 的 pom.xml 文件中修改一个依赖的版本号都可能引出故障,见下图例子

此类型的问题难以发现的原因:

  • 方案层面:评估此类版本依赖修改成本较高,因为一项依赖的修改可能牵扯很多代码;
  • 开发层面:因修改 pom.xml 文件依赖版本号操作小,无代码逻辑修改,开发自测场景不完整;
  • 测试层面:QA 不了解具体代码和波及影响,很难确定测试范围,不知道哪些功能需要使用这些依赖;
  • 自动化层面:通过自动化扫描异常日志,日志中存在大量干扰,导致自动化异常扫描精度不高;
  • 成本高:即使评估出测试功能点,方案评估,开发实现,测试验证整个流程成本较高。

B. 没办法对所有的新增代码进行 CodeReview,Review 的效果一般,容易泄露在功能测试阶段无法发现的问题。

因此,针对上述原因,将大模型与当前自动化测试相结合,通过大模型自动化来防护此类问题就是本实践的目标。

二、解决思路

虽然该类问题难以发现,但也并不是毫无办法。

该类问题有个共同点:在 JVM 运行时,找不到某个依赖,会抛出:ClassNotFoundException,NoClassDefFoundError,IllegalAccessException,InstantiationException,NoSuchMethodError 等异常。

那我们收集到组件日志后是否就能规避这种问题呢?

答案肯定是不行的。举个例子:

例如团队内组件热部署探针运行在框架中,在热部署探针启停阶段,因探针在框架中提交的线程还在轮询,探针实例已经被销毁后,依旧会出现很多上面的异常来干扰判断,无法得出精确的结果,所以需要从提升精度的方向去优化。以下是思考方向:

  1. 故障多出现于代码修改前后。
  2. 不同分支代码的差别可能导致新故障的出现。

如何去解决呢,想了以下几点:

  1. 如果能获取到 git 上面的代码提交记录,就能收集到热点代码。
  2. 能拿到不同分支代码的变化,就能收集到另一部分的热点代码。

然后还有最重要的一点,收集的异常信息和热点代码的映射如何建立呢?如果手动去建立 异常->依赖 的映射始终会滞后,出一次问题加一次不利于可持续发展,而且只有当在出问题时才能知道抛出的异常长什么样,难以完成该项目。

所以引入了大模型去帮我完成 异常->依赖 的建立。

三、实践过程

架构概述

实践过程采用了以下架构:

  1. 数据源层:项目组件运行在大环境下,利用大环境提供的基本数据源。
  2. 代码变更获取层:从 Git 仓库中获取代码变更信息,包括同分支增量对比和不同分支全量对比。
  3. 日志处理层:通过日志收集模块处理组件日志,提取异常信息。
  4. AI 大模型交互层:向 AI 大模型提出针对性问题,并解析其回答。
  5. 结果处理与输出层:根据 AI 大模型的回答在缓存中寻找热点代码,记录日志,并发送邮件通知对应处理人。

针对 pom.xml 的实践步骤

  1. 获取大环境数据源:项目组件运行在大环境下,利用大环境提供的数据源进行后续操作。

  2. 获取代码变更

    • 从 Git 中获取 XX 项目 XX 分支的 XX 个提交记录。
    • 遍历每个提交记录,获取修改的文件列表。
    • 过滤出 maven 依赖类型的文件(如 pom.xml)。
  3. 解析修改文件

    • 对修改的文件进行分类处理,包括只增、只减、修改三种类型。
    • 解析出代码修改内容,并将其缓存起来,作为热点代码。
  4. 日志处理

    • 通过日志收集模块下载组件实例的日志文件(包括.log 和.gz 格式)。
    • 解压 gz 格式的日志文件,并将不同实例的日志进行汇总。
    • 将日志按照级别(info, warn, error)进行分类。
    • 提取 warn 和 error 级别日志中的异常堆栈信息。
    • 对堆栈信息进行去重处理,减少后续处理的冗余数据。
  5. 与 AI 大模型交互

    • 根据提取的异常信息,向 AI 大模型提出针对性问题。
    • 解析 AI 大模型的回答,获取可能的故障原因或解决建议。
  6. 热点代码匹配与记录

    • 使用 AI 大模型的回答在缓存的热点代码中寻找相关匹配项。
    • 如果找到匹配项,记录相关日志信息,包括匹配的热点代码和异常信息。
  7. 发送邮件通知

    • 将记录的日志信息整理成邮件格式。
    • 发送邮件到对应处理人的邮箱,通知其关注并解决相关问题。

针对 java 文件的实践步骤

  1. 获取大环境数据源:同样利用大环境提供的数据源。

  2. 获取指定需求编号的代码变更

    • 从 Git 中获取指定需求编号的提交记录。
    • 遍历每个提交记录,获取修改的文件列表。
    • 过滤出.java 文件。
  3. 解析修改文件

    • 对修改的.java 文件进行类似 pom.xml 的解析操作。
    • 分类处理只增、只减、修改的代码,并缓存热点代码。
  4. 与 AI 大模型交互

    • 根据需求背景和修改内容,向 AI 大模型提出针对性问题。
    • 解析 AI 大模型的回答,获取可能的代码风险或优化建议。
  5. 结果输出

    • 将 AI 大模型的回答或解析结果输出到文件中,便于后续查看和分析。

通过上述实践过程,可以有效结合 AI 大模型与自动化测试技术,提升故障发现和产品质量的能力。同时,减少了人工审查和测试的工作量,提高了工作效率和准确性。

效果评价

针对 pom.xml 如下:

  1. 运行自动化测试用例,测试用例会从 git 库中获取指定项目指定分支的代码提交记录,进行解析和缓存。

    • 在代码配置每个标签中的子标签,如需解析其他的标签,则新增一个标签类并重写查找该标签的正则表达式
    • 按照新增或删除的配置和修改的配置(修改可能只会改一个结构体的一部分,需要找到对应完整的结构体)进行解析
    • 缓存解析结果
  2. 收集环境上日志,拿到原始日志后进行分类

    • 先把容器内的日志拷贝到节点上,通过 SSH 协议把日志下载到运行环境。组件可能有多个实例,获取所有实例日志。
    • 把 GZ 日志包进行解压,对日志内容进行分类(info, warn, error),按照配置文件配置的异常类型在 warn 和 error 日志中进行查找
    • 可能某些异常日志会很多(几个 G 日志),不方便查看,所以下面进行去重。
  3. 通过一些对字符串的算法,把 java 异常堆栈进行去重,同种类型的异常只保留一个,方便人工查看和邮件推送

  4. 把解析好异常堆栈向大模型提问,自定义模板的大模型提问见下:
    出现以下异常是缺少什么 maven 依赖: java.lang.ClassCastException: XXXXXX

大模型回答:
这个问题可能是由于你的项目缺少了 CGLIB 库引起的。你可以尝试在你的 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>  <!-- 你可以选择需要的版本号 -->
</dependency>

然后运行 mvn clean install 以重新构建你的项目并下载所需的依赖

  1. 解析出答案中的依赖 groupId 和 artifactId 等关键字段,把解析出的依赖在热点代码中查找,找到了说明大概率引出故障!

针对 java 文件的自动化大模型 CodeReview

实践过程

1. 运行自动化测试用例

测试用例会从 git 库中获取指定项目指定需求编号的代码提交记录,进行解析和缓存。

  • 把每个 java 文件拿到后,把代码解析成抽象语法树(AST)。
  • 依次解析各个节点,如 Class, Package, Import, Implement, Constructors, Field, Method 等。
  • 对解析结果进行缓存,以便后续快速访问。

2. 并发请求 AI 大模型进行 CodeReview

获取需要使用的 Method 信息,以最大并发 20 的限制去请求 AI 大模型。以下是 prompt 模版的示例:

## 角色: 代码评审专家

## 背景: 你是一位java语言专家级工程师,具备出色的代码审查能力,能够从多个方面评审代码。

## 任务: - 对指定代码进行系统化地审查。
## 任务细节: 对代码能够从以下方面进行逐一审查:
- 逻辑漏洞:评审代码逻辑是否有缺陷。遵守要求:排除因为没有判空而导致的空指针异常等,不评审因没有判空等而导致的漏洞,不进行入参为空的校验。
- 代码结构:评审代码结构是否清晰、易于理解,变量和函数的命名是否规范。
- 函数实现:评审函数实现是否正确、高效、安全,是否存在潜在的内存泄漏。
- 异常处理:评审异常处理机制是否完善,是否存在可能引发异常或崩溃的代码。
- 数据结构:评审代码中使用的数据结构是否合理,是否能满足实际需求。
- 性能优化:评审代码中是否存在性能瓶颈,是否有不必要的计算和调用等问题,是否可以进行优化。
- 资源泄露:评审代码中是否存在资源泄露,是否有文件流、线程池等未关闭情况。
- 安全性:评审代码中是否存在安全漏洞,是否有防止SQL注入、XSS攻击等安全机制。
- 可读性:评审代码是否清晰易于读懂,注释是否充分、准确、规范。
## 任务输出: 评审代码存在的问题,并输出代码风险等级
## 待评审代码
[这里插入待评审的Java方法代码]

3. 写入评审结果

把 AI 大模型返回的 CodeReview 结果写入文件,方便后续查看和分析。

评审结果

代码走查报告
一、逻辑漏洞

在XXX方法中,XXXList values = new XXXList();被初始化为一个单例,然后在多线程环境下被多个线程共享。这可能导致线程安全问题,因为多个线程可能同时修改values的内容。根据方法内的使用情况来看,每个线程应该处理自己的XXXList实例,而不是共享一个。

countDownLatch.await();在调用时未设置超时时间,如果某个线程因为异常或其他原因未能执行countDown,那么主线程将永远等待下去,造成程序阻塞。

二、代码结构

代码结构基本清晰,但是使用了裸线程池和CountDownLatch,使得代码在并发控制上稍显复杂。建议使用更高级的并发工具,如CompletableFuture,以简化代码。

变量命名较为规范,但XXX方法作为lambda表达式中的匿名内部类调用,其参数列表过长,可以考虑封装成对象来传递。

三、函数实现

executor.submit中捕获了所有异常,但只是打印了日志,没有重新抛出或者对异常进行处理,这可能会导致调用者无法得知操作是否成功。

使用了CopyOnWriteArrayList来作为结果集合,这个数据结构适合读多写少的场景。然而,在本例中,写操作(添加元素)频繁发生,这可能会导致性能问题,因为每次写操作都会复制底层数组。如果并发写操作不多,可以考虑使用其他并发集合,如ConcurrentLinkedQueue。

四、异常处理

异常处理机制不完善,如上所述,捕获异常后只是打印了日志,并没有合适的错误处理逻辑。
五、数据结构

CopyOnWriteArrayList的选择可能不是最佳的数据结构,如上所述,它可能不适合写操作频繁的场景。
六、性能优化

由于使用了CopyOnWriteArrayList,且写操作频繁,可能会导致性能问题。建议考虑其他数据结构或并发控制策略。

线程池中的核心线程数设置为1,可能无法充分利用多核CPU资源,如果处理逻辑不是IO密集型的,考虑增加核心线程数。

七、资源泄露

代码中创建了线程池,但未在方法结束时关闭线程池。这可能导致资源泄露,因为线程池会一直存在,即使方法执行完毕。建议在方法结束时关闭线程池,或者使用try-with-resources语句(如果线程池实现了AutoCloseable接口)。
八、安全性

代码中未看到明显的安全漏洞,如SQL注入或XSS攻击,但需要注意XXX和XXX方法内部是否有可能引入安全问题。
九、可读性

代码整体可读性较好,但并发控制和异常处理部分可以进一步简化,以提高可读性。
代码风险等级

综合上述评审,代码存在逻辑漏洞、资源泄露等问题,风险等级评定为中等偏高。建议对上述问题进行修复,以提高代码的健壮性和性能。

这段代码线程泄露的问题当时没有测试到,从而在春节的时候泄露到外场去了😂 ,导致开发在春节还在排查问题。
还有一个问题是在修改后,通过大模型评审发现的,就是方法中 new 了个非线程安全的 List,并且线程池提交的多个线程都传入了这个 List,在处理逻辑中又进行了 add 的操作,所以可能会导致数据最后 add 少了或者直接抛出异常。

这些问题虽然很基础,但是如果没有进行代码走查,其实通过黑盒测试很难发现这两个问题,还是体现了该工具产生的价值!

4. 推送邮件

把评审风险为中或者高的结果推送到相关人员进行进一步的人工评审。

优势与效果

通过自动化进行大模型 CodeReview,可以显著减少人工审查的时间和成本,提高代码质量和安全性。在分钟级的时间内就能完成需求或故障修改的大模型 CodeReview,极大地提升了开发效率和代码稳定性。同时,通过并发请求 AI 大模型,可以充分利用计算资源,进一步提高审查速度。

共收到 32 条回复 时间 点赞

写的很好👍 码住。

感谢分享。
总结:大模型解决 class 依赖问题和用于 codereview。

找不到依赖这个,我们是从流程上去解决的,用大模型做这个倒是没见过~

潘西西 回复

😀

Ouroboros 回复

请教下如何从流程上去解决呢?

AMEAME 回复

流水线自动打包

所有核心的点都是提交给 AI 大模型,这个大模型是第三方的还是你们自己的? 涉及到的项目隐私风险咋算?所有流程的前提都是基于大模型给出精准无误的判断,你咋知道大模型的回复是精准的?

测试新人 回复

AI 大模型是公司自己的,不存在隐私泄露。 是否精准还是依赖大模型的能力,本来也是防护黑盒测试发现不了的问题,发现一个故障就是血赚,目前效果还是可以,每个迭代还是能发现几个故障。😀

AMEAME 回复

啥公司? 真有钱,还训练了一个大模型。看来你们的模型比 chatgpt 强,因为每次用 gpt 问问题时都会给我报一些不存在的库或者一些内置库不存在的方法,或者本来是正确的回答,我多问句还有什么缺陷,它会没事找事的罗列出些不存在的问题,所以我才会有这个疑问

AMEAME 回复

像我就是测黑盒的,现在报告显示代码逻辑有漏洞同时有风险,然后找了开发看了后回复是大模型判断错误了或者当前漏洞不存在,我作为一个黑盒肯定不懂也不敢确定开发说的是否真的,那这种情况你们是怎么处理? 一两次还好,多了开发也烦,黑盒测试也累

测试新人 回复

首先需要完全看的懂开发的代码,这样你就不会被开发糊弄,并且在团队里面的地位是和开发平等的,开发不敢随便应付你提出的 bug。 具备这种能力后,看这个评审报告其实就很明显了,这段代码有两个 bug,一个是在方法里面 new 线程池但是在方法里面没有关闭,导致线程泄露,这个问题我们还泄露到外场了😂 ,后面做了这个工具后,把这段代码进行分析,又发现了有一个对象在多线程环境下使用,add 会导致丢数据或者抛异常。这两个问题很明显,其实一看报告对着代码一看就能确诊😀 所以最主要的还是要提示代码能力

AMEAME 回复

测试完全看懂开发代码会不会有点扯😂 。。。。,另外大模型是静态分析的吧,如你说的 new 线程池,可能大模型报没有关闭的错,但大模型又无法准确了解调用方法的上下文环境,线程池的关闭通常取决于应用程序的资源管理策略,可能涉及到整个应用程序的生命周期管理、线程池的复用性等因素。其实按你这么一说,我倒觉得这个工具可以绕开测试直接扔给开发那边了,干嘛还需要测试做中介,他们自己写的代码自己最清楚,增强他们的自测和转测质量。

写的 不错 同样适合开发提测时的 codeReview 顶!!

解析出答案中的依赖 groupId 和 artifactId 等关键字段,把解析出的依赖在热点代码中查找,找到了说明大概率引出故障!

这种 NoClassDefFoundError 出来,然后不太知道 MAVEN 里面可能有问题,这个基本功其实真的不怎么样,当然你说用大模型能不能解决,肯定可以,用搜索其实也能解决,成本问题。

然后有一点小疑问就是,既然 NoClassDefFoundError 都不太知道可能哪里有问题的,为啥要去搞那些高难度的什么 AST,抽象语法树,直接代码分析,代码级别测试,如果能这么分析,我觉得还不如直接生成单元测试和集成测试来的直接呢,数据反正都能生成的,用例也能覆盖,为啥要绕着弯子去做一样的事情。

测试新人 回复

不少公司都布局 ai 了吧,我看挺多有的

还是要看大模型能力可不可靠,感觉各个公司 AI 训练方式可能都大同小异,主要还是训练数据的质量问题,要怎么生成大量有价值的训练数据

AMEAME #16 · 2024年04月23日 Author

1.因为日志中出现 NoClassDefFoundError 这种类型的异常并不一定是问题,因为我们的组件是运行在一个独立框架中的,框架有一个独立的 Classloader 用于加载不同组件公共的 jar 包,目的是为了组件瘦身。组件的实例又有自己的 Classloader 用于加载自己的 jar 包,组件实例会调用框架去执行一些定时任务或者线程轮询的一些任务,当组件实例被销毁时,框架获取对于实例是否存活的状态可能延后,不知道实例已经没了,去执行运行的定时任务,因为实例的 Classloader 已经没有了,所以也会报上述异常,所以并不能确定它一定是问题,所以还要结合 maven 修改一起做判断。
2.做 AST 是因为需要获取一些上下文一起喂给大模型,最大化提升大模型给出的结果
3.之前很多泄露的问题编译后没有报错,单元测试也没覆盖到某些方法,所以在运行时才会抛出这些异常,所以还是得在程序运行时去做一个持续监控😀

AMEAME #17 · 2024年04月23日 Author
测试新人 回复

因为线程池是在方法里面 new 出来的,所以方法结束后必须要显式的去关闭,至于上下文所以去解析了 AST,可以获得同一个类的上下文,如果涉及跨类的上下文,就要做字节码的解析了,目前还没做到那😀
如果开发都能很自觉的完成这些流程,说实话测试就不需要那么多岗位了😂 ,QA 还是要在整个流程里面做一个推动作用,完成故障闭环

AMEAME #18 · 2024年04月23日 Author
KKK 回复

😘

AMEAME #19 · 2024年04月23日 Author
sir 回复

雀食,大模型能力越强,工具效果越好

陈恒捷 将本帖设为了精华贴 04月23日 23:44

大佬有 demo 例子可以参考嘛

AMEAME #22 · 2024年04月24日 Author
zhangnuo 回复

目前项目中和公司机密内容耦合,也没时间优化,暂时还不能开源😂

感觉很厉害,先三连后理解

要是能 dome 下就更好了

楼主是如何做到把获取到的提交的代码发给大模型进行 review 的呢?
1、是提取本次变更的文件的所有内容?这样的话假如有开发人员一次提交较多代码,先假设提交了 5 个文件的变化,难道要把这 5 个文件的内容全部提取出来发给大模型?这样难道不会超过大模型输入内容的限制吗?
2、还是仅仅只提取本次新增或者删除的代码行?如果这样的话,好多变更的代码都是一个方法或者一个类中的极少部分,这对于大模型的 review 不是很友好啊。有时候无法 review 出有效的建议。

望楼主能够解惑。感谢🙏

AMEAME #29 · 2024年07月22日 Author
ningboRainbow 回复

1.假设五个文件都做了修改,会把五个文件中修改部分提取出来,粒度目前为方法级别。一个方法的代码量看各个公司制定的规则吧,一般不能超过 50 行。 这个内容大小大多数 AI 模型都是支持的吧。
2.粒度是方法级别,在一个方法里面修改了一行,也会把整个方法给大模型,修改的部分重点给模型标注,然后 review

1、我们公司的语言比较多,既有嵌入式侧的 C/C++ 为主,也有 APP 侧的 swift、kt 等代码。而且方法的大小基本上都是千行级别😂 😂
2、不是每一个提交的代码修改都是刚好落在方法内部的,比如一些宏定义的修改,一些 import,还有版本号的修改,或者是配置文件的修改,等等。总之提交的修改比较杂乱,这种你们是如何处理给大语言模型的呀。

AMEAME #31 · 2024年07月24日 Author
rainbowbobo521 回复

没有写过 C/C++,如果代码很多的话,可以把方法拆成多个代码段,让大模型记住上下文,可能可以解决,但是效果会差些。
Import 的改动是直接过滤掉的,版本号的话 Java 一般改动在 pom 文件里面,是和组件运行日志一起做校验的,配置这些改错了,一般环境上运行会抛一些异常,这样组合来使用的🤔

AI CR 给出的结果信息量有点大, 也许有部分是关键有效的, 但是大量不重要的信息,会淹没重要信息, 长期执行下去,会降低开发的关注度。

请问作者有类似情况,或者优化方法吗?

YuSong 回复

确实有很多信息是不重要的,但还不知道如何提取关键信息😐

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