박종훈 기술블로그

기초가 탄탄한 자바 개발자가 되기 위해 알아야할 테스트 기초 (testing fundamentals)

원문 : Well Grounded Java Developer - ch13 testing fundamentals


이 장에서 다루는 것

  • 테스트를 하는 이유
  • 테스트 하는 방법
  • 테스트 주도 개발
  • 테스트 대역
  • JUnit 4에서 5로 migration

이 글에서 전체를 다루지는 않고 13.2 까지 하여 테스트를 하는 이유와 테스트 하는 방법에 대한 부분까지 다룬다.


최근 몇 년 사이 프로그래밍 분야에서 테스트 자동화를 개발 과정의 당연한 부분으로 받아들이게 되었습니다. 테스트는 시스템과 동작을 보장하기 위해서 개발자의 로컬 환경과 지속적 통합(CI, continuous integration) 환경에서 수행됩니다. 테스트에 대한 다양한 도구, 접근 방식, 철학들도 폭발적으로 나오고 있습니다.

모든 기술이 그러하듯 만능인 것은 없습니다. 모든 상황을 다 테스트를 할 수는 없습니다. 그렇기 때문에 왜 테스팅을 해야하는지에 대해 이해하는 것이 중요합니다. 그래야 어떻게 테스트를 할 지에 대해서 더 좋은 결정을 할 수 있거든요.

13.1 테스트 하는 이유

사실 테스트 를 하는 이유는 매우 다양합니다. 대략적으로 다음과 같습니다.

목록을 읽어보면 알겠지만 코드를 테스트한다는 것이 그렇게 간단하지는 않습니다. 따라서 우리는 테스트를 할 때 스스로에게 다음과 같이 질문을 해야합니다.

엣지 케이스와 코너 케이스
엣지 케이스 : 매개변수의 값이 극단적인(최대 또는 최소) 상황에서 발생하는 문제 또는 상황
코너 케이스 : 매개변수가 지정된 범위 내에 있더라도 여러 환경 변수 또는 조건이 동시에 극단적인 수준에 있을 때 나타나는 문제 또는 상황

13.2 테스트 하는 방법

다양한 유형의 테스트에 대해서 논의할 때 가장 대표적으로 사용되는 도구는 테스트 피라미드 입니다. (Mike Cohn의 Succeeding with Agile에서 처음으로 나옴)

testing-pyramid

테스트 피라미드는 테스트 유형에 따른 비용의 균형에 대해서 설명합니다. 유형의 정확한 경계에 대해서는 여전히 논쟁이 벌어지고 있지만 핵심 아이디어는 매우 유용합니다.

[Note] 테스트 유형은 테스트 도구에 결정되는 것은 아닙니다. Junit을 사용한다고 해서 단위 테스트를 작성하는 것이 아니며 명세 라이브러리 (specification library)를 사용한다 해서 이해하기 쉬운 인수 테스트를 생성한다고는 보장하지 않습니다. 테스트 유형은 우리가 어떤 부분을 테스트하고 증명하려는지에 대한 것 입니다.

명세 라이브러리가 무엇을 이야기 하는건지 모르겠어서 ChatGPT에게 물어보니 JBehave, Cucumber 를 예시로 들었다. (BDD 도구들을 명세 라이브러리로 보는 것 같다.) JBehave의 공식 홈페이지에서는 BDD를 “BDD is an evolution of test-driven development (TDD) and acceptance-test driven design” 이라고 소개하고 있긴 하다.
BDD 에 대한 것은 이전에 BDD를 소개합니다. 라는 글을 통해 정리해두었다.

단위 테스트는 피라미드의 아래 부분에 있습니다. 단위 테스트는 시스템의 한 부분에만 집중하는 테스트입니다. 한 측면이라는 것은 어떻게 정할 수 있을까요? 외부 종속성과 어떻게 관련되는지를 보면 알 수 있습니다. 예를들어 테스트가 결과에 대한 일부 논리를 수행하기 전에 데이터베이스를 호출했다면 이는 더 이상 하나를 테스트하는 것이 아닙니다. (데이터베이스 검색에 대한 테스트와 로직의 동작여부에 대한 테스트를 수행해야 합니다.) 이러한 외부 종속성은 일반적으로 네트워크 서비스나 파일도 포함합니다.

한 부분에 집중하는 원칙을 위반하는 것을 피하기 위해 일반적으로 테스트 대역을 사용합니다. 예를 들면 단위 테스트 안에서 실제 데이터베이스를 사용하지 않고 페이크 객체를 사용하는 것입니다. 이 부분에 대해서는 다음 섹션에서 더 자세히 알아보겠습니다. 그러나 기본 아이디어는 이 대역들은 다양한 특징을 가지고 있으며 잘 사용하기 위해서는 많은 사항을 고려해야 한다는 것입니다.

단위 테스트는 여러가지 매력적인 점들이 있습니다. 그로인해 테스트 피라미드에서 가장 큰 부분을 차지하고 있습니다. 이유를 설명하자면 다음과 같습니다.

좋아 보이지만, 그렇다고 단위 테스트만 작성하는건 좋지 않습니다. 단위 테스트가 모든 규모의 코드에 적합한 것은 아니기 때문에 다음의 문제들이 발생할 수 있습니다.

테스트 피라미드의 다음 단계인 통합 테스트 는 유닛 테스트의 종속성 제한에서 벗어난 테스트입니다. 통합테스트는 경계를 넘어 시스템의 다양한 부분들이 원활하게 통합되도록 하는데 중점을 둡니다.

단위 테스트와 마찬가지로 통합 테스트도 시스템의 일부만 선택하여 실행할 수 있습니다. 외부 서비스와 같은 일부 종속성은 여전히 테스트 대역으로 대체하되, 데이터베이스와 같은 종속성은 테스트에 포함시킵니다.

이 부분은 8장 통합 테스트를 하는 이유 (2) - 언제 목을 써야할까? + 예시 글의 관리 의존성과 비관리 의존성 부분을 참고하면 좋을 것 같다.

중요한 점은 단위를 넘어섰다는 것입니다.

단위 테스트와 통합 테스트의 경계는 모호할 수 있습니다. 하지만 명확하게 통합 테스트인 예시를 들자면 다음과 같습니다.

통합 서비스는 다음과 같은 장점이 있습니다.

물론 모든것에는 트레이드오프가 있습니다. 통합 테스트는 잘 관리되지 않으면 다음과 같은 이유로 오히려 문제가 되기도 합니다.

이러한 부분들에도 불구하고 통합 테스트는 테스트 영역에서 중요한 부분입니다.

엔드 투 엔드 (End to End) 테스트 는 통합 테스트를 넘어 시스템에 대한 사용자 경험 전체를 복제하는 것을 목표로 합니다. 웹 브라우저나, 기타 어플리케이션을 프로그래밍을 통해 제어하거나, 테스트 환경에서 완전히 배포된 서비스 인스턴스를 활용하는 것을 의미할 수 있습니다. 엔드 투 엔드 테스트는 다음과 같은 이점을 가집니다.

그러나 엔드 투 엔드 테스트에는 다음과 같은 어려움도 있습니다.

그러면 각 테스트 유형 간의 올바른 테스트 비율은 어떻게 될까요? 사실 정해진 답변은 없습니다. 각 프로젝트와 시스템의 요구에 다라 다릅니다. 하지만 피라미드는 시스템의 각 부분을 어떻게 테스트할지 장단점을 고려하는데 도움이 될 수 있습니다.

유일한 방법은 아니겠지만, 기초가 탄탄한(well-grounded) 개발자는 시스템이 커짐에 따라 테스트 수준을 명확하게 유지하는데 테스트 주도 개발이 도움이 된다는 것을 발견할 것입니다.


다음 글에서는 테스트 주도 개발에 대해서 이어서 정리한다.