为什么JUnit不提供assertNotEquals方法?

有人知道为什么JUnit 4提供assertEquals(foo,bar)方法而不提供assertNotEqual(foo,bar)方法吗?

它提供了assertNotSame(对应于assertSame)和assertFalse(对应于assertTrue),所以它们没有包含assertNotEqual似乎很奇怪。

顺便说一下,我知道JUnit-addons提供了我正在寻找的方法。我只是出于好奇才问的。

194051 次浏览

我也想知道。Assert的API不是很对称;用于测试对象是否相同,它提供了assertSameassertNotSame

当然,写起来也不算太长:

assertFalse(foo.equals(bar));

不幸的是,使用这样的断言,输出中唯一有信息的部分是测试方法的名称,因此应该单独形成描述性消息:

String msg = "Expected <" + foo + "> to be unequal to <" + bar +">";
assertFalse(msg, foo.equals(bar));

这当然是非常乏味的,最好是自己滚动assertNotEqual。幸运的是,将来它可能会成为JUnit: JUnit第22期的一部分

我建议你使用更新的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.*;

对于否定的断言,最好使用Hamcrest,而不是assertFalse,因为在前者中,测试报告将显示断言失败的差异。

如果使用assertFalse,只会在报告中得到一个断言失败。例如,丢失了关于失败原因的信息。

我认为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 ) ;

在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;

人们想要assertNotEquals()的明显原因是比较内置对象,而不必首先将它们转换为完整的对象:

详细的例子:

....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....

vs。

assertNotEqual(1, winningBidderId);

不幸的是,由于Eclipse默认情况下不包括JUnit 4.11,您必须详细说明。

注意:我不认为'1'需要包装在Integer.valueOf()中,但由于我刚从。net返回,所以不要指望我的正确性。

Modulo API的一致性,为什么JUnit没有提供assertNotEquals()和为什么JUnit从来没有提供像

  • assertStringMatchesTheRegex(regex, str) vs. assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) vs. assertStringDoesntBeginWith(prefix, str)

也就是说,为断言逻辑中可能需要的东西提供特定的断言方法是无止境的!

更好的方法是提供像equalTo(...)is(...)not(...)regex(...)这样的可组合测试原语,并让程序员将它们拼凑在一起,以获得更强的可读性和完整性。

我在java 8环境下的JUnit工作,使用jUnit4.12

为我:编译器无法找到方法assertNotEquals,即使当我使用
import org.junit.Assert; < / p >

所以我把
assertNotEquals("addb", string);
改成
Assert.assertNotEquals("addb", string);

因此,如果你面临assertNotEqual无法识别的问题,那么将其更改为Assert.assertNotEquals(,);,它应该可以解决你的问题

我完全同意OP的观点。Assert.assertFalse(expected.equals(actual))不是一个表达不等式的自然方式 但我认为,除了Assert.assertEquals()之外,Assert.assertNotEquals()还可以工作,但在记录测试实际断言的内容以及在断言失败时理解/调试方面并不用户友好 所以是的JUnit 4.11和JUnit 5提供了Assert.assertNotEquals()(在JUnit 5中是Assertions.assertNotEquals()),但我真的避免使用它们。< / p >

作为替代,为了断言一个对象的状态,我通常使用一个匹配器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);
}

测试如预期的那样失败了,但是提供给开发人员的原因并没有什么帮助。它只是说值应该是不同的,并输出对实际的Animal参数调用的toString()结果:

assertionerror:值应该不同。实际:动物

[name=scoubi, age=10, favoriteFood=hay]

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, "hay"]>

精确地包含:精确地包含(以相同的顺序):

["little scoubi", 1, "hay"]>

但有些元素没有被发现:

["little scoubi", 1]>

还有一些是意料之外的:

& lt;(“scoubi”,10)>

在junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java: 26)

我们可以读到,对于返回的Animal的Animal::getName, Animal::getAge, Animal::getFavoriteFood值,我们期望有这些值:

"little scoubi", 1, "hay"

但是我们有这些价值观:

"scoubi", 10, "hay"
所以我们知道在哪里调查:nameage的值不正确。 此外,在Animal::getFavoriteFood()的断言中指定hay值的事实也允许更精确地断言返回的Animal。我们希望某些属性的对象不相同,但不一定对每个属性都相同 因此,使用匹配器API显然更加清晰和灵活

通常当我期望两个对象相等时,我这样做:

assertTrue(obj1.equals(obj2));

和:

assertFalse(obj1.equals(obj2));

当他们被认为是不平等的。我知道这不是对你问题的回答,但这是我能得到的最接近的答案。它可以帮助其他人搜索在JUnit 4.11之前的JUnit版本中可以做什么。