简体   繁体   中英

Why this assertThat assertion throws AssertionError?

Creating tests for an assignment, I'm getting an strange AssertionError exception.

I have changed it until i got to a simple case:

List<Integer> elements= new ArrayList<Integer>();
elements.add(1);
elements.add(2);
elements.add(3);

Permutation p2 = new Permutation(elements);
Permutation p1 = new Permutation(elements);

assertThat(p2, equalTo(p1));

Permutation.java:

public class Permutation {

  private List<Integer> elements;

  public Permutation(List<Integer> elements) {
    this.elements = elements;
  }

public boolean equals(Permutacion permutation){
  if ( this.elements.size() != permutation.elements.size()){
    return false;
  }

  Iterator<Integer> iterThis = this.elements.iterator();
  Iterator<Integer> iterOther = permutation.elements.iterator();
  while ( iterThis.hasNext() && iterOther.hasNext()){
    if ( iterThis.next() != iterOther.next()){
      return false;
    }
  }

  return true;

}

Digging in both junit and hamcrest source code I have found that junit assertThat only calls matches on the matcher.

The matches method in this case is:

public boolean matches(Object arg) {
    return areEqual(arg, object);
}

private static boolean areEqual(Object o1, Object o2) {
    if (o1 == null) {
        return o2 == null;
    } else if (o2 != null && isArray(o1)) {
        return isArray(o2) && areArraysEqual(o1, o2);
    } else {
        return o1.equals(o2);
    }
}

Where arg should be "p2" and object should be "p1".

(It can be browsed at Hamcrest repository )

The results of the comparisons in areEqual method, using debugger inspection, are:

"p2 == null"                    false   
"p1 != null"                    true    
"p2.getClass().isArray()"       false   
"p2.equals(p1)"                 true    
"equalTo(p1).matches(p2)"       false   

So as you can see, the code should reach the last else condition and return true ( p2.equals(p1) ), but equalTo(p1).matches(p2) returns false

Thanks for your help

Why would you expect p2.equals(p1) to return true ? You haven't overridden equals in the Permutation class, so it will use reference identity by default. You need to override equals (and hashCode , in general, to match equals) to indicate what you mean by equality when it comes to permutations.

EDIT: Now you've posted more code, it's clearer what's going on. Your equals method has this signature:

public boolean equals(Permutacion permutation){

That doesn't override Object.equals , which is what the matcher will be using. Instead, it overloads it - introducing a new method, which is what is being called in the debugger inspection. If you write:

Object o1 = p1;

then p2.equals(o1) will show false in the debugger too - and that's effectively what the matcher is doing. Your equals method should look something like:

@Override
public boolean equals(Object other)
{
    if (other == null || other.getClass() != this.getClass())
    {
        return false;
    }
    Permutation otherPermutation = (Permutation) other;

    // List.equals should do the right thing here
    return elements.equals(otherPermutation.elements);
}

(You should also override hashCode in a way which corresponds to this.)

Additionally:

  • Either consider the case where elements is null, or validate it in the constructor
  • Equality is simplest to define in final classes
  • As you're not making a defensive copy of the list, it's possible for it to be mutated by the caller after construction; that could lead to problems if you ever use the permutation as a key in a map, for example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM