TotT: 테스트에 로직을 넣지 마세요
원문: https://testing.googleblog.com/2014/07/testing-on-toilet-dont-put-logic-in.html (Translated by Google Gemini)
프로그래밍 언어는 우리에게 많은 표현력을 제공합니다. 연산자와 조건문 같은 개념들은 광범위한 입력을 처리하는 프로그램을 작성할 수 있게 해주는 중요한 도구입니다. 하지만 이러한 유연성은 복잡성 증가라는 대가를 치르게 하여, 우리 프로그램을 이해하기 어렵게 만듭니다.
프로덕션 코드와 달리, 테스트에서는 유연성보다 단순성이 더 중요합니다. 대부분의 단위 테스트는 단일의, 알려진 입력이 단일의, 알려진 출력을 생성하는지 검증합니다. 테스트는 입력과 출력을 계산하는 대신 직접 명시함으로써 복잡성을 피할 수 있습니다. 그렇지 않으면 테스트 자체에 버그가 생기기 쉽습니다.
간단한 예를 살펴보겠습니다. 이 테스트가 올바르게 보이나요?
@Test
public void shouldNavigateToPhotosPage() {
String baseUrl = "http://plus.google.com/";
Navigator nav = new Navigator(baseUrl);
nav.goToPhotosPage();
assertEquals(baseUrl + "/u/0/photos", nav.getCurrentUrl());
}
작성자는 공유 접두사를 변수에 저장하여 중복을 피하려고 하고 있습니다. 단일 문자열 연결을 수행하는 것이 그리 나빠 보이지는 않지만, 변수를 인라인으로 처리하여 테스트를 단순화하면 어떻게 될까요?
@Test
public void shouldNavigateToPhotosPage() {
Navigator nav = new Navigator("http://plus.google.com/");
nav.goToPhotosPage();
assertEquals("http://plus.google.com//u/0/photos", nav.getCurrentUrl()); // 이런!
}
테스트에서 불필요한 계산을 제거하고 나니, 버그가 명확해졌습니다. URL에 슬래시가 두 개 있는 것을 기대하고 있었던 것입니다! 이 테스트는 실패하거나, (더 나쁘게는) 프로덕션 코드에 동일한 버그가 있다면 잘못 통과할 것입니다. 만약 우리가 입력과 출력을 계산하려고 시도하는 대신 직접 명시했다면 이런 코드는 절대 작성하지 않았을 것입니다. 그리고 이것은 매우 간단한 예일 뿐입니다. 테스트가 더 많은 연산자를 추가하거나 루프와 조건문을 포함하면, 그것이 올바르다고 확신하기는 점점 더 어려워집니다.
이것을 다른 방식으로 말하면, 프로덕션 코드는 주어진 입력에 대한 출력을 계산하는 일반적인 전략을 설명하는 반면, 테스트는 입력/출력 쌍의 구체적인 예시라는 것입니다 (여기서 출력은 다른 클래스와의 상호작용 검증과 같은 부수 효과를 포함할 수 있습니다). 그것을 계산하는 데 필요한 로직이 매우 복잡하더라도, 입력/출력 쌍이 올바른지 아닌지는 보통 쉽게 알 수 있습니다. 예를 들어, 주어진 서버 응답에 대해 자바스크립트 함수가 생성할 정확한 DOM을 상상하기는 어렵습니다. 따라서 그러한 함수에 대한 이상적인 테스트는 기대하는 출력 HTML을 포함하는 문자열과 그냥 비교하는 것입니다.
테스트에 자체적인 로직이 정말로 필요한 경우, 그러한 로직은 종종 테스트 본문에서 벗어나 유틸리티 및 헬퍼 함수로 옮겨져야 합니다. 그러한 헬퍼는 꽤 복잡해질 수 있으므로, 사소하지 않은 테스트 유틸리티는 자체적인 테스트를 갖는 것이 보통 좋은 생각입니다.