Java — тест, который проверяет, правильно ли скопированы значения HashMap в ArrayList, возвращает false

#java

#java

Вопрос:

Я должен написать метод, который возвращает все значения HashMap в виде списка.

Для контекста, вот классы, которые я использую. Вопрос класса — это вопрос с несколькими вариантами ответов, похожий на тест, который имеет несколько ответов, отображенных в HashMap, с ключевыми значениями (a), b), c) и т.д.).

Класс Answer имеет следующий интерфейс:

 public class Answer{
    private String answerText= "";
    private boolean correct;

    public Answer(String answerText, boolean correct) {
      this.correct = correct;
      this.answerText = answerText;
    }
...
  

Класс Question имеет следующий интерфейс:

 public class Question{
    private String text;
    private double points;
    private HashMap<String, Answer> answers= new HashMap<> ();

    public void addAnswer(String id, String text, boolean correct)
       answers.put(id, new Answer(text, correct));
   }
...
  

У каждого вопроса есть text (фактический вопрос), points (количество баллов, которые вы получаете за него) и HashMap<String, Answer> answers где String находится ключ ответа (a), b), c) и так далее), и Answer это фактический ответ (поэтому каждый ключ соответствует одному вопросу).

Теперь мне дали задание написать метод в классе Question , который возвращает все ответы в виде списка. Мне был предоставлен JUnit-тест для этого метода, и вот код теста:

 @Test
    public void testGetListAnswers(){
        Question question = new Question("Some question", 2);
        question.addAnswer("a", "first answer", true);
        question.addAnswer("b", "second answer", false);
        List<Answer> answers= question.getListAnswers();
        assertAll(
                () -> assertEquals(2, answers.size()),
                () -> assertTrue(answers.contains(new Answer("first answer", true))),
                () -> assertTrue(answers.contains(new Answer("second answer", false))),
                () -> assertFalse(answers.contains(new Answer("package", false)))
        );
    }
  

И вот фактический метод, который я написал:

  public List<Answer> getListAnswers() {

        ArrayList<Answer> list = new ArrayList<>();

        for(Map.Entry<String, Answer> map : answers.entrySet()) {
            Answer o =  map.getValue();
            list.add(o);
        }
        return list;
    }
  

Что я пробовал

Я написал простой метод, который использует цикл for для перебора набора и просто добавляет каждое значение HashMap в ArrayList .

Однако тест завершается неудачей.

Комментарии:

1. Есть ли у вашего Answer класса equals и hashCode реализован ли он?

2. @user7 это не так!

3. @user7 может что-то делать… Утверждения проверяют, содержит ли список ответов новые ответы. Это не удастся, потому что это разные экземпляры!

4. Я реализовал equals и hashCode , и тест неожиданно (для меня) выдает true.

5. @l0ner9 проверьте мой ответ, возможно, это объясняет, почему тест проходит

Ответ №1:

Ваш метод звучит нормально. Однако утверждения теста делают

 assertTrue(answers.contains(new Answer("first answer", true))
  

В методе contains в списке javadocs записывается следующее

Возвращает true, если этот список содержит указанный элемент. Более формально, возвращает true тогда и только тогда, когда этот список содержит хотя бы один элемент e, такой, что (o==null ? e==null : o.равно(e)) .

Поскольку они создают новый экземпляр ответа, а в Answer нет реализации equals, вызывается equals из базового класса объектов. Это вернет false, потому что метод basic equals сравнивает ссылки.

Метод equals для class Object реализует наиболее различающее возможное отношение эквивалентности к объектам; то есть для любых ненулевых ссылочных значений x и y этот метод возвращает true тогда и только тогда, когда x и y ссылаются на один и тот же объект (x == y имеет значение true).

Если вы реализуете и переопределяете метод equals (и hashCode) в ответе, чтобы сравнить текст ответа и исправить, тесты должны пройти.

Быстрое равенство (сгенерированное intellij IDEA) может выглядеть следующим образом

 @Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Answer)) return false;
    Answer that = (Answer) o;
    return correct == that.correct amp;amp;
            answerText.equals(that.answerText);
}