Почему этот assertThat утверждение выдает AssertionError? - PullRequest
2 голосов
/ 12 декабря 2011

Создавая тесты для задания, я получаю странное AssertionError исключение.

Я менял его, пока не попал в простой случай:

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;

}

Копание в исходном коде junit и hamcrest Я обнаружил, что junit assertThat вызывает только совпадения на совпадении.

В этом случае метод matchs:

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);
    }
}

Где аргумент arg должен быть "p2", а объект должен быть "p1".

(его можно просмотреть в хранилище Hamcrest )

РезультатыСравнения в методе areEqual, использующие проверку отладчика, таковы:

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

Итак, как вы можете видеть, код должен достичь последнего условия else и вернуть true (p2.equals(p1)), но equalTo(p1).matches(p2) возвращаетfalse

Спасибо за помощь

1 Ответ

7 голосов
/ 12 декабря 2011

Почему вы ожидаете, что p2.equals(p1) вернет true? Вы не переопределили equals в классе Permutation, поэтому он будет использовать ссылочный идентификатор по умолчанию. Вам нужно переопределить equalshashCode, как правило, чтобы соответствовать равным), чтобы указать, что вы подразумеваете под равенством, когда речь идет о перестановках.

РЕДАКТИРОВАТЬ: Теперь вы разместили больше кода, стало понятнее, что происходит. Ваш метод equals имеет эту подпись:

public boolean equals(Permutacion permutation){

То, что не переопределяет Object.equals, это то, что будет использовать сопоставитель. Вместо этого он перегружает это - вводит новый метод, который вызывается при проверке отладчика. Если вы напишите:

Object o1 = p1;

тогда p2.equals(o1) также покажет false в отладчике - и это фактически то, что делает сопоставитель. Ваш метод equals должен выглядеть примерно так:

@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);
}

(Вы также должны переопределить hashCode способом, который соответствует этому.)

Дополнительно:

  • Либо рассмотрите случай, когда elements является нулем, либо подтвердите его в конструкторе
  • Равенство проще всего определить в выпускных классах
  • Поскольку вы не делаете защитную копию списка, возможно, он будет видоизменен вызывающим абонентом после создания; это может привести к проблемам, если вы когда-нибудь будете использовать перестановку в качестве ключа на карте, например.
...