두 목록을 JUnit 테스트하는 방법 동일한 순서로 동일한 요소를 포함합니까?
문맥
클래스에 대한 간단한 JUnit 테스트를 작성 중 MyObject
입니다.
A MyObject
는 String 의 varargs를 취하는 정적 팩토리 메서드에서 만들 수 있습니다 .
MyObject.ofComponents("Uno", "Dos", "Tres");
이 존재하는 동안 언제든지 MyObject
클라이언트는 메서드를 통해 List <E> 형식으로 생성 된 매개 변수를 검사 할 수 있습니다 .getComponents()
.
myObject.ofComponents(); // -> List<String>: { "Uno", "Dos", "Tres" }
즉, MyObject
둘 다 자신을 존재하게 만든 매개 변수 목록을 기억하고 노출합니다. 이 계약에 대한 자세한 내용 :
- 의 순서는
getComponents
객체 생성을 위해 선택한 순서 와 동일합니다. - 중복 된 후속 문자열 구성 요소가 허용되고 순서대로 유지됩니다.
- 의 동작
null
이 정의되지 않았습니다 (다른 코드null
는 공장에 도착 하지 못하도록 보장 ). - 개체 인스턴스화 후 구성 요소 목록을 변경할 방법이 없습니다.
StringMyObject
목록에서 생성 하고 .NET을 통해 동일한 목록을 반환 할 수 있는지 확인 하는 간단한 테스트를 작성 중 입니다. 이 작업을 즉시 수행하지만 실제 코드 경로에서 멀리 떨어져 있어야합니다 ..getComponents()
암호
여기 내 시도 :
List<String> argumentComponents = Lists.newArrayList("One", "Two", "Three");
List<String> returnedComponents =
MyObject.ofComponents(
argumentComponents.toArray(new String[argumentComponents.size()]))
.getComponents();
assertTrue(Iterables.elementsEqual(argumentComponents, returnedComponents));
질문
- 빌드 경로에 라이브러리 가있는 경우 Google Guava가이
Iterables.elementsEqual()
두 목록을 비교하는 가장 좋은 방법인가요? 이것은 제가 고민해 왔던 것입니다. Iterable <E> .. 크기를 확인한 다음 실행.equals()
.. 또는 인터넷 검색에서 제안하는 다른 방법 을 반복하는 이 도우미 메서드를 사용해야 합니까? 단위 테스트에 대한 목록을 비교하는 표준 방법은 무엇입니까?
내가 얻고 싶은 선택적 통찰력
저는 Hamcrest를 사용하는 것을 선호합니다. 실패시 훨씬 더 나은 출력을 제공하기 때문입니다.
Assert.assertThat(listUnderTest,
IsIterableContainingInOrder.contains(expectedList.toArray()));
보고 대신
expected true, got false
그것은보고 할 것이다
expected List containing "1, 2, 3, ..." got list containing "4, 6, 2, ..."
IsIterableContainingInOrder.contain
Javadoc에 따르면 :
검사 된 Iterable에 대한 단일 패스가 지정된 항목의 해당 항목과 논리적으로 동일한 일련의 항목을 생성 할 때 일치하는 Iterables에 대한 일치자를 만듭니다. 긍정적 인 일치의 경우 검사 된 이터 러블은 지정된 항목의 수와 길이가 같아야합니다.
따라서에 listUnderTest
동일한 수의 요소가 있어야하며 각 요소는 예상 값과 순서대로 일치해야합니다.
왜 단순히 사용하지 List#equals
않습니까?
assertEquals(argumentComponents, imapPathComponents);
두 목록은 동일한 순서로 동일한 요소를 포함하는 경우 동일하게 정의됩니다.
List 구현의 equals () 메서드는 요소 별 비교를 수행해야합니다.
assertEquals(argumentComponents, returnedComponents);
훨씬 쉽습니다.
org.junit.Assert.assertEquals()
그리고 org.junit.Assert.assertArrayEquals()
일을하십시오.
다음 질문을 피하려면 : 순서를 무시하려면 모든 요소를 설정하고 비교하십시오. Assert.assertEquals(new HashSet<String>(one), new HashSet<String>(two))
그러나 중복을 무시하고 순서를 유지하려면 LinkedHashSet
.
또 다른 팁. 트릭 Assert.assertEquals(new HashSet<String>(one), new HashSet<String>(two))
은 비교가 실패 할 때까지 잘 작동합니다. 이 경우 세트의 순서를 거의 예측할 수 없기 때문에 (적어도 복잡한 객체의 경우) 혼란 스러울 수있는 세트의 문자열 표현에 대한 오류 메시지를 표시합니다. 그래서 내가 찾은 트릭은 컬렉션을 HashSet
. TreeSet
사용자 지정 비교기와 함께 사용할 수 있습니다 .
뛰어난 코드 가독성을 위해 Fest Assertions 는 어설 션 목록에 대한 멋진 지원을 제공 합니다.
따라서이 경우 다음과 같습니다.
Assertions.assertThat(returnedComponents).containsExactly("One", "Two", "Three");
또는 예상 목록을 배열로 만들지 만 더 명확하기 때문에 위의 접근 방식을 선호합니다.
Assertions.assertThat(returnedComponents).containsExactly(argumentComponents.toArray());
assertTrue () / assertFalse () : 반환 된 부울 결과를 주장하는 데만 사용합니다.
assertTrue (Iterables.elementsEqual (argumentComponents, returnedComponents));
사용하려는 경우 Assert.assertTrue()
또는 Assert.assertFalse()
테스트중인 메서드가 boolean
값을 반환 합니다.
메서드가 List
예상되는 요소를 포함해야하는 a와 같은 특정 항목을 반환 하므로이 assertTrue()
방식 으로을 주장 Assert.assertTrue(myActualList.containsAll(myExpectedList)
하는 것은 안티 패턴입니다.
어설 션을 작성하기 쉽게 만들지 만 테스트가 실패하면 테스트 실행자가 다음과 같은 말만하기 때문에 디버그하기가 어렵습니다.
예상
true
하지만 실제는false
Assert.assertEquals(Object, Object)
JUnit4 또는 Assertions.assertIterableEquals(Iterable, Iterable)
JUnit을 5 : 양으로 만 사용 equals()
하고 toString()
, 비교 대상의 클래스를 오버라이드 (깊이)되고
어설 션의 동등성 테스트가 의존 equals()
하고 테스트 실패 메시지 toString()
가 비교 된 객체 에 의존하기 때문에 중요 합니다.
으로 String
재정의 모두 equals()
와 toString()
,이 주장 완벽하게 유효 List<String>
로 assertEquals(Object,Object)
. 그리고이 문제에 대해 : equals()
JUnit을 사용한 테스트에서 어설 션을 더 쉽게 만들기 위해서뿐만 아니라 객체 동등성 측면에서 의미가 있기 때문에 클래스에서 재정의해야합니다 .
어설 션을 더 쉽게 만들기 위해 다른 방법이 있습니다 (다음 답변에서 볼 수 있음).
Guava는 단위 테스트 어설 션을 수행 / 구축하는 방법입니까?
빌드 경로에 라이브러리가있는 경우 Google Guava Iterables.elementsEqual ()이 두 목록을 비교하는 것이 가장 좋은 방법입니까?
전혀 그렇지 않다. Guava는 단위 테스트 어설 션을 작성하는 라이브러리가 아닙니다.
대부분의 단위 테스트를 작성하는 데 필요하지 않습니다.
단위 테스트 목록을 비교하는 표준 방법은 무엇입니까?
좋은 방법으로 나는 assertion / matcher 라이브러리를 선호합니다.
JUnit이 특정 어설 션을 수행하도록 장려 할 수는 없습니다.이 기능은 너무 적고 제한적인 기능을 제공하기 때문입니다. 깊은 동등성을 가진 어설 션 만 수행합니다.
때로는 요소의 순서를 허용하고 싶을 때도 있고, 예상되는 요소가 실제와 일치하도록 허용하고 싶을 때도 있습니다.
따라서 Hamcrest 또는 AssertJ와 같은 단위 테스트 어설 션 / 매처 라이브러리를 사용하는 것이 올바른 방법입니다.
실제 답변은 Hamcrest 솔루션을 제공합니다. 다음은 AssertJ 솔루션입니다.
org.assertj.core.api.ListAssert.containsExactly()
is what you need : it verifies that the actual group contains exactly the given values and nothing else, in order as stated :
Verifies that the actual group contains exactly the given values and nothing else, in order.
Your test could look like :
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
@Test
void ofComponent_AssertJ() throws Exception {
MyObject myObject = MyObject.ofComponents("One", "Two", "Three");
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two", "Three");
}
A AssertJ good point is that declaring a List
as expected is needless : it makes the assertion straighter and the code more readable :
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two", "Three");
And if the test fails :
// Fail : Three was not expected
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two");
you get a very clear message such as :
java.lang.AssertionError:
Expecting:
<["One", "Two", "Three"]>
to contain exactly (and in same order):
<["One", "Two"]>
but some elements were not expected:
<["Three"]>
Assertion/matcher libraries are a must because these will really further
Suppose that MyObject
doesn't store String
s but Foo
s instances such as :
public class MyFooObject {
private List<Foo> values;
@SafeVarargs
public static MyFooObject ofComponents(Foo... values) {
// ...
}
public List<Foo> getComponents(){
return new ArrayList<>(values);
}
}
That is a very common need. With AssertJ the assertion is still simple to write. Better you can assert that the list content are equal even if the class of the elements doesn't override equals()/hashCode()
while JUnit ways require that :
import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;
@Test
void ofComponent() throws Exception {
MyFooObject myObject = MyFooObject.ofComponents(new Foo(1, "One"), new Foo(2, "Two"), new Foo(3, "Three"));
Assertions.assertThat(myObject.getComponents())
.extracting(Foo::getId, Foo::getName)
.containsExactly(tuple(1, "One"),
tuple(2, "Two"),
tuple(3, "Three"));
}
- My answer about whether
Iterables.elementsEqual
is best choice:
Iterables.elementsEqual
is enough to compare 2 List
s.
Iterables.elementsEqual
is used in more general scenarios, It accepts more general types: Iterable
. That is, you could even compare a List
with a Set
. (by iterate order, it is important)
물론 ArrayList
과 LinkedList
정의가 꽤 좋은 동일 직접 등호를 호출 할 수 있습니다. 잘 정의되지 않은 목록을 사용할 때 Iterables.elementsEqual
가장 좋은 선택입니다. 한 가지 주목해야 할 점 : Iterables.elementsEqual
수락하지 않음null
List를 배열로 변환하려면 : 더
Iterables.toArray
쉽습니다.단위 테스트의 경우 테스트 케이스에 빈 목록 을 추가하는 것이 좋습니다 .
'program tip' 카테고리의 다른 글
어셈블리없는 C / C ++ 함수 정의 (0) | 2020.10.29 |
---|---|
ARC가 활성화 된 UUID 문자열 생성 (0) | 2020.10.29 |
객체에 특정 속성이 있는지 어떻게 테스트 할 수 있습니까? (0) | 2020.10.29 |
브라우저에서 운영 체제가 사용하는 소수 구분 기호를 어떻게 알 수 있습니까? (0) | 2020.10.29 |
하나의 솔루션에서 여러 프로젝트간에 스크립트를 어떻게 공유합니까? (0) | 2020.10.29 |