ChatGPT解决这个技术问题 Extra ChatGPT

为什么 JUnit 不提供 assertNotEquals 方法?

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

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

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

至少从 JUnit 4.12 开始,提供了 assertNotEquals。 junit.org/junit4/javadoc/4.12/org/junit/…
较新版本的 Junit 提供此功能。这是 how to use assertEquals and assertNotEquals 上的一个很好的例子

J
Joachim Sauer

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

我很欣赏指向备用断言语法的指针,但指向别处并不能回答 为什么 JUnit 从未提供 assertNotEquals()
@seh:我读它的方式不是关于历史兴趣,而是关于在 JUnit 测试中制定“这两个对象不相等”的断言的方法。我这样回答。考虑到“为什么没有 assertNotEqual”,我想说这是因为它是一个专门的断言,不像 assertEquals 那样经常需要,因此可以通过通用的 assertFalse 来表达。
“选择你认为最易读的一个”。阅读和编写单元测试的人是程序员。他们真的觉得这比 assertNotEqual(objectUnderTest, someOtherObject) 或 assertFalse(objectUnderTest.equals(someOtherObject)) 更具可读性吗?我不相信花哨的匹配器 API - 程序员探索/发现如何使用它们似乎要困难得多......
@bacar:对于某些人来说,这基本上是风格问题。但是 assertThat 比有限的一组 assert* 方法更具表现力。因此,您可以在一行中表达确切的约束,让它(几乎)读起来像一个英文句子并且在断言失败时得到一个有意义的消息。诚然,这并不总是一个杀手级功能,但是当您多次看到它的实际应用时,您会看到它增加了多少价值。
@Joachim 我同意 assertThatassert* 更具表现力,但我不认为它比你可以在 assert* 表达式内部和外部放置的 java 表达式更具表现力(毕竟我可以表达任何东西在java代码中)。这是我开始使用流式 API 时遇到的一个普遍问题 - 每个基本上都是您必须学习的新 DSL(当我们都已经知道 Java 的时候!)。我想 Hamcrest 现在已经无处不在了,所以期望人们知道它是合理的。我要演戏...
S
Stefan Birkner

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;

请注意,其中一个 jmock (2.6.0) maven artefacts 泄漏了 junit-dep 的旧版本,该版本又具有没有 assertNotEquals 的 Assert 类。使用常春藤时最好排除它。
我正在使用 4.12 但仍然无法找到 assertNotEqual。 :秒
k
k1eran

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


但这不太有用,因为 JUnit 无法生成有用的失败消息来告诉您,例如 foo 和 bar 的值不相等。真正的失败原因被隐藏并变成了一个简单的布尔值。
我完全同意。尤其是 assertFalse 需要适当的消息参数来产生输出来告诉真正出了什么问题。
我认为这对于文本呈现测试很有用。谢谢
文本的问题在于它会随着代码的发展而过时。
B
Bernhard Bodenstorfer

我认为缺少 assertNotEqual 确实是一种不对称,并且使 JUnit 的可学习性降低了一些。请注意,添加方法会降低 API 的复杂性时,这是一个很好的案例,至少对我而言:对称有助于统治更大的空间。我的猜测是,省略的原因可能是调用该方法的人太少了。然而,我记得有一段时间甚至 assertFalse 都不存在。因此,我对最终可能会添加该方法抱有积极的期望,因为它并不困难;尽管我承认有许多变通方法,甚至是优雅的变通方法。


u
user903724

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

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

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

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

虽然这确实有效,但问题是如果断言失败,它只会说“预期为真,但为假”,或其他一些不清楚的陈述。如果预期不是 123,而是 123,那就太好了。
A
Akshay Vijay Jain

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

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

所以我将
assertNotEquals("addb", string);
改为
Assert.assertNotEquals("addb", string);

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


那是因为方法是静态的,你必须静态地导入它们。使用这个 import static org.junit.Assert.*;,您将能够使用所有没有类名的断言。
M
Mark Levison

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

详细示例:

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

对比

assertNotEqual(1, winningBidderId);

遗憾的是,由于默认情况下 Eclipse 不包含 JUnit 4.11,因此您必须是冗长的。

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


C
Chris Kelly

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

如果你使用 assertFalse,你只会在报告中得到一个断言失败。即丢失有关故障原因的信息。


W
Willempie

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

assertTrue(obj1.equals(obj2));

和:

assertFalse(obj1.equals(obj2));

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


d
davidxxx

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

作为替代方案,我通常使用匹配器 API 来断言对象的状态,该 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+(或 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() 结果:

java.lang.AssertionError:值应该不同。实际:org.junit.Assert.fail(Assert.java:88) 中的动物 [name=scoubi, age=10, favoriteFood=hay]

好的,对象不相等。但是问题出在哪里?
测试方法中哪个字段的值不正确?一 ?二 ?所有这些?
要发现它,您必须深入研究 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: Expecting: <["scoubi", 10, "hay"]> to contain exactly (and in the order): <["little scoubi", 1, "hay"]> 但有些元素没有发现:<["little scoubi", 1]> 和其他人没有预料到: <["scoubi", 10]> at 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 更加清晰和灵活。


f
fatuhoku

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

assertStringMatchesTheRegex(regex, str) 与 assertStringDoesntMatchTheRegex(regex, str)

assertStringBeginsWith(prefix, str) 与 assertStringDoesntBeginWith(prefix, str)

即,为断言逻辑中可能需要的各种事物提供特定的断言方法是没有止境的!

提供可组合的测试原语(如 equalTo(...)is(...)not(...)regex(...))要好得多,并让程序员将它们拼凑在一起,以提高可读性和完整性。


好吧,由于某种原因, assertEquals() 存在。它没有必要,但它确实如此。问题是关于缺乏对称性 - 为什么 assertEquals 存在但它的对应物不存在?