2017-02-22 9 views
3

테스트 목적으로 만 equals()hashCode()을 정의하는 것은 코드 냄새로 간주되므로 단위 테스트를 수행하는 동안 ReflectionEquals 또는 사용자 지정 matchers를 사용하여 개체를 비교하는 것을 선호합니다.equals() 및 hashCode()를 정의하지 않고 사용자 정의 클래스 목록을 비교하는 방법은 무엇입니까?

그러나 사용자 정의 클래스 목록을 비교할 때 ReflectionEquals 또는 사용자 지정 matchers를 사용하는 방법을 알지 못합니다.

예를 들어, equals()hashCode()을 정의하지 않고 (ReflectionEquals 또는 맞춤 matcher 만 사용할 수 있음) 다음 코드를 어떻게 지정합니까?

// 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를 사용하는) 그런 일을 수행해야합니다 :이 resultvalueexpected의 것과 동일한 지 확인합니다. 필요한만큼 필드를 추가 할 수 있습니다.

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

당신이 다음 클래스의 필드의 게터를 만드는 기본 가시성을 사용하지 않으려면

(즉 무엇을하는 구아바 수행하고 그들이 @VisibleForTesting와 같은 필드에 주석).

사용자 정의 유창한 matchers를 만들려면 AssertJ을 참조하십시오 (다소 비슷하게 작동 함).

2

Hamcrest 라이브러리에는 콜렉션 유형에 대한 어서션을 만들기위한 훌륭한 선택기가 있습니다. 특히, hasItem, hasItems, containscontainsAnyOrder 정규식은 matcher 자체를 사용할 수 있습니다. (나는 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

생각 나는 'Matcher'의'matchesSafely' 메소드에서'ReflectionEquals'를 사용할 수 없다는 이유를 추가했습니다. –

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

그런 다음 시험 방법 볼 수 있습니다 : 나는 테스트 (및 TDD)의 큰 팬이다 이후로 난 항상 도우미 메서드를 만들 수 있도록 항상 테스트 톤을 쓰기

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

을 어디 BiConsumer 포장 :

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

나는 이것이 도움이되기를 바랍니다, 건배!