有人知道为什么 JUnit 4 提供 assertEquals(foo,bar)
而不是 assertNotEqual(foo,bar)
方法吗?
它提供了assertNotSame
(对应于assertSame
)和assertFalse
(对应于assertTrue
),因此它们没有费心包含assertNotEqual
似乎很奇怪。
顺便说一句,我知道 JUnit-addons 提供了我正在寻找的方法。我只是出于好奇而问。
assertEquals
and assertNotEquals
上的一个很好的例子
我建议您使用较新的 assertThat()
样式断言,它可以轻松描述各种否定,并自动构建对您的预期以及断言失败时得到的结果的描述:
assertThat(objectUnderTest, is(not(someOtherObject)));
assertThat(objectUnderTest, not(someOtherObject));
assertThat(objectUnderTest, not(equalTo(someOtherObject)));
所有三个选项都是等效的,选择您认为最易读的一个。
要使用方法的简单名称(并允许这种时态语法起作用),您需要以下导入:
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
JUnit 4.11 中有一个 assertNotEquals
:https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and-assume
import static org.junit.Assert.assertNotEquals;
我也想知道。 Assert 的 API 不是很对称;为了测试对象是否相同,它提供了 assertSame
和 assertNotSame
。
当然,写起来也不会太长:
assertFalse(foo.equals(bar));
有了这样的断言,不幸的是,输出的唯一信息部分是测试方法的名称,因此应该单独形成描述性消息:
String msg = "Expected <" + foo + "> to be unequal to <" + bar +">";
assertFalse(msg, foo.equals(bar));
这当然很乏味,最好自己推出 assertNotEqual
。幸运的是,将来它可能会成为 JUnit 的一部分:JUnit issue 22
我认为缺少 assertNotEqual 确实是一种不对称,并且使 JUnit 的可学习性降低了一些。请注意,添加方法会降低 API 的复杂性时,这是一个很好的案例,至少对我而言:对称有助于统治更大的空间。我的猜测是,省略的原因可能是调用该方法的人太少了。然而,我记得有一段时间甚至 assertFalse 都不存在。因此,我对最终可能会添加该方法抱有积极的期望,因为它并不困难;尽管我承认有许多变通方法,甚至是优雅的变通方法。
我很晚才来参加这个聚会,但我发现表格是:
static void assertTrue(java.lang.String message, boolean condition)
可以适用于大多数“不等于”的情况。
int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
我正在使用 jUnit4.12 在 java 8 环境中开发 JUnit
对我来说:编译器无法找到方法 assertNotEquals,即使我使用了
import org.junit.Assert;
所以我将assertNotEquals("addb", string);
改为Assert.assertNotEquals("addb", string);
因此,如果您遇到关于 assertNotEqual
无法识别的问题,请将其更改为 Assert.assertNotEquals(,);
它应该可以解决您的问题
import static org.junit.Assert.*;
,您将能够使用所有没有类名的断言。
人们想要 assertNotEquals() 的明显原因是比较内置函数,而不必先将它们转换为完整的对象:
详细示例:
....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....
对比
assertNotEqual(1, winningBidderId);
遗憾的是,由于默认情况下 Eclipse 不包含 JUnit 4.11,因此您必须是冗长的。
警告我认为不需要将“1”包装在 Integer.valueOf() 中,但由于我是从 .NET 新返回的,所以不要指望我的正确性。
最好将 Hamcrest 用于否定断言而不是 assertFalse,因为在前者中,测试报告将显示断言失败的差异。
如果你使用 assertFalse,你只会在报告中得到一个断言失败。即丢失有关故障原因的信息。
通常当我期望两个对象相等时我会这样做:
assertTrue(obj1.equals(obj2));
和:
assertFalse(obj1.equals(obj2));
当他们被认为是不平等的。我知道这不是您问题的答案,但这是我能得到的最接近的答案。它可以帮助其他人在 JUnit 4.11 之前的 JUnit 版本中搜索他们可以做什么。
我完全同意OP的观点。 Assert.assertFalse(expected.equals(actual))
不是表达不等式的自然方式。
但我认为,除了 Assert.assertEquals()
,Assert.assertNotEquals()
可以工作,但对于记录测试实际断言的内容以及理解/调试作为断言失败。
所以是的,JUnit 4.11 和 JUnit 5 提供了 Assert.assertNotEquals()
(JUnit 5 中的 Assertions.assertNotEquals()
),但我真的避免使用它们。
作为替代方案,我通常使用匹配器 API 来断言对象的状态,该 API 可以轻松挖掘对象状态,清楚地记录断言的意图,并且对于理解断言失败的原因非常用户友好。
这是一个示例。
假设我有一个 Animal 类,我想测试 createWithNewNameAndAge()
方法,该方法通过更改名称和年龄但保留它最喜欢的食物来创建一个新的 Animal 对象。
假设我使用 Assert.assertNotEquals()
来断言原始对象和新对象是不同的。
下面是 createWithNewNameAndAge()
实现有缺陷的 Animal 类:
public class Animal {
private String name;
private int age;
private String favoriteFood;
public Animal(String name, int age, String favoriteFood) {
this.name = name;
this.age = age;
this.favoriteFood = favoriteFood;
}
// Flawed implementation : use this.name and this.age to create the
// new Animal instead of using the name and age parameters
public Animal createWithNewNameAndAge(String name, int age) {
return new Animal(this.name, this.age, this.favoriteFood);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getFavoriteFood() {
return favoriteFood;
}
@Override
public String toString() {
return "Animal [name=" + name + ", age=" + age + ", favoriteFood=" + favoriteFood + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((favoriteFood == null) ? 0 : favoriteFood.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Animal)) return false;
Animal other = (Animal) obj;
return age == other.age && favoriteFood.equals(other.favoriteFood) &&
name.equals(other.name);
}
}
JUnit 4.11+(或 JUnit 5)既作为测试运行器又作为断言工具
@Test
void assertListNotEquals_JUnit_way() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assert.assertNotEquals(scoubi, littleScoubi);
}
测试按预期失败,但提供给开发人员的原因确实没有帮助。它只是说值应该不同并输出在实际 Animal
参数上调用的 toString()
结果:
java.lang.AssertionError:值应该不同。实际:org.junit.Assert.fail(Assert.java:88) 中的动物 [name=scoubi, age=10, favoriteFood=hay]
好的,对象不相等。但是问题出在哪里?
测试方法中哪个字段的值不正确?一 ?二 ?所有这些?
要发现它,您必须深入研究 createWithNewNameAndAge()
实现/使用调试器,而测试 API 会更加友好,如果它能让我们区分预期和获得的差异。
JUnit 4.11 作为测试运行器和一个测试 Matcher API 作为断言工具
这里是相同的测试场景,但使用 AssertJ(一个优秀的测试匹配器 API)来断言 Animal
状态::
import org.assertj.core.api.Assertions;
@Test
void assertListNotEquals_AssertJ() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assertions.assertThat(littleScoubi)
.extracting(Animal::getName, Animal::getAge, Animal::getFavoriteFood)
.containsExactly("little scoubi", 1, "hay");
}
当然,测试仍然失败,但这次明确说明了原因:
java.lang.AssertionError: Expecting: <["scoubi", 10, "hay"]> to contain exactly (and in the order): <["little scoubi", 1, "hay"]> 但有些元素没有发现:<["little scoubi", 1]> 和其他人没有预料到: <["scoubi", 10]> at junit5.MyTest.assertListNotEquals_AssertJ(MyTest.java:26)
我们可以看到,对于返回的 Animal 的 Animal::getName, Animal::getAge, Animal::getFavoriteFood
值,我们期望有这些值:
"little scoubi", 1, "hay"
但我们有这些价值观:
"scoubi", 10, "hay"
所以我们知道在哪里调查:name
和 age
的值不正确。此外,在 Animal::getFavoriteFood()
的断言中指定 hay
值的事实还允许更精细地断言返回的 Animal
。我们希望某些属性的对象不同,但不一定对每个属性都相同。
因此,毫无疑问,使用匹配器 API 更加清晰和灵活。
Modulo API 一致性,为什么 JUnit 不提供 assertNotEquals()
与 JUnit 从未提供类似方法的原因相同
assertStringMatchesTheRegex(regex, str) 与 assertStringDoesntMatchTheRegex(regex, str)
assertStringBeginsWith(prefix, str) 与 assertStringDoesntBeginWith(prefix, str)
即,为断言逻辑中可能需要的各种事物提供特定的断言方法是没有止境的!
提供可组合的测试原语(如 equalTo(...)
、is(...)
、not(...)
、regex(...)
)要好得多,并让程序员将它们拼凑在一起,以提高可读性和完整性。
assertNotEquals()
。assertNotEqual
”,我想说这是因为它是一个专门的断言,不像assertEquals
那样经常需要,因此可以通过通用的assertFalse
来表达。assertThat
比有限的一组assert*
方法更具表现力。因此,您可以在一行中表达确切的约束,让它(几乎)读起来像一个英文句子并且在断言失败时得到一个有意义的消息。诚然,这并不总是一个杀手级功能,但是当您多次看到它的实际应用时,您会看到它增加了多少价值。assertThat
比assert*
更具表现力,但我不认为它比你可以在assert*
表达式内部和外部放置的 java 表达式更具表现力(毕竟我可以表达任何东西在java代码中)。这是我开始使用流式 API 时遇到的一个普遍问题 - 每个基本上都是您必须学习的新 DSL(当我们都已经知道 Java 的时候!)。我想 Hamcrest 现在已经无处不在了,所以期望人们知道它是合理的。我要演戏...