TotT: 상태 테스트 vs. 상호작용 테스트
원문: https://testing.googleblog.com/2013/03/testing-on-toilet-testing-state-vs.html (Translated by Google Gemini)
단위 테스트가 테스트 중인 코드가 제대로 작동하는지 확인하는 방법은 일반적으로 두 가지가 있습니다: 상태 테스트 또는 상호작용 테스트. 이들의 차이점은 무엇일까요?
상태 테스트는 테스트 중인 코드가 올바른 결과를 반환하는지 확인하는 것을 의미합니다.
public void testSortNumbers() {
NumberSorter numberSorter = new NumberSorter(quicksort, bubbleSort);
// 반환된 목록이 정렬되었는지 확인합니다. 어떤 정렬 알고리즘이 사용되었는지는 중요하지 않으며,
// 올바른 결과가 반환되기만 하면 됩니다.
assertEquals(
new ArrayList(1, 2, 3),
numberSorter.sortNumbers(new ArrayList(3, 1, 2)));
}
상호작용 테스트는 테스트 중인 코드가 특정 메서드를 올바르게 호출하는지 확인하는 것을 의미합니다.
public void testSortNumbers_quicksortIsUsed() {
// 클래스에 목(mock) 객체를 전달하고 테스트 중인 메서드를 호출합니다.
NumberSorter numberSorter = new NumberSorter(mockQuicksort, mockBubbleSort);
numberSorter.sortNumbers(new ArrayList(3, 1, 2));
// numberSorter.sortNumbers()가 quicksort를 사용했는지 확인합니다.
// mockQuicksort.sort()가 전혀 호출되지 않거나 잘못된 인수로 호출되면 (예: mockBubbleSort가 숫자를 정렬하는 데 사용된 경우)
// 테스트는 실패해야 합니다.
verify(mockQuicksort).sort(new ArrayList(3, 1, 2));
}
두 번째 테스트는 좋은 코드 커버리지를 제공할 수 있지만, 정렬이 제대로 작동하는지 여부는 알려주지 않고 quicksort.sort()가 호출되었는지 여부만 알려줍니다. 상호작용을 사용하는 테스트가 통과했다고 해서 코드가 제대로 작동한다는 의미는 아닙니다. 이것이 대부분의 경우 상호작용이 아닌 상태를 테스트해야 하는 이유입니다.
일반적으로 상호작용은 코드의 출력이 무엇인지 뿐만 아니라 출력이 어떻게 결정되는지에 따라 정확성이 달라지는 경우에 테스트해야 합니다. 위 예시에서, quicksort가 사용되는 것이 중요하다면 (예: 다른 정렬 알고리즘을 사용하면 메서드가 너무 느리게 실행될 경우) 상태 테스트에 더해 상호작용을 테스트해야 하며, 그렇지 않으면 상호작용을 사용하는 테스트는 불필요합니다.
상호작용을 테스트하고 싶은 다른 사례들은 무엇일까요?
-
테스트 중인 코드가 호출 횟수나 순서의 차이가 원치 않는 동작을 유발할 수 있는 메서드를 호출하는 경우 - 사이드이펙트(예: 이메일을 하나만 보내고 싶은 경우), 지연(예: 특정 횟수의 디스크 읽기만 발생시키고 싶은 경우) 또는 멀티스레딩 문제(예: 코드가 잘못된 순서로 일부 메서드를 호출하면 교착 상태에 빠지는 경우) - 상호작용 테스트는 이러한 메서드가 제대로 호출되지 않으면 테스트가 실패하도록 보장합니다.
-
UI의 렌더링 세부 정보가 UI 논리에서 추상화된 UI를 테스트하는 경우 - 예: MVC 또는 MVP 사용 - 컨트롤러/프레젠터 테스트에서는 뷰의 특정 메서드가 호출되었는지 여부만 중요하고 실제로 무엇이 렌더링되었는지는 중요하지 않으므로 뷰와의 상호작용을 테스트할 수 있습니다. 마찬가지로 뷰를 테스트할 때는 컨트롤러/프레젠터와의 상호작용을 테스트할 수 있습니다.