一个很好的功能是引入BiMatcher
层次结构(类似于org.hamcrest.Matcher
,但消耗 2 个参数)。 或者,标准Comparator
可以用于此目的。 也可以使用BiPredicate
(这会将 JUnit 与 Java-8 联系起来,但会添加开箱即用的 AND/OR/NOT 逻辑)。
所有进一步代码的几个注意事项:
BiMatcher
、 BiPredicate
和Comparator
名称将互换使用。添加到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()));
感谢您提出这个问题。
我希望 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 用于我们自己的单元测试。