java 当预期和实际看起来相同时,为什么我会收到 AssertionError?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41118063/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 05:41:31  来源:igfitidea点击:

Why am I getting an AssertionError when the expected and actual look identical?

javacollectionsjunit

提问by Vishal Kotcherlakota

I'm trying to answer the following question from Cracking the Coding Interview. The code below is part of a project on GitHub, here.

我正在尝试回答来自Cracking the Coding Interview的以下问题。下面的代码是 GitHub 上一个项目的一部分,这里

Given a binary search tree, design an algorithm which creates a linked list of all the nodes at each depth (i.e., if you have a tree with depth D, you'll have D linked lists).

给定一棵二叉搜索树,设计一个算法,该算法创建一个包含每个深度的所有节点的链表(即,如果您有一棵深度为 D 的树,您将拥有 D 个链表)。

Being a good little developer, I wrote a unit test to check this.

作为一名优秀的小开发人员,我编写了一个单元测试来检查这一点。

@Test
public void testGetValuesAtEachLevel() {
    Integer[] treeValues = {
            1, 2, 3, 4, 5,
            6, 7, 8, 9, 10,
            11, 12, 13, 14, 15
    };

    tree = new GenericBinaryTree<>(treeValues);

    Integer[][] expectedArray = {
            { 1 },
            { 2, 3 },
            { 4, 5, 6, 7 },
            { 8, 9, 10, 11, 12, 13, 14, 15 }
    };

    List<List<Node<Integer>>> expectedList = new ArrayList<>(4);
    for (Integer[] level : expectedArray) {
        List<Node<Integer>> list = new LinkedList<>();
        for (Integer value : level) {
            list.add(new Node<>(value));
        }
        expectedList.add(list);
    }

    assertEquals(expectedList, tree.getValuesAtEachLevel());
}

And here's the code.

这是代码。

List<List<Node<T>>> getValuesAtEachLevel() {
    List<List<Node<T>>> results = new ArrayList<>();

    List<Node<T>> firstLevel = new LinkedList<>();
    firstLevel.add(getRoot());
    results.add(firstLevel);

    loadAtLevel(results, 1);

    return results;
}

private void loadAtLevel(List<List<Node<T>>> list, int level) {
    List<Node<T>> levelList = new LinkedList<Node<T>>();

    for (Node<T> node : list.get(level - 1)) {
        if (node.left() != null) levelList.add(node.left());
        if (node.right() != null) levelList.add(node.right());
    }

    if (levelList.isEmpty()) return;

    level++;
    list.add(levelList);
    loadAtLevel(list, level);
}

Imagine my surprise when the unit test fails with the following error:

想象一下当单元测试失败并出现以下错误时我的惊讶:

java.lang.AssertionError: expected: 
java.util.ArrayList<[[1], [2, 3], [4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14, 15]]> but was: 
java.util.ArrayList<[[1], [2, 3], [4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14, 15]]>

The string representations are identical, so I can't really figure out what happened here. As a matter of fact, if I change the assertion to this, the test passes:

字符串表示是相同的,所以我无法弄清楚这里发生了什么。事实上,如果我将断言更改为此,则测试通过:

    assertEquals(expectedList.toString(), tree.getValuesAtEachLevel().toString());

I'm getting the sense I'm about to learn something awfully valuable about interfaces and objects. What did I do wrong here?

我感觉我将要学习一些关于接口和对象的非常有价值的东西。我在这里做错了什么?

回答by Naman

As pointed by @Jon Skeet in the comment as well, you need to override the equalsand hashCodefor your Nodeclass, to perform a comparison (equals) on its instances.

正如@Jon Skeet 在评论中所指出的,您需要为您的类覆盖equals和,以对其实例执行比较 ( )。hashCodeNodeequals

A sample(autogenerated using intelliJ default) could be -

示例(使用 intelliJ 默认自动生成)可能是 -

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Node<?> node = (Node<?>) o;

    if (left != null ? !left.equals(node.left) : node.left != null) return false;
    if (right != null ? !right.equals(node.right) : node.right != null) return false;
    if (parent != null ? !parent.equals(node.parent) : node.parent != null) return false;
    return value != null ? value.equals(node.value) : node.value == null;

}

@Override
public int hashCode() {
    int result = left != null ? left.hashCode() : 0;
    result = 31 * result + (right != null ? right.hashCode() : 0);
    result = 31 * result + (parent != null ? parent.hashCode() : 0);
    result = 31 * result + (value != null ? value.hashCode() : 0);
    return result;
}


On the other hand why

另一方面为什么

assertEquals(expectedList.toString(),tree.getValuesAtEachLevel().toString());

works.

作品。

It is because what you are tyring to assert finally here is compare two Stringinstances which has its @Overriden equalsdefinition here

这是因为您最终要在这里断言的是比较两个在此处String具有@Overridenequals定义的实例

回答by Dariusz

Most likely because

很可能是因为

new Node().equals(new Node()) == false

Override equals()and hashCode()in Node

覆盖equals()hashCode()进入Node

回答by davidxxx

Asserting that String representations of two objects are equals and these objects are equal are two distinct things.
The Object equality relies on the boolean equals(Object o)method from the Objectclass.
int hashcode()can improve the performance if overrided in a symmetric way to equals(Object o)method rather than returning a unique value in any case(by default) but for the assertion, it doesn't change the result. A good practice is overriding both when you override equals().

断言两个对象的 String 表示相等并且这些对象相等是两个不同的事情。
对象相等依赖于类中的boolean equals(Object o)方法Object
int hashcode()如果以对称方式覆盖equals(Object o)方法而不是在任何情况下(默认情况下)返回唯一值,则可以提高性能,但对于断言,它不会改变结果。当您覆盖equals().

In your case, you have multiple ways of proceeding. I propose you 3 common ways:

在您的情况下,您有多种处理方式。我建议你3种常用方法:

  • overriding equals()and hashcode()it their properties allow to define them in a way where it defines really the identity of the object. overriding equals()and hashcode()to make successful assertion in unit test is not enough to override the method.
    Besides, equals()overriding may be suitable for this test and not suitable for another test.
    The implementation of equals()and hashcode()method has to be conform to the way to identify the object and how you use this class in your application, not only for unit testing assertions.
    Here is another post where the question is asked : Should one override equals method for asserting the object equality in a unit test?

  • doing the assertions in the loop.

  • 覆盖equals()hashcode()它自己的特性允许在它真正定义了对象身份的方式来定义他们。覆盖equals()hashcode()在单元测试中成功断言不足以覆盖该方法。
    此外,equals()覆盖可能适用于本次测试,而不适用于其他测试。和方法
    的实现必须符合识别对象的方式以及您在应用程序中使用此类的方式,而不仅仅是单元测试断言。 这是另一篇提出问题的帖子:是否应该覆盖等于方法以在单元测试中断言对象相等?equals()hashcode()

  • 在循环中做断言。

For example, loop on the actual result and do the assertion node by node by using the 2D array you created for the expected result :

例如,循环实际结果并使用您为预期结果创建的二维数组逐节点执行断言:

int i=0;

for (List<Node<Integer>>> listOfNodes :  tree.getValuesAtEachLevel()) {

    int j=0;

    for (Node node : listOfNodes ) {
        list.add(new Node<>(value));
       // I called the method getValue() because I don't know the getter
        Assert.assertEquals(expectedArray[i][j], node.getValue(); 
         // You could also display a JUnit failure message if you want
        j++; 
    }

    i++;
}

It way of proceeding may be clumsy and time consuming if you have to do assertions for your List of List in several other scenarios and the way to do the assertions is very different. But with a suitable code refactoring , we can extract util asserting methods in a reusable way in many scenarios.

如果您必须在其他几种情况下对 List of List 进行断言,并且进行断言的方式非常不同,则其处理方式可能既笨拙又耗时。但是通过适当的代码重构,我们可以在许多场景中以可重用的方式提取 util 断言方法。

  • A alternative solution I like a lot is reflection for the assertions.
  • 我非常喜欢的一个替代解决方案是对断言的反射。

the Unitils library provides this feature and other things.

Unitils 库提供了这个特性和其他东西。

http://www.unitils.org/tutorial-reflectionassert.html

http://www.unitils.org/tutorial-reflectionassert.html

With Unitils, you could do :

使用 Unitils,您可以:

ReflectionAssert.assertReflectionEquals(expectedList, tree.getValuesAtEachLevel());

without overiding equals(Object o)and hashcode().
The other advantage of Unitils is the rather friendly message if the assertion fails.

没有覆盖equals(Object o)hashcode()
Unitils 的另一个优点是断言失败时的相当友好的消息。