AssertJ:使用 String.format 生成的消息测试异常

AssertJ: Testing exceptions with messages generated with String.format

我想知道当使用 String.format() 生成该消息时,是否有一种干净而完整的方法来断言附加到抛出异常的消息。例如,class 如:

public class Car {

  public static final String DRIVE_ERROR = "Can't drive while car %s is parked!";

  private String name;
  private boolean parked;

  public Car(String name) {
    this.name = name;
    this.parked = true;
  }

  public void drive() {
    if (parked) {
      throw new IllegalStateException(String.format(DRIVE_ERROR, name));
    }
  }
}

(抱歉这个奇怪的例子,只是想尽可能简单) 现在,如果我正在测试汽车,我会有这样的 class:

public class CarTest {

  @Test
  public void drive_test() {
    Car car = new Car("Greased Lightning");
    assertThatThrownBy(() -> car.drive())
        .isInstanceOf(IllegalStateException.class)
        .hasMessageContaining("???");
  }
}

问题是,对消息进行断言的最佳方式是什么?在这个例子中,我可以分离出汽车名称的声明,然后自己使用字符串格式从 Car 中获取静态字符串并在名称中进行格式化,但这似乎有很多额外的代码,并且不能在很多情况下很容易使用(例如,当格式化字符串中的项目在运行时确定时)。我真正想做的是将错误消息字符串传递给 hasMessageContaining 并让它忽略“%s”占位符并接受该位置的任何内容。有没有办法用 assertJ 进行字符串的正则表达式匹配?或者其他一些干净的方法?

编辑:我也对抛出具有更易于测试的消息的异常的替代方案持开放态度。一种解决方案是仅使用字符串连接,例如 throw new Exception(STATIC_ERROR_MESSAGE + name),然后测试消息是否包含第一部分,但这确实限制了您的消息格式化能力并且看起来不太干净。

与常规字符串断言相比,异常消息断言是有限的。 你可以做的是使用 matches or containsPattern 断言,例如:

@Test
public void test() {
  // GIVEN some preconditions

  // WHEN
  Throwable thrown = catchThrowableOfType(() -> { throw new IllegalStateException("boom!"); }, 
                                         IllegalStateException.class);
  // THEN
  assertThat(thrown.getMessage()).matches(".oo.")
                                 .containsPattern("oo.");
  // or even better thanks to Rolland Illig suggestion
  assertThat(thrown).hasMessageMatching(".oo.");

}

请注意,通过使用 catchThrowableOfType,您不必再检查捕获的异常是否属于预期类型。