乐于分享
好东西不私藏

巧用AI解锁Squaretest插件,再也不用担心单元测试覆盖率了!

巧用AI解锁Squaretest插件,再也不用担心单元测试覆盖率了!

很多团队在实际的项目开发过程中,会强制检查测试覆盖率。在将代码真正部署到生产环境之前,单元测试覆盖率是许多开发人员必须满足的硬性要求之一。

前阵子,我体验了一款名为SquareTest的IDEA插件:

别再手敲单元测试了!IDEA这个插件,比手写快70%

它的效果令人惊叹!可以一键生成Java类生成单元测试,可惜它的免费试用只有30天。

今天,借助强大的AI辅助代码分析的能力,我将配合deepseek一步一步彻底解锁SquareTest这个单元测试神器,让广大开发者再也不用为测试覆盖率烦恼了。

话不多说,我们启动实战。

从插件市场下载最新版本的Squaretest

当前最新的版本是2025.9.21发布的v1.8.16,我们直接下载离线包:Squaretest.zip。

zip包解开之后,在lib目录下很容易找个一个TestStarter-proguard.jar文件(其他都是三方依赖库),然后用jadx-1.5.3打开这个jar包进行字符串搜索:

这里有一个关于搜索的技巧:因为老外写代码一般命名都很规范,所以我们上来就搜“license”这个关键词。果不其然,有重大发现,而且字符串还是明文形式的,一下子就能定位到。

根据经验原则,很多插件喜欢搞联网校验,所以我们也顺带搜索一下“https”这个关键词,还真有发现,定位到有一个class发出了https请求。


用DeepSeek辅助“静态分析”

把jadx反编译的类com.squaretest.c.p丢给deepseek,几乎是秒回。 DeepSeek:这段Java代码是一个用于验证许可证的客户端类。我来解释一下它的主要功能:

类结构和目的

这是一个名为 p 的类(可能是简写,应该是许可证检查器),主要功能是:

  • 包路径com.squaretest.c.p
  • 主要目的: 通过HTTP请求验证许可证是否有效

核心组件

  1. 日志记录器 (Logger a):

    • 使用IntelliJ平台的日志系统
    • 用于记录异常和调试信息
  2. JSON处理器 (ObjectMapper b):

    • 来自Jackson库,用于JSON序列化和反序列化
    • 通过构造函数注入

主要方法 a(String str)

这个方法用于检查许可证:

  1. 参数验证:

    • 通过 @NotNull 注解和手动检查确保许可证ID不为null
    • 如果为null会调用 a(0) 方法抛出异常
  2. HTTP请求流程:

    // 构建请求URLURI.create(String.format("https://check-license.squaretest.com/%s", str))// 示例: https://check-license.squaretest.com/ABC123-LICENSE-KEY
  3. 处理响应:

    • 将响应JSON反序列化为 e 类型的对象(应该是许可证验证结果的封装类)
    • 使用 ObjectMapper.readValue() 从URL直接读取并转换
  4. 异常处理:

    • 捕获所有异常,记录日志,返回null

代码特点

  1. 混淆代码迹象:

    • 类名 p、变量名 ab 等简写
    • 注释显示来源:TestStarter-proguard.jar
    • 方法名 a() 用于许可证检查(应该是 checkLicense()
  2. 设计模式:

    • 依赖注入(通过构造函数注入ObjectMapper)
    • 工厂模式(返回 e 类型的结果对象)

使用场景

这个类很可能是某个软件(可能是测试工具”SquareTest”)的许可证验证模块的一部分,用于:

  • 在线验证用户许可证的有效性
  • 防止软件盗版或未授权使用
  • 可能支持许可证过期检查等功能

注意: 从代码风格看,这应该是经过ProGuard混淆后的代码,原始类名和方法名应该更具描述性。


再把jadx反编译的类com.squaretest.c.q也丢给deepseek,这个类长一些,3秒回也返回了应答。

DeepSeek:这是一个SquareTest许可证验证器,用于验证许可证文件的真实性和有效性。让我详细解析它的功能:

总体功能

这是一个数字签名验证系统,用于验证SquareTest软件的许可证文件是否合法。

主要组件分析

1. 静态初始化块 (static {…})

  • 硬编码了一个DSA公钥(Base64编码)
  • 用于验证许可证的数字签名
  • 如果密钥解析失败,会抛出运行时异常

2. 正则表达式模式

四个关键的正则表达式:

  • b: 匹配许可证开始标记 BEGIN SQUARETEST LICENSE
  • c: 匹配许可证结束标记 END SQUARETEST LICENSE
  • d: 核心模式,匹配许可证内容:
    • 组1:许可证持有者信息
    • 组2:许可证类型(”User License” 或 “Single User License”)
    • 组3:许可证ID(SQT1-格式的UUID)
    • 组4:Base64编码的数字签名

3. 主验证方法 a(String str)

完整的验证流程:

// 1. 输入检查if (licenseText == null) → 返回失败// 2. 解析许可证文本List<String> licenseParts = b(licenseText)  // 解析和规范化// 3. 格式验证if (!b(licenseParts)) → 返回失败  // 检查格式规则// 4. 数字签名验证String dataToVerify = licenseParts[1-3]的拼接  // 要验证的数据String signature = licenseParts[4]           // Base64签名// 5. DSA签名验证Signature.verify() 使用预加载的公钥验证

4. 许可证格式要求

许可证必须符合以下结构:

--- BEGIN SQUARETEST LICENSE ---[许可证持有者信息][许可证类型] [许可证ID][Base64数字签名]--- END SQUARETEST LICENSE ---

严格验证规则

  • 必须有6行
  • 第一行和最后一行必须精确匹配开始/结束标记
  • 不能有空行
  • 许可证ID必须是 SQT1- 开头,41字符长(UUID格式)
  • 数字签名必须是Base64编码

5. 辅助方法功能

  • b(String): 提取和规范化许可证内容
  • c(String): 移除许可证开始/结束标记周围的空白
  • b(List): 验证许可证格式是否符合所有规则
  • a(List): 重新组装许可证文本

加密和安全性特性

数字签名验证

  • 算法: SHA256withDSA(DSA with SHA-256)
  • 提供者: SUN
  • 流程:
    1. 提取许可证前3行作为签名数据
    2. Base64解码第4行的签名
    3. 使用公钥验证签名是否匹配

许可证ID格式

SQT1-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}

  • 这是一个版本4的UUID(第13位是’4’,第17位是8、9、a或b)
  • SQT1- 前缀标识这是SquareTest许可证

返回值类型

  • 返回 r 对象(应该是验证结果类)
  • r.a 表示验证失败(静态失败实例)
  • 成功时返回包含验证状态和规范化许可证文本的 r 对象

设计特点

  1. 防篡改: 使用数字签名确保许可证内容不被修改
  2. 格式严格: 多层次的格式验证防止伪造
  3. 日志完善: 详细的日志记录便于调试
  4. 异常安全: 捕获所有可能的异常,返回失败而不是崩溃
  5. 离线验证: 公钥硬编码,无需网络连接即可验证

典型使用场景

这个验证器很可能被集成到SquareTest软件的启动过程中,用于:

  1. 检查用户输入的许可证文件
  2. 确保只有付费用户才能使用软件
  3. 防止许可证伪造和盗版
  4. 支持不同许可证类型(单用户/多用户)

这是一个典型的软件保护机制,结合了格式验证和密码学验证来确保许可证的真实性。


理解了“验证”过程,再让AI反推“生成”过程

关键特性:

1. 完全兼容验证器

  • 使用相同的签名算法(SHA256withDSA + SUN provider)
  • 生成符合验证器正则表达式格式的许可证
  • 签名数据格式与验证器提取的完全一致

2. 灵活的API

  • 简单生成:generateLicense(holder, isSingleUser)
  • 自定义ID:generateLicense(holder, isSingleUser, customId)
  • 带过期日期:generateLicenseWithExpiration(holder, isSingleUser, expirationDate)
  • 批量生成:generateLicenses(List<LicenseRequest>)

3. 输入验证

  • 验证许可证持有者不为空且不含换行符
  • 验证自定义许可证ID格式(SQT1-前缀,41字符,合法UUID)
  • 验证密钥和签名算法可用性

4. 工具方法

  • generateKeyPair():生成DSA密钥对
  • encodePublicKeyToBase64():将公钥转换为Base64(用于验证器)
  • decodePublicKeyFromBase64():从Base64还原公钥

5. 扩展功能

  • LicenseManager:管理已签发的许可证
  • LicenseRecord:许可证记录类
  • LicenseRequest.Builder:构建器模式简化创建

生成与验证流程匹配:

// 生成器创建的许可证:数据: "John Doe\nSingle User License SQT1-xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\n"签名: Base64编码的DSA签名// 验证器验证时:1. 提取第2-4行(持有者信息 + 许可证类型+ID)2. 将提取的数据与第5行的签名进行验证3. 确保格式完全匹配

微调原版的校验逻辑来配合我们的生成过程

对于联网验证部分,我们就在方法开头插入一段“逻辑短路”:

if (true) {returnnew e("{\"status\":\"VALID\"}");}

对于离线DSA签名校验的部分,我们直接替换掉公钥即可:

KeyFactory keyFactory = KeyFactory.getInstance("DSA");byte[] arrayOfByte = Base64.getDecoder().decode("MIIExjCCAzkGByqGSM44BAEwggMsAoIBgQDqnNqfX72mbdgwSUYJQFaHq3zzhTjgWNHi9o3qlTZIZuHAW+rN7SQift7ijK2AvOytOZE747cTJns7lsjZ8PagO138kiLVz+SvzJmC8zeE92DDt1muvju+kJimuEyW8f3kTOEcCEwqCCx6dqDvFCkotPMoQGq5vrJPhFd90PRs6G/Y8ISIJpmYv0dC1kJfeg7HXYZgxd1vTjs9O+6BssIa/oyei4S4cZLizCD5YdK82BM6/PNnWrgGgcs3THjzPinRARCD2J+cVyi5Rnb8yxtXvGAojBXYWug4rhlBxaIK4rIEmzWD/jDaRV3bPmrZuZVc2btWgUMWIr6w+S2lM/yrSWzrxEeqG7WoA5Ui8tqY/0FiiTI6ZN9iaraIGHCSfc7jh/E7XJ0k1suh2C7TdaCCUG7oe8euMAZ/SpTi7jY9mSxA8nJbXbSzUl694iu7/Q+hJKWIsPWkrLOoaVGv8J+MgZj7W1PaDJMc7cWYtPg1t3nQTZkCbHugjEsn8RisHj0CIQDE7qwrureb2DGUbXF6Vqbmh1R6qOnFSUpaSy9MoT1sEQKCAYBC5fp4RPj6nYmY2DDQBOexWx0na8vl8Sw17JDBol9YMgGKZyS9nNvoA7Z1UJvtFn89fPhZn8hlxtWg95FYwbyRjwCpRNCtDzj1IPuR2F2CZ00NX4dPql/N/lbNF4wa/cfOh5Vye33ulm7Qs8XO3O+KymKL7+vy0QXHr/jrDanJYQc33WTc4SN7gsGyvIYI1V/9qY1xiUROZYgzFWacBXFr3jbHixMKo98uTWCZFMfI3EcPTjABh8d1+B57GpwNzkBdbqssu52cTvREErpXPdQDxO17wjZHcvVqMMSN549QA/k3HFUmLSyKwiRq3jsC/c/PXL/edPvL/m4OD98xYHZPhNMRwXmkCvZ5qPR6sTyPcGiTJF6xHtzORR+iq5gAGZh/El2NyWYi1Bm6DXHxbGAk3OnTZMOybY7Bo8go9snRSx0DM7ldt3v9vjxrzlM3oaWnrOEBESGUSERxl+KjRMxCO+dou4nie+bL0iCFYUpaM2C+I7G/u25uZHE2PTLIXTEDggGFAAKCAYBSjYbZDWPGeU/+rNUtzK8ynmVZucPVsuCnzGuoMD59KX6l0rdZvre8JEnUeg0NfYWGPy0OHLc2q5TAPr7KzF9m7HU+1RbNE23A+KLil/XC8b7U9Q1aUSV06kh8agd+DLvxPxZAWA96Y4fnIej9sBzmNyuzVVz8ylkG+iTpoVzf+M8NNNYzjh+EwFZPCpMRqHodP5r26zPHiigs6HRWhbp+klmeYJ674tKf8bhwlEn8EwoaNRdi4R2xKcdpLLSxFWilFMMa8cIdTCnjXLHAJenAnSG7FPZWP+VPRYQgArLj6v1S/z7jAjUwISmzhQ6lMu14nJyL68NVdvLbckRoALTfS/F+fo8HobBzfmtsOzl7iKOYWbEHofOKFsGBz/YnxPbjgOPvOO336qabVxx6VN6wNl3WG7RkQIepgRu8FkTtnKZul1wzCB2z1az3NP8cquKldWOIiyXfg/gseFsKip6iCK7HbtxRkGEKPkN9OKBXxJrALGNg5C80S6kqXUOtKqw=".getBytes(StandardCharsets.UTF_8));X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(arrayOfByte);e = keyFactory.generatePublic(x509EncodedKeySpec);

然后用我们自己的私钥来制作license,离线验证因为我们替换了自己的公钥,所以签名验证可过。对于联网验证,已经被“逻辑短路”,提前返回了验证成功。

本地代码调用验证全部成功后,我们就可以打包,直接采用离线安装插件的方式进行“生产”验证:期待的奇迹发生了:)所有Java开发者们,再也不用焦虑单元测试覆盖率了。


你有过被测试组的同事天天追单元测试的经历么?或者被测试覆盖率考核指标折磨过?

如果本文对你有启发和帮助,点赞收藏就是最大的鼓励。也可以关注公众号“码农工具栈”,期待与你成为技术同路人,拥抱AI,拆解二进制世界的运行逻辑。

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 巧用AI解锁Squaretest插件,再也不用担心单元测试覆盖率了!

评论 抢沙发

2 + 2 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮