基于属性的Java测试:比Junit更快地找出代码问题

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

基于属性的Java测试:一个测试替代一千个

\n\n

您是否曾经编写过一个单元测试,它通过了绿色,但一周后却在生产环境中因为“某个奇怪的值”而失败?很熟悉吧。常规的(基于示例的)测试就像检查水壶是否沸腾,但只在100°C时检查。如果气压不同呢?如果海拔高度不同呢?基于属性的Java测试正是为了解决这个问题:您不需要手动列举示例,而是描述您的代码必须满足的属性,而框架会生成数百个随机输入数据并检查一切是否正常。

\n\n

这种方法对于编写复杂算法、处理集合、解析器或数学函数的Java开发者尤其有用。在本文中,我们将探讨如何使用jqwik库将基于属性的测试集成到您的项目中,并展示真实的代码示例。

\n\n

什么是基于属性的测试,它与常规测试有何不同?

\n\n

在经典方法中,您编写:

\n\n
@Test\nvoid testAddition() {\n    assertEquals(4, Calculator.add(2, 2));\n    assertEquals(0, Calculator.add(-1, 1));\n}
\n\n

一切正常,直到有人传入了Integer.MAX_VALUE。或者null。或者一个非常大的负数。基于属性的测试颠覆了逻辑:您说的不是“期望某个结果”,而是“对于任何有效的输入,结果都满足属性X”。

\n\n

例如,加法的属性:add(a, b) == add(b, a)(交换律)。或者add(a, 0) == a(单位元)。框架会生成数千个随机的a和b对,并检查属性是否被违反。

\n\n

基于属性的测试的主要优势:

\n
    \n
  • 自动生成测试数据 — 您会发现甚至没想过的Bug。
  • \n
  • 减少测试数量 — 一个属性测试可以替代数十个基于示例的测试。
  • \n
  • 检查边界情况 — 生成器能够创建“危险”的值(0、null、空字符串、大数字)。
  • \n
  • Shrinking — 如果测试失败,框架会将输入数据最小化到能重现错误的最简单示例。
  • \n
\n\n

Java中用于基于属性测试的库

\n\n

市场上有几个成熟的解决方案:

\n
    \n
  • jqwik — 现代、轻量级,与JUnit 5完美集成。推荐作为入门选择。
  • \n
  • QuickTheories — 速度快,但不太流行。
  • \n
  • JUnit QuickCheck — 旧Scala工具的分支,但支持不佳。
  • \n
\n\n

我们将使用jqwik,因为它拥有最方便的API和出色的文档。在pom.xml中添加依赖:

\n\n
<dependency>\n    <groupId>net.jqwik</groupId>\n    <artifactId>jqwik</artifactId>\n    <version>1.8.4</version>\n    <scope>test</scope>\n</dependency>
\n\n

或者对于Gradle:

\n\n
testImplementation 'net.jqwik:jqwik:1.8.4'
\n\n

编写第一个基于属性的Java测试

\n\n

假设我们有一个反转字符串的方法:

\n\n
public static String reverse(String s) {\n    if (s == null) return null;\n    return new StringBuilder(s).reverse().toString();\n}
\n\n

可以检查什么属性?如果反转字符串两次,我们会得到原始字符串。这是基于属性的测试的理想属性。

\n\n
import net.jqwik.api.*;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass StringProperties {\n                

博客

书籍推荐