Junit4: 引入 BiMatcher 层次结构

创建于 2018-02-08  ·  9评论  ·  资料来源: junit-team/junit4

一个很好的功能是引入BiMatcher层次结构(类似于org.hamcrest.Matcher ,但消耗 2 个参数)。 或者,标准Comparator可以用于此目的。 也可以使用BiPredicate (这会将 JUnit 与 Java-8 联系起来,但会添加开箱即用的 AND/OR/NOT 逻辑)。
所有进一步代码的几个注意事项:

  • BiMatcherBiPredicateComparator名称将互换使用。
  • 所有的代码都不是最终的,只是一个一般的想法。

添加到Assert类中的主要方法:

  • assertThat(T expected, T actual, Comparator<T> biMatcher);
  • assertThat(String message, T expected, T actual, Comparator<T> biMatcher); (可能没有必要,见下文)。
  • 多个assertArray()方法(见下文)。

动机:有时您需要对一批对象进行大量类似的检查。 现有功能允许它,但有很多额外的代码(循环和条件)。 使用Comparator可以用更少的代码和更高的灵活性来完成。

真实的例子(我实际上是从中开始考虑这个特性的):需要测试数组中的所有对象是否相同(取决于布尔标志)。

使用当前的 JUnit 功能,它将如下所示:
更短:

for (int i = 0; i < elements.length - 1; i++) {
  if (checkSame) { // Same
    assertSame("elements must be the same.", elements[i], elements[i + 1]);
  } else { // Not same
    assertNotSame("elements must be not the same.", elements[i], elements[i + 1]);
  }
}

更好的性能(但谁关心测试中的性能):

if (checkSame) { // Same
  for (int i = 0; i < elements.length - 1; i++) {
    assertSame("elements must be the same.", elements[i], elements[i + 1]);
  }
else { // Not same
  for (int i = 0; i < elements.length - 1; i++) {
    assertNotSame("elements must be not the same.", elements[i], elements[i + 1]);
  }
}

我们也可以实现自己的Comparator并使用assertTrue()/assertFalse() ,但是创建比较器的代码也会很大(除非我们使用 lambdas)。

对我来说,使用新方法代码看起来更短更干净,如下所示:

Comparator<T> comparator = checkSame ? BiMatchers.same() : BiMatchers.notSame();
String message = checkSame ? "elements must be the same." : "elements must be not the same.";
for (int i = 0; i < elements.length - 1; i++) {
  assertThat(message, elements[i], elements[i + 1], comparator);
}

为了让它更短,我们可以使用可选的message属性将Comparator扩展到MessagedComparator ,这样 JUnit 就会以某种方式从那里获取它。 像这样:

MessagedComparator<T> comparator = checkSame ? BiMatchers.same("elements must be the same.") : BiMatchers.notSame("elements must be not the same.");
for (int i = 0; i < elements.length - 1; i++) {
  assertThat(message, elements[i], elements[i + 1], comparator);
}

后果非常深远:
1) Assert类通常非常灵活,因为您可以使用任何标准或自己的比较器而无需太多代码。
2) 由于之前的原因,可以更轻松地使用 lambda 来注入任何条件。
3) 额外的逻辑级联(AND、OR、NOT)可以直接添加(类似于org.hamcrest.CoreMatchers )。 目前对于任何新的迷你功能都需要一个新方法,例如assertNotSame()旁边的assertSame()等。或者我们再次需要assertTrue()/assertFalse()方法。
4)整个Assert类可以重构为统一形式。 举个例子:

public static void assertSame(String message, Object expected, Object actual) {
  assertThat(message, expected, actual, BiMatchers.same())
}

public static void assertThat(String message, T expected, T actual, Comparator<T> biMatcher) {
...
}

或者如前所述:

public static void assertSame(String message, Object expected, Object actual) {
  assertThat(expected, actual, BiMatchers.same(message))
}

public static void assertThat(T expected, T actual, MessagedComparator<T> biMatcher) {
...
}

5) 可以添加assertArray()方法(从而重构旧方法)。 例如:

public static void assertArrayEquals(boolean[] expecteds, boolean[] actuals) {
  assertArray(expecteds, actuals, BiMatchers.equals());
}

public static void assertArray(boolean[] expecteds, boolean[] actuals, Comparator<Boolean>) {
...
}

因此,例如,您可以检查 2 个数组中的所有元素都一样简单:

Assert.assertArray(array1, array2, BiMatchers.same());

数组长度或其他检查也可以单独控制。 伪代码:

Assert.assertArray(array1, array2, and(sameLength(), same()));

所有9条评论

感谢您提出这个问题。

我希望 JUnit 4.x 不添加更多断言 DSL。 确实很难让 DSL 正确且灵活,而且当有像 Truth 或 Fest 这样的第三方断言框架时,我不确定在 JUnit 中花费时间和精力是否有意义。

此外,需要 Java 8 的功能应该去http://github.com/junit-team/junit5

@gitIvanB感谢您打开问题。 像@kcooney一样,我也认为最好通过断言库来解决这个问题,例如 AssertJ。

@kcooney@marcphilipp ,感谢您的提示。 看起来使用断言库是这里的最佳实践。
一个额外的问题。 我可以根据经验使用Assert用法已被弃用(或不建议)吗? 尤其要记住将来可能从 JUnit-4 升级到 JUnit-5。

@gitIvanB这个问题有点不清楚。 如果针对 junit4 开发,则使用org.junit.Assert ,如果针对 junit5 开发,则使用org.junit.jupiter.api.Assertions

推荐的方法是从它静态导入方法,以避免每行都有Assert.

@gitIvanB你绝对不应该使用junit.framework.Assert ,但org.junit.Assert很好。 升级时,您可以使用 IDE 将 JUnit 4 转换为 JUnit Jupiter 断言(IDEA 已经支持)。 最大的区别是可选的消息参数在 JUnit 4 中排在第一位,而在 Jupiter 中排在最后。 或者,您现在可以决定使用不同的断言库,并在从 Vintage 迁移到 Jupiter 时继续使用它。

@panchenko , @marcphilipp所以据我所知,JUnit-4 的org.junit.Assert是密封的,不会用新方法来丰富。 在 JUnit-5 中,我需要使用org.junit.jupiter.api.Assertions (作为org.junit.Assert的替代品)或一些断言库。
我需要一些时间来学习 JUnit-5,但乍一看org.junit.jupiter.api.Assertions看起来它的方法一般类似于org.junit.Assert 。 因此,我很有可能需要为 JUnit-5 创建类似的票证 :) 我相信 JUnit-5 应该提供成熟的断言或完全委托给断言库。

@gitIvanB JUnit 的理念是提供一个良好的、可扩展的框架,用于用 Java 编写 Java 测试。 作为一个可扩展的框架,我们经常看到其他开源项目扩展了框架和/或提供了可与 JUnit 一起使用的丰富测试库。

对于断言,有几个项目提供了更丰富的断言集,有时以可扩展的方式提供。
例如,有 Google Truth (https://github.com/google/truth)、FEST (https://github.com/alexruiz/fest-assert-2.x)、AssertJ (http://joel- costigliola.github.io/assertj/) 和 Hamcrest (http://hamcrest.org/JavaHamcrest/)。

因为有太多令人惊叹的断言库,所以我一直不愿意接受org.junit.Assert新功能请求。 我不会认为它是冻结的(尽管我会认为junit.framework.Assert冻结的)。 事实上,4.13 会引入assertThrows 。 但是由于我们永远不会拥有像那些项目那样丰富的断言集,因此我们可以轻松地提供基础知识。

换句话说,围绕这些项目的使用和维护已经形成了健康的社区,所以我认为我们应该拥抱和支持它们。

我不能代表 JUnit5,但我想最初的开发人员觉得如果没有良好的断言方法基础,他们就无法发布下一代 JUnit(或者他们觉得这是确保新框架功能性和功能性的最佳方式)抛光是使用 JUnit5 测试 JUnit5)。 与 Kent、Erich 和 David 相比,新的编程实践使他们对基本断言 API 的外观做出了不同的决定,因此您会看到一些差异。 但是您可能永远不会在 JUnit 中看到像 Hamcrest 或像 Truth 这样的可扩展 DSL 那样丰富的断言集。

我让 JUnit5 团队决定他们将考虑哪些类型的更改。 如果您强烈认为
您提出的方法很重要,因为它是编写良好测试的基础,请随时在那里创建问题。

当我们开始研究 JUnit 5(当时代号为“JUnit Lambda”)时,我们讨论了我们想要实现的范围。 明确决定不在范围内编写一种新的断言库。

或者,正如JUnit 5 用户指南所说:

尽管 JUnit Jupiter 提供的断言工具足以满足许多测试场景,但有时需要或需要更多功能和附加功能(例如匹配器)。 在这种情况下,JUnit 团队建议使用第三方断言库,例如 AssertJ、Hamcrest、Truth 等。因此开发人员可以自由使用他们选择的断言库。

因此,它也没有被冻结,但其目标与 JUnit 4 中的类似,即提供一组基本的断言来让用户开始使用。 我们甚至将 AssertJ 用于我们自己的单元测试。

此页面是否有帮助?
0 / 5 - 0 等级