有人知道为什么 JUnit 4 提供assertEquals(foo,bar)
但不提供assertNotEqual(foo,bar)
方法吗?
它提供assertNotSame
(对应assertSame
)和assertFalse
(对应assertTrue
),所以他们没有费心包括assertNotEqual
.
顺便说一句,我知道 JUnit-addons 提供了我正在寻找的方法。我只是出于好奇而问。
我建议您使用较新的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.*;
assertNotEquals
JUnit 4.11 中有一个: 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 第 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(,);
应该可以解决您的问题
人们想要 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()
(Assertions.assertNotEquals()
在 JUnit 5 中),但我真的避免使用它们。
作为替代方案,我通常使用匹配器 API 来断言对象的状态,该 API 可以轻松挖掘对象状态,清楚地记录断言的意图,并且对于理解断言失败的原因非常用户友好。
这是一个例子。
假设我有一个 Animal 类,我想测试该createWithNewNameAndAge()
方法,该方法通过更改名称和年龄但保留其最喜欢的食物来创建新的 Animal 对象。
假设我用 Assert.assertNotEquals()
断言原始对象和新对象不同。
这是 Animal 类,它的实现有缺陷createWithNewNameAndAge()
:
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);
}
测试按预期失败,但提供给开发人员的原因确实没有帮助。它只是说值应该不同并输出toString()
在实际Animal
参数上调用的结果:
java.lang.AssertionError:值应该不同。实际:动物
[姓名=scoubi,年龄=10,喜爱食物=干草]
在 org.junit.Assert.fail(Assert.java:88)
好的,对象不相等。但问题出在哪里?
测试方法中哪个字段的值不正确?一 ?二 ?他们都是 ?
要发现它,您必须深入研究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:
期待:
<["scoubi", 10, "干草"]>
准确地包含(并且以相同的顺序):
<["小史考比", 1, "干草"]>
但没有找到一些元素:
<["小史考比", 1]>
和其他人没有预料到:
<["scoubi", 10]>
在 junit5.MyTest.assertListNotEquals_AssertJ(MyTest.java:26)
我们可以读到对于Animal::getName, Animal::getAge, Animal::getFavoriteFood
返回的 Animal 的值,我们期望有这些值:
"little scoubi", 1, "hay"
但我们有这些价值观:
"scoubi", 10, "hay"
所以我们知道在哪里调查 :name
并且age
没有正确评估。此外,hay
在 的断言中指定值的事实Animal::getFavoriteFood()
还允许更精细地断言返回的Animal
. 我们希望对象对于某些属性不一样,但不一定对于每个属性。
所以毫无疑问,使用匹配器 API 更加清晰和灵活。
Modulo API 一致性,为什么 JUnit 不提供assertNotEquals()
与 JUnit 从未提供类似方法的原因是一样的
assertStringMatchesTheRegex(regex, str)
对比assertStringDoesntMatchTheRegex(regex, str)
assertStringBeginsWith(prefix, str)
对比assertStringDoesntBeginWith(prefix, str)
即,为断言逻辑中可能需要的各种事物提供特定的断言方法是没有止境的!
最好提供可组合的测试原语,如equalTo(...)
, is(...)
, not(...)
,regex(...)
并让程序员将它们拼凑在一起以获得更高的可读性和完整性。