为什么JUnit不提供assertNotEquals方法?


429

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

它提供了assertNotSame(对应于assertSame)和assertFalse(对应于assertTrue),因此它们似乎没有包含在内就显得很奇怪assertNotEqual

顺便说一句,我知道JUnit插件提供了我正在寻找的方法。我只是出于好奇而问。


至少从JUnit 4.12开始,提供了assertNotEquals。 junit.org/junit4/javadoc/4.12/org/junit/…–
WebF0x

Answers:


403

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

134
我很欣赏的指针替代的断言语法,但在其他地方指着不回答,为什么从来没有提供的JUnit assertNotEquals()
SEH

14
@seh:我读它的方式不是关于历史利益,而是关于在JUnit测试中表达“这两个对象不相等”的断言的方法。我回答了。考虑到“为什么/是否没有assertNotEqual”,我想说这是因为它是一个专门的断言,不需要那么频繁assertEquals,因此可以通过泛型来表示assertFalse
约阿希姆·绍尔

21
“选择您认为最可读的那个”。读写单元测试的人是程序员。他们真的发现它比assertNotEqual(objectUnderTest,someOtherObject)或assertFalse(objectUnderTest.equals(someOtherObject))更具可读性吗?我对花哨的匹配器API并不信服-对于程序员来说,探索/发现如何使用它们似乎要困难得多……
bacar 2012年

@bacar:有人断言这基本上是风格问题。但是assertThat,与assert*可用的有限方法相比,它更具表达力。因此,您可以在一行中表达确切的约束,使其(几乎)像英语句子一样阅读, 在断言失败时获得有意义的消息。当然,这并不总是一个杀手级功能,但是当您几次看到它在行动中时,您会看到它增加了多少价值。
约阿希姆·绍尔

5
@Joachim我同意它assertThat比更具表达力assert*,但是我不认为它比您可以在表达式内外添加的Java表达式更具表达assert*力(毕竟我可以用Java代码表达任何东西)。我已经开始遇到流利风格的API,这是一个普遍的问题-每个基本都是您必须学习的新DSL(当我们都已经知道Java的时候!)。我认为Hamcrest现在已经无处不在,可以期望人们知道了。我会玩...
bacar

154

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

1
记住,其中一个jmock(2.6.0)专家文物泄漏了junit-dep的旧版本,而junit-dep却具有一个Assert-class,但没有assertNotEquals。使用常春藤时最好将其排除在外。
gkephorus 2014年

7
我正在使用4.12,但仍然找不到assertNotEqual。:s
Mubashar 2015年

49

我也想知道 声明的API不是很对称。为了测试对象是否相同,它提供了assertSameassertNotSame

当然,写起来并不太长:

assertFalse(foo.equals(bar));

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

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

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


19
但这用处不大,因为JUnit无法生成有用的失败消息,例如,告诉您foo和bar的值不相等。真正的失败原因被隐藏起来,并变成一个简单的布尔值。
本·詹姆斯

3
我完全同意。特别是assertFalse需要适当的消息参数来产生输出,以告知真正出了问题的地方。
2011年

我认为这对于当前的文本测试很有用。Thnx
Marouane Gazanayi 2012年

文本的问题在于,随着代码的发展,它会过时。
Mark Levison

13

我认为没有assertNotEqual确实是不对称的,这使JUnit的学习性降低了。请注意,这在添加方法将减少API的复杂性时是一个很好的情况,至少对我而言:对称性有助于确定更大的空间。我的猜测是,省略的原因可能是要求该方法的人太少。但是,我记得有一次甚至没有assertFalse的时候。因此,我对此有积极的期望,因为该方法并不困难;即使我承认有很多解决方法,甚至是优雅的方法。


7

我来晚了,但是发现表格是:

static void assertTrue(java.lang.String message, boolean condition) 

可以在大多数“不平等”情况下工作。

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;

4
虽然这样做确实可行,但问题是,如果断言失败,它将仅说“确实为真,但为假”或其他一些不清楚的陈述。什么是伟大的是,如果它预期不是123,但123
隐形拉比

6

我正在使用jUnit4.12在Java 8环境中开发JUnit

对我来说:即使我使用了编译器,也无法找到方法assertNotEquals
import org.junit.Assert;

所以我改变了
assertNotEquals("addb", string);

Assert.assertNotEquals("addb", string);

因此,如果您遇到有关assertNotEqual无法识别的问题,则将其更改为Assert.assertNotEquals(,);它可以解决您的问题


1
这是因为方法是静态的,因此您必须静态地导入它们。使用它import static org.junit.Assert.*;,您将可以使用没有类名的所有断言。
汤姆·斯通

3

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

详细示例:

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

assertNotEqual(1, winningBidderId);

可悲的是,由于Eclipse默认不包含JUnit 4.11,因此您必须很冗长。

请注意,我认为不需要将'1'包裹在Integer.valueOf()中,但是由于我刚从.NET返回,因此请不要指望我的正确性。


1

最好将Hamcrest用于否定性断言,而不要使用assertFalse,因为在前一种情况下,测试报告将显示断言失败的区别。

如果使用assertFalse,则报表中只会出现断言失败。即丢失有关故障原因的信息。


1

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

assertTrue(obj1.equals(obj2));

和:

assertFalse(obj1.equals(obj2));

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


0

我完全同意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()断言原始对象和新对象是不同的。
这是动物类,其实现存在缺陷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。我们希望某些属性的对象不相同,但并非每个属性都相同。
因此,毫无疑问,使用Matcher API更加清晰和灵活。


-1

Modulo API的一致性,为什么JUnit不提供assertNotEquals()与JUnit从未提供类似方法的原因相同

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

也就是说,为您可能在断言逻辑中想要的事情提供特定的断言方法是没有止境的!

远东更好地提供组合的测试原语一样equalTo(...)is(...)not(...)regex(...)并让程序员一块那些一起,而不是更多的可读性和理智。


3
好吧,由于某种原因,assertEquals()存在。它不是必须的,但是确实如此。问题是缺乏对称性-为什么assertEquals存在,但不存在?
foo 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.