1. 概述

本教程将带你一步步创建自定义的 AssertJ 断言。AssertJ 的基础知识可以参考这里

简单说,自定义断言能让你为特定类创建专属断言方法,使测试代码更贴近业务领域模型。

2. 测试类

本教程的测试用例围绕 Person 类展开:

public class Person {
    private String fullName;
    private int age;
    private List<String> nicknames;

    public Person(String fullName, int age) {
        this.fullName = fullName;
        this.age = age;
        this.nicknames = new ArrayList<>();
    }

    public void addNickname(String nickname) {
        nicknames.add(nickname);
    }

    // getters
}

3. 自定义断言类

编写自定义 AssertJ 断言类非常简单。只需声明一个继承 AbstractAssert 的类,添加必需的构造函数,并实现自定义断言方法。

断言类必须继承 AbstractAssert 才能访问核心 API 的基础断言方法(如 isNotNullisEqualTo)。

以下是 Person 类的自定义断言类骨架:

public class PersonAssert extends AbstractAssert<PersonAssert, Person> {

    public PersonAssert(Person actual) {
        super(actual, PersonAssert.class);
    }

    // 后续描述的断言方法
}

继承 AbstractAssert 时需指定两个类型参数:

  1. 自定义断言类本身(用于方法链式调用)
  2. 被测试的类

为提供入口点,可定义静态方法启动断言链:

public static PersonAssert assertThat(Person actual) {
    return new PersonAssert(actual);
}

接下来介绍 PersonAssert 类中的几个自定义断言方法:

全名验证:检查 Person 的全名是否匹配指定字符串:

public PersonAssert hasFullName(String fullName) {
    isNotNull();
    if (!actual.getFullName().equals(fullName)) {
        failWithMessage("Expected person to have full name %s but was %s", 
          fullName, actual.getFullName());
    }
    return this;
}

成年验证:根据年龄判断是否成年:

public PersonAssert isAdult() {
    isNotNull();
    if (actual.getAge() < 18) {
        failWithMessage("Expected person to be adult");
    }
    return this;
}

昵称验证:检查是否存在指定昵称:

public PersonAssert hasNickName(String nickName) {
    isNotNull();
    if (!actual.getNickNames().contains(nickName)) {
        failWithMessage("Expected person to have nickname %s", 
          nickName);
    }
    return this;
}

当存在多个自定义断言类时,建议将所有 assertThat 方法封装到统一入口类:

public class Assertions {
    public static PersonAssert assertThat(Person actual) {
        return new PersonAssert(actual);
    }

    // 其他断言类的静态工厂方法
}

这个 Assertions 类作为所有自定义断言的统一入口,同名方法通过参数类型区分。

4. 实战演示

以下测试用例展示自定义断言的使用。注意:assertThat 方法从自定义 Assertions 类导入,而非 AssertJ 核心 API。

全名验证示例

@Test
public void whenPersonNameMatches_thenCorrect() {
    Person person = new Person("John Doe", 20);
    assertThat(person)
      .hasFullName("John Doe");
}

成年验证(失败用例)

@Test
public void whenPersonAgeLessThanEighteen_thenNotAdult() {
    Person person = new Person("Jane Roe", 16);

    // 断言将失败
    assertThat(person).isAdult();
}

昵称验证(失败用例)

@Test
public void whenPersonDoesNotHaveAMatchingNickname_thenIncorrect() {
    Person person = new Person("John Doe", 20);
    person.addNickname("Nick");

    // 断言将失败
    assertThat(person)
      .hasNickname("John");
}

5. 断言生成器

为领域模型创建自定义断言类能显著提升测试可读性。

但若项目类数量庞大,手动创建所有断言类会非常耗时。这时 AssertJ 的断言生成器就派上用场了。

Maven 配置

pom.xml 添加插件:

<plugin>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-assertions-generator-maven-plugin</artifactId>
    <version>2.1.0</version>
    <configuration>
        <classes>
            <param>com.baeldung.testing.assertj.custom.Person</param>
        </classes>
    </configuration>
</plugin>

插件最新版本查看这里

<classes> 元素指定需要生成断言的类。更多配置参考官方文档

生成规则

AssertJ 生成器会为目标类的每个公共属性创建断言,方法名根据字段/属性类型自动生成。完整说明见参考手册

使用步骤

  1. 在项目根目录执行:
    mvn assertj:generate-assertions
    
  2. 生成的断言类位于 target/generated-test-sources/assertj-assertions 目录

生成的入口类示例:

// 生成注释已省略

package com.baeldung.testing.assertj.custom;

@javax.annotation.Generated(value="assertj-assertions-generator")
public class Assertions {

    @org.assertj.core.util.CheckReturnValue
    public static com.baeldung.testing.assertj.custom.PersonAssert
      assertThat(com.baeldung.testing.assertj.custom.Person actual) {
        return new com.baeldung.testing.assertj.custom.PersonAssert(actual);
    }

    protected Assertions() {
        // empty
    }
}

⚠️ 重要提醒

  • 将生成的源文件复制到测试目录
  • 添加自定义断言方法满足特殊需求
  • 生成器仍在完善中,代码可能不完全正确
  • 建议作为辅助工具使用,而非完全依赖

6. 总结

本教程展示了如何手动和自动创建 AssertJ 自定义断言,提升测试代码可读性。

  • 少量类:手动创建足够
  • 大量类:推荐使用生成器

所有示例代码可在 GitHub 获取。


原始标题:Custom Assertions with AssertJ | Baeldung