나는 OP 관점에 전적으로 동의합니다. Assert.assertFalse(expected.equals(actual))
불평등을 표현하는 자연스러운 방법은 아닙니다.
그러나 나는 Assert.assertEquals()
, Assert.assertNotEquals()
작동하지만 테스트가 실제로 주장하는 것을 문서화하고 어설 션이 실패함에 따라 이해 / 디버그하는 것은 사용자 친화적이지 않다고 주장합니다.
JUnit 4.11과 JUnit 5는 Assert.assertNotEquals()
(Assertions.assertNotEquals()
JUnit 5에서) 제공하지만 실제로는 사용하지 않습니다.
대안으로, 객체의 상태를 주장하기 위해 나는 일반적으로 객체 상태를 쉽게 파헤치며 어설 션의 의도를 명확하게 문서화하고 어설 션 실패의 원인을 이해하는 데 매우 친숙한 matcher API를 사용합니다.
다음은 예입니다. 이름과 나이를 변경하면서 가장 좋아하는 음식을 유지하여 새 Animal 객체를 만드는 방법 인
테스트 할 Animal 클래스가 있다고 가정 createWithNewNameAndAge()
합니다. 원본과 새 객체가 다르다고 주장하는 데
사용한다고 가정 Assert.assertNotEquals()
합니다.
다음은 결함이있는 Animal 클래스입니다 createWithNewNameAndAge()
.
public class Animal {
private String name;
private int age;
private String favoriteFood;
public Animal(String name, int age, String favoriteFood) {
this.name = name;
this.age = age;
this.favoriteFood = favoriteFood;
}
// Flawed implementation : use this.name and this.age to create the
// new Animal instead of using the name and age parameters
public Animal createWithNewNameAndAge(String name, int age) {
return new Animal(this.name, this.age, this.favoriteFood);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getFavoriteFood() {
return favoriteFood;
}
@Override
public String toString() {
return "Animal [name=" + name + ", age=" + age + ", favoriteFood=" + favoriteFood + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((favoriteFood == null) ? 0 : favoriteFood.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Animal)) return false;
Animal other = (Animal) obj;
return age == other.age && favoriteFood.equals(other.favoriteFood) &&
name.equals(other.name);
}
}
테스트 러너 및 어설 션 도구 인 JUnit 4.11+ (또는 JUnit 5)
@Test
void assertListNotEquals_JUnit_way() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assert.assertNotEquals(scoubi, littleScoubi);
}
테스트가 예상대로 실패하지만 개발자에게 제공된 원인은 실제로 도움이되지 않습니다. 그냥 값이 달라야하고 toString()
실제 Animal
매개 변수 에서 호출 된 결과를 출력한다고 말합니다 .
java.lang.AssertionError : 값이 달라야합니다. 실제 : 동물
[이름 = scoubi, 나이 = 10, 가장 좋아하는 음식 = hay]
org.junit.Assert.fail (Assert.java:88)에서
좋아, 개체가 같지 않습니다. 그러나 문제는 어디에 있습니까?
테스트 된 방법에서 어떤 필드가 올바르게 평가되지 않습니까? 하나? 둘? 그들 모두?
그것을 발견하기 위해서는 createWithNewNameAndAge()
구현에서 디버거를 사용하고 디버거를 사용해야합니다. 테스트 API가 우리에게 기대되는 것과 얻는 것의 차이를 만들면 훨씬 친숙합니다.
테스트 러너 인 JUnit 4.11 및 어설 션 도구 인 테스트 매처 API
다음은 테스트 시나리오와 동일하지만 AssertJ (우수한 테스트 매처 API)를 사용하여 Animal
상태 를 확인합니다 . :
import org.assertj.core.api.Assertions;
@Test
void assertListNotEquals_AssertJ() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assertions.assertThat(littleScoubi)
.extracting(Animal::getName, Animal::getAge, Animal::getFavoriteFood)
.containsExactly("little scoubi", 1, "hay");
}
물론 테스트는 여전히 실패하지만 이번에는 그 이유가 명확하게 명시되어 있습니다.
java.lang.AssertionError :
기대 :
<[ "scoubi", 10, "hay"]>
정확히 (그리고 같은 순서로) 포함하기 :
<[ "little scoubi", 1, "hay"]>
그러나 일부 요소가 발견되지 않았습니다.
<[ "little scoubi", 1]>
다른 사람들은 예상하지 못했습니다.
<[ "scoubi", 10]>
junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java:26)에서
우리는 대한 것을 읽을 수 있습니다 Animal::getName, Animal::getAge, Animal::getFavoriteFood
반환 된 동물의 값, 우리는 이러한 가치가 기대 :
"little scoubi", 1, "hay"
그러나 우리는 다음과 같은 가치를 가지고 있습니다.
"scoubi", 10, "hay"
우리는 어디에서 조사를 알 수 있도록 : name
그리고 age
제대로 평가되지 않습니다. 또한 hay
어설 션에 값 을 지정 Animal::getFavoriteFood()
하면 반환 된을보다 세밀하게 어설 션 할 수 있습니다 Animal
. 우리는 객체가 일부 속성에 대해 동일하지는 않지만 모든 속성에 대해 반드시 필요한 것은 아닙니다.
따라서 matcher API를 사용하는 것이 훨씬 명확하고 유연합니다.