2017-02-22 34 views
3

Поскольку определение equals() и hashCode() только для целей тестирования рассматривается как запах кода, я предпочитаю использовать ReflectionEquals или пользовательские сопоставления для сравнения объектов при проведении модульного тестирования.Как сравнить списки пользовательских классов без определения equals() и hashCode()?

Однако я не знаю, как использовать ReflectionEquals или пользовательские сопоставления при сравнении списков пользовательских классов.

Например, как я могу утверждать следующий код без определения equals() и hashCode() (может быть, использовать только ReflectionEquals или пользовательские совпадения)?

// When 
List<Record> actual = sut.findBySomeId(); 

// Then 
List<Record> expected = asList(
    aRecord()...build(), 
    aRecord()...build() 
); 
assertThat(expected, /* how to compare? */); 

ответ

1

Я хотел бы сказать, что с помощью отражения для проверки equals/hashCode еще один код запах сам по себе.

С Matcher, вы должны сделать что-то подобное (я использовал полное имя для ясности, используйте import вместо): это будет проверять, что value из result такой же, как один из expected. Вы можете добавить столько полей, сколько вам нужно.

assertThat(result, new org.hamcrest.BaseMatcher<MyObject>() { 
    public boolean matches(MyObject o) { 
    return java.lang.Objects.equals(o.getValue(), expected.getValue()); 
    } 
}); 

Если вы не хотите, чтобы создать геттер для поля вашего класса, а затем использовать видимость по умолчанию (то, что это делает гуавы и они аннотирование таких полей с @VisibleForTesting).

Вы также можете взглянуть на AssertJ, чтобы создать пользовательские и плавные совпадения (он работает более или менее одинаково).

2

В библиотеке Hamcrest имеется большой выбор шаблонов для составления утверждений о типах коллекций. В частности, могут быть использованы совпадения, которые могут использовать сами согласные (hasItem, hasItems, contains и containsAnyOrder). Я хотел бы использовать TypeSafeMatcher для проверки элементов в коллекциях.

Я оставлю вас, чтобы решить, какой из них лучше всего подходит для ваших потребностей, но я буду использовать contains для моего примера:

List<Record> actual = sut.findBySomeId(); 

Record expected1 = aRecord()...build(); 
Record expected2 = aRecord()...build(); 

assertThat(actual, contains(matchingRecord(expected1), matchingRecord(expected2)); 

... 

// somewhere the test has access to it 
private Matcher<Record> matchingRecord(Record expected) { 
    return new TypeSafeMatcher<Record>() { 
     public boolean matchesSafely(Record actual) { 
      // perform tests and return result, e.g. 
      return actual.getValue() == expected.getValue(); 
     } 

     public void describeMismatchSafely(Record record, Description mismatchDescription) { 
      mismatchDescription.appendText("give a meaningful message"); 
     } 
    }; 

} 
+0

Думаю, я бы добавил, что нет причин, по которым вы не могли бы использовать 'ReflectionEquals' в методе' matchesSafely' 'Matcher' –

0

Я также столкнулся с этим вопросом в прошлом, и я полностью согласен с вы, что реализация equals() и hashCode() только для целей тестирования - это запах кода. Я не использую библиотеку Hamcrest, поэтому я дам вам специальное решение, которое я успешно использую в своих проектах, и что касается его использования, я им очень доволен.

public static <E, A> void assertListEquals(BiConsumer<E, A> asserter, List<E> expected, List<A> actual) throws AssertionError { 
    assertEquals(
      "Lists have different sizes. Expected list: " + expected + ", actual list: " + actual, 
      expected.size(), 
      actual.size()); 

    for (int i = 0; i < expected.size(); i++) { 
     try { 
      asserter.accept(expected.get(i), actual.get(i)); 
     } catch (AssertionError e) { 
      throw e; 
     } 
    } 
} 

Как вы можете видеть, BiConsumer используется для того, чтобы применить логику для проверки равенства двух объектов. Таким образом, этот должен быть реализован в тестовом классе. Преимущество этого в том, что вы можете определить поля интересующего вас класса при сравнении двух объектов.

Посмотрите на использование: Сначала у нас есть пользовательский класс, f.e.: Человек

public class Person { 
    private String firstName; 
    private String lastName; 
    private int age; 

    public Person(String firstName, String lastName, int age) { 
     this.firstName = firstName; 
     this.lastName = lastName; 
     this.age = age; 
    } 

    public String getFirstName() { 
     return firstName; 
    } 

    public String getLastName() { 
     return lastName; 
    } 

    public int getAge() { 
     return age; 
    } 
} 

Тогда давайте посмотрим, метод испытания:

@Test 
public void peopleLiveInTheVillage_findPeople_peopleAreFound() throws Exception { 
    //Arrange 
    List<Person> expectedPeople = Arrays.asList(
      new Person("Lewis", "Smith", 20), 
      new Person("Steven", "Richard", 25), 
      new Person("Richie", "Rich", 30)); 

    //Act 
    List<Person> actualPeople = sut.findPeople(); 

    //Assert 
    assertListEquals(
      (e, a) -> assertPersonEquals(e, a), 
      expectedPeople, 
      actualPeople); 
} 

private void assertPersonEquals(Person expected, Person actual) { 
    assertEquals(expected.getFirstName(), actual.getFirstName()); 
    assertEquals(expected.getLastName(), actual.getLastName()); 
    assertEquals(expected.getAge(), actual.getAge()); 
} 

Поскольку я большой поклонник тестов (и TDD) Я всегда пишу тонны тестов, так что я всегда делаю вспомогательный метод, где я оберните BiConsumer:

private void assertPeopleEqual(List<Person> expectedPeople, List<Person> actualPeople) throws AssertionError { 
    assertListEquals(
      (e, a) -> assertPersonEqual(e, a), 
      expectedPeople, 
      actualPeople); 
} 

Надеюсь, это поможет, ура!

 Смежные вопросы

  • Нет связанных вопросов^_^