首页 文章

为什么JUnit不提供assertNotEquals方法?

提问于
浏览
402

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

它提供 assertNotSame (对应于 assertSame )和 assertFalse (对应于 assertTrue ),所以看起来很奇怪,他们没有打扰包括 assertNotEqual .

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

10 回答

  • -2

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

    如果使用assertFalse,则只会在报告中出现断言失败 . 即丢失有关失败原因的信息 .

  • 12

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

    作为替代方案,为了断言对象的状态,我通常使用一个可以轻松挖掘对象状态的匹配器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+ (or JUnit 5) both as test runner and assertion tool

    @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:值应该不同 . 实际:动物[name = scoubi,年龄= 10,favoriteFood = hay] org.junit.Assert.fail(Assert.java:88)

    好吧,对象不等于 . 但问题出在哪里?
    在测试方法中哪个字段没有正确估值?一个?两个?他们都是 ?
    要发现它,你必须深入研究 createWithNewNameAndAge() 实现/使用调试器,而测试API会更友好,如果它能使我们之间的差异是预期,哪些得到了 .


    JUnit 4.11 as test runner and a test Matcher API as assertion tool

    这里是相同的测试场景,但使用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]>和其他人没有预料到:<[“scoubi”,10]>在junit5.MyTest.assertListNotEquals_AssertJ(MyTest.java:26)

    我们可以读取返回的Animal的 Animal::getName, Animal::getAge, Animal::getFavoriteFood 值,我们希望有这些值:

    "little scoubi", 1, "hay"
    

    但我们有这些 Value 观:

    "scoubi", 10, "hay"
    

    所以我们知道调查的地方: nameage 没有正确估价 . 此外,在 Animal::getFavoriteFood() 的断言中指定 hay 值的事实也允许更精细地断言返回的 Animal . 我们希望对象对于某些属性不同,但不一定对每个属性都相同 .
    所以,使用匹配器API更加清晰和灵活 .

  • 0

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

    JUnit 4.11中有 assertNotEqualshttps://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and-assume

    import static org.junit.Assert.assertNotEquals;
    
  • 3

    我不知道 . Assert的API不是很对称;为了测试对象是否相同,它提供 assertSameassertNotSame .

    当然,写作时间不会太长:

    assertFalse(foo.equals(bar));
    

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

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

    这当然是如此乏味,最好滚动你自己的 assertNotEqual . 幸运的是,将来它可能是JUnit的一部分:JUnit issue 22

  • 387

    我认为缺少assertNotEqual确实是不对称的,并且使JUnit的学习成果不那么容易 . 请注意,添加方法时这是一个简洁的案例会降低API的复杂性,至少对我而言:对称性有助于统治更大的空间 . 我的猜测是遗漏的原因可能是人们要求这种方法的人太少了 . 然而,我记得甚至断言不存在的时候;因此,我有一个积极的期望,最终可能会添加该方法,因为它不是一个困难的方法;即使我承认有很多变通方法,甚至是优雅的变通办法 .

  • 48

    我很晚才参加这个派对,但我发现这个形式:

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

    可以使其适用于大多数“不平等”的案件 .

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

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

    详细的例子:

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

    assertNotEqual(1, winningBidderId);
    

    遗憾的是,由于Eclipse默认情况下不包含JUnit 4.11,因此您必须详细 .

    警告我不认为'1'需要包含在Integer.valueOf()中,但由于我是从.NET返回的,所以不要指望我的正确性 .

  • 142

    我正在使用jUnit4.12在java 8环境中使用JUnit

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

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

    Assert.assertNotEquals("addb", string);

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

  • 7

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

    • assertStringMatchesTheRegex(regex, str)assertStringDoesntMatchTheRegex(regex, str)

    • assertStringBeginsWith(prefix, str)assertStringDoesntBeginWith(prefix, str)

    也就是说,为断言逻辑中你可能想要的东西提供特定的断言方法是没有意义的!

    更好的是提供可组合的测试原语,如 equalTo(...)is(...)not(...)regex(...) ,让程序员将这些组合在一起,以获得更多的可读性和理智性 .

相关问题