代码之家  ›  专栏  ›  技术社区  ›  Chris B

为什么JUnit不提供assertNotEquals方法?

  •  407
  • Chris B  · 技术社区  · 17 年前

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

    它提供 assertNotSame (对应于 assertSame ) assertFalse (对应于 assertTrue ,所以奇怪的是他们没有费心包括 assertNotEqual .

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

    10 回复  |  直到 17 年前
        1
  •  389
  •   Joachim Sauer    13 年前

    我建议你用新的 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.*;
    
        2
  •  144
  •   Stefan Birkner    10 年前

    有一个 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;
    
        3
  •  48
  •   k1eran    11 年前

    我也想知道。断言的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

        4
  •  12
  •   Bernhard Bodenstorfer    15 年前

    我认为,断言notequal的缺失确实是一种不对称,使junit的学习能力有所下降。请记住,在添加方法时,这是一个很好的例子,至少对我来说,这会降低API的复杂性:对称有助于控制更大的空间。 我的猜测是,遗漏的原因可能是调用该方法的人太少了。然而,我记得有一段时间,即使断言错误也不存在;因此,我有一个积极的期望,即考虑到这不是一个困难的方法,最终可能会添加该方法;即使我承认有许多解决方法,甚至是优雅的方法。

        5
  •  7
  •   user903724    14 年前

    我很晚才来参加这个聚会,但我发现表格:

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

    可用于大多数“不等于”的情况。

    int status = doSomething() ; // expected to return 123
    assertTrue("doSomething() returned unexpected status", status != 123 ) ;
    
        6
  •  4
  •   Akshay Vijay Jain    10 年前

    我在JAVA 8环境下使用JunIT4.12进行JUnit工作

    对我来说:即使在我使用
    import org.junit.Assert;

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

    Assert.assertNotEquals("addb", string);

    所以如果你面对的问题是 assertNotEqual 无法识别,然后将其更改为 Assert.assertNotEquals(,); 它应该能解决你的问题

        7
  •  3
  •   Mark Levison    13 年前

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

    详细示例:

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

    VS

    assertNotEqual(1, winningBidderId);
    

    遗憾的是,由于Eclipse默认不包括JUnit4.11,所以您必须是冗长的。

    注意,我不认为“1”需要用integer.valueof()包装,但是因为我刚从.NET返回,所以不依赖于我的正确性。

        8
  •  1
  •   Chris Kelly    15 年前

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

    如果使用assertfalse,则只会在报告中得到断言失败。即丢失故障原因信息。

        9
  •  0
  •   davidxxx    8 年前

    我完全同意OP的观点。 Assert.assertFalse(expected.equals(actual)) 不是表达不平等的自然方式。
    但我认为 Assert.assertEquals() , Assert.assertNotEquals() 工作,但不便于用户记录测试实际断言的内容,以及在断言失败时理解/调试。
    所以是的,Junit 4.11和Junit 5提供了 assert.assertNotEquals()。 ( Assertions.assertNotEquals() 但我确实避免使用它们。

    另外,为了断言一个对象的状态,我通常使用一个容易进入对象状态的matcher API,它清楚地记录了断言的意图,并且非常便于用户理解断言失败的原因。

    下面是一个例子。
    假设我有一个动物课,我想测试 createWithNewNameAndAge() 方法,通过改变动物的名称和年龄,但保留其最喜欢的食物来创建新的动物对象的方法。
    假设我使用 assert.assertNotEquals()。 断言原始对象和新对象是不同的。
    这是一个有缺陷的动物类 使用新名称和GE()创建 :

    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 参数:

    断言错误:值应该不同。实际:动物

    [姓名=Scoubi,年龄=10,FavoriteFood=Hay]

    在Org.jUnit中。Asvest.Fault(Asvest.java:88)

    好的,对象不相等。但问题在哪里呢?
    测试方法中哪个字段的值不正确?一个?两个?都是吗?
    要发现它,你必须在 使用新名称和GE()创建 实现/使用一个调试器,而测试API将更加友好,如果它可以使我们区分预期的和得到的。


    JUnit4.11作为测试运行程序,测试匹配器API作为断言工具

    这里是相同的测试场景,但是它使用断言j(一个优秀的测试匹配器API)来声明 动物 状态:

    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.断言错误:

    期望:

    <[“Scoubi”,10,“Hay”]>

    完全包含(并且顺序相同):

    <[“小史考比”,1,“干草”]>

    但没有发现一些元素:

    <[“小史考比”,1]>

    其他人则没有预料到:

    <[“Scoubi”,10]>

    在JunIT5.5.MyTest.AdjtListNoqQualsAsjtJ(MyTest.java:26)

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

    "little scoubi", 1, "hay" 
    

    但我们有这些价值观:

    "scoubi", 10, "hay"
    

    所以我们知道在哪里调查: name age 价值不正确。 此外,指定 hay 断言的价值 Animal::getFavoriteFood() 还允许更精细地断言返回的 动物 . 我们希望某些属性的对象不相同,但不一定每个属性都相同。
    因此,使用MatcherAPI显然更加清晰和灵活。

        10
  •  -2
  •   fatuhoku    13 年前

    modulo api一致性,为什么junit没有提供 assertNotEquals() 也是Junit从未提供类似方法的原因

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

    也就是说,为断言逻辑中可能需要的类型提供特定的断言方法是没有尽头的!

    更好的方法是提供可组合的测试原语,比如 equalTo(...) , is(...) , not(...) , regex(...) 让程序员把它们拼凑在一起,以获得更好的可读性和理智性。