[Book - JUnit IN ACTION 3판] 5. 소프트웨어 테스트 원칙
다양한 유형의 소프트웨어와 그러한 테스트가 애플리케이션의 생애 주기에서 수행하는 역할을 살펴보자.
이 모든 내용을 왜 다 알아야 할까?
단위 테스트는 계획과 준비 없이 되는 것이 아니기 때문이다.
최고 수준의 개발자가 되기 위해서는 단위 테스트를 기능 테스트를 비롯한 다른 테스트와 구분할 수 있어야 한다.
그렇게 단위 테스트가 왜 필요한지를 이해하면 어느 정도까지 테스트 해야 하는지도 알 수 있다.
단위 테스트를 하는 이유
단위 테스트의 핵심 목표는 애플리케이션이 예상대로 작동하는지 확인하고 사전에 버그를 찾아내는 것이다.
기능 테스트로도 작동을 확인하고 버그를 찾을 수 있지만, 단위 테스트는 기능 테스트보다 훨씬 강력하고 다양한 기능을 제공한다.
또한 단위 테스트를 수행했을 때는 아래와 같은 장점이 있다.
- 단위 테스트의 장점
- 기능 테스트만 수행했을 때보다 테스트 커버리지를 높일 수 있다.
- 팀 생산성이 향상된다.
- 회귀(regression)를 사전에 발경하여 디버깅 작업을 줄일 수 있다.
- 소스를 리팩터링(refactoring)하거나 변경할 때 개발자에게 확신을 준다.
- 애플리케이션 기능 구현에 도움을 준다.
- 코드의 예상 동작을 문서화할 수 있다.
- 코드 커버리지를 비롯해 다양한 지표를 측정하게 해준다.
테스트 커버리지를 높일 수 있다
단위 테스트는 애플리케이션이 가져야 하는 첫 번째 유형의 테스트다.
단위 테스트는 기능테스트로는 수행하기 어렵거나 불가능한 오류 조건에 대해서도 쉽게 테스트할 수 있다.
팀 생산성이 향상된다
단위 테스트를 활용하면 다른 컴포넌트가 준비될 때까지 기다리지 않고도 질적으로 우수한 코드를 전달할 수 있다.
회귀를 사전에 발경하여 디버깅 작업을 줄일 수 있다
통과하는 단위 테스트 묶음은 코드가 제대로 동작하는지 확인해 주고, 기존 소스를 리팩터링하거나 새로운 기능을 추가 또는 수정할 때 개발자에게 확신을 준다.
단위 테스트 묶음을 사용하면 어디에 문제가 있는지 알 수 있고, 애플리케이션을 일일이 디버깅할 필요도 줄어든다.
기능 테스트는 유스 케이스 ‘어디엔가’ 버그가 숨어 있다는 사실을 알려주지만,
단위 테스트는 특정 메서드가 어떤 이유로 실패했는지를 구체적으로 알려 준다.
자신 있게 리팩터링할 수 있다
단위 테스트가 없으면 리팩터링을 정당화하기 어렵다.
소스를 고치는 일에는 항상 오류를 만들 위험이 내포되어 있기 때문이다.
기능 구현에 도움이 된다
단위 테스트야말로 테스트가 필요한 코드의 가장 중요한 클라이언트라고 말할 수 있다.
단위 테스트는 테스트 중인 API를 유연하게 만들고 격리된 상태에서 테스트가 가능하게끔 만들어 준다.
때로는 단위 테스트를 하기 위해 소스를 리팩터링해야 하는 경우도 있다.
즉, 단위 테스트를 잘할 수 있게 소스 코드를 짜야 한다는 TDD를 실천하게 될 것이다.
단위 테스트가 너무 길고 다루기 힘들다면 일반적으로는 테스트 대상 코드에 설계 문제가 있는 것이며 리팩터링이 필요하다.
테스트 메서드 하나가 너무 많은 기능을 테스트하고 있을 수도 있다.
격리된 상태에서 테스트가 기능을 검증하지 못한다는 건 테스트 대상 코드가 충분히 유연하지 못하며 리팩터링이 필요하다는 의미다.
코드의 예상 동작을 문서화할 수 있다
단위 테스트는 그 자체로 API 사용 예제가 되어 개발자들에게 훌륭한 설계 문서가 될 수 있다.
단위 테스트는 현재 운영 중인 코드와 함께 업데이트되므로 일반적인 설계 문서와 달리 최신 상태일 수밖에 없다.
즉, 테스트를 수행하기만 해도 유스 케이스를 명확하게 알 수 있다.
따라서 각 테스트를 실행할 때마다 구체적으로 어떤 일이 일어날지를 알 수 있으므로 테스트는 프로젝트 문서의 일부라고 할 수 있다.
코드 커버리지 및 다양한 지표를 측정할 수 있게 해 준다
단위 테스트는 버튼만 누르면 모든 프로그램이 여전히 잘 동작하는지를 알려 준다.
또한 단위 테스트는 라인별로 테스트가 실행 되었는지 되지 않았는지 보여 주는 코드 커버리지 지표도 제공하고,
도구를 사용하여 진행한 빌드에서 다음 빌드까지 테스트 통과/실패의 진행 상황도 추적할 수 있다.
테스트 성능을 모니터링하여 성능이 이전 빌드보다 떨어졌다면 테스트가 실패하도록 유도할 수도 있다.
테스트 유형
- 단위 테스트 (가장 좁은 범위)
- 통합 테스트
- 시스템 테스트
- 인수 테스트 (가장 넓은 범위)
단위 테스트는 작업 단위를 철저하게 구분하고 개별 단위에만 집중한다고 했다.
그렇다면 개별 단위를 하나의 작업 흐름으로 결합해서 테스트하는 것은 어떨까?
개별 단위로 합쳐서 테스트할 때도 정상적으로 수행될까?
단위 테스트
단위 테스트는 소스 코드의 개별 단위(메서드, 클래스)를 테스트하여 해당 코드를 사용할 수 있는지를 결정하는 소프트웨어 테스트 기법이다.
단위 테스트는 개발 과정에서 발생할 수 있는 잠재적 오류로부터 개발자를 보호하는 안전망 역할을 하므로 코드 변경을 하고자 하는 개발자에게 확신을 줄 수 있다.
좋은 단위 테스트가 있고 코드를 수정할 때마다 테스트를 실행한다면, 변경점이 기존 기능에 영향을 미치지 않는다 확신할 수 있다.
단위 테스트에서는 개별 단위를 격리하여 테스트하는 것이 중요하다.
고객 관리 시스템을 만든다고 가정하자.
먼저 고객 생성이 잘되는지부터 테스트한다.
그런 다음 고객을 조회하고 회사 데이터베이스에 고객 데이터를 추가하고 삭제하는 작업을 테스트한다.
이때 단위 테스트는 대상 범위가 가장 좁기 때문에 일부 격리된 클래스만 있으면 된다.
통합 테스트
단위 테스트는 품질 관리가 필수적이다.
그런데 서로 다른 개별 작업 단위가 하나의 작업 흐름으로 합쳐지면 어떻게 될까?
단위 테스트로 개별 클래스나 메서드 테스트가 잘되었다면,
다음은 클래스를 다른 메서드나 서비스와 연결하는 과정이 필요하다.
통합 테스트는 대상 환경에서 실행 가능한 컴포넌트 간의 상호작용을 테스트하는 것이다.
아래 표는 컴포너트 간에 상호작용하는 경우를 구분한 것이다.
상호작용 | 테스트 설명 |
---|---|
객체 | 테스트는 객체를 인스턴스화하고 다른 객체의 메서드를 호출한다. 통합 테스트를 통해 다른 클래스에 속한 객체 간에 어떻게 협력해서 문제를 해결할 수 있는지 확인할 수 있다. |
서비스 | 통합 테스트는 컨테이너가 애플리케이션을 관리하는 동안 실행된다. 그동안 애플리케이션은 데이터베이스와 연동되어 있거나 다른 외부 리소스, 장치와 통신할 수 있다. 컨테이너에 배포되는 애플리케이션을 개발할 때 통합 테스트를 활용할 수 있다. |
서브 시스템 | 계층적 애플리케이션에서는 화면을 관리하는 프런트엔드와 비즈니스 로직을 수행하는 백엔드로 구분된다. 통합 테스트를 가지고 프런트엔드를 통과한 요청이 백엔드에서 적절한 응답을 반환하는지 확인할 수 있다. 웹 인터페이스와 같은 프런트엔드와 비즈니스 로직을 수행하는 백엔드가 구분된 아키텍처를 가진 애플리케이션은 통합 테스트를 활용할 수 있다. |
객체 간 상호작용이 발생하는 지점에서 버그가 생기기 쉽다.
이상적으로는 애플리케이션 코드를 짜기 전에 통합 테스트부터 정의해야 한다.
통합 테스트 코드 작성은 개발자가 올바르게 동작하는 객체를 만드는 능력을 향상시키는 데에 큰 도움을 준다.
통합 테스트를 사용하여 고객 객체가 주문 1건당 1번만 포함되는지와 같이 고객 객체와 주문 객체 간의 협력이 잘 이루어지는지 확인할 수 있다.
시스템 테스트
시스템 테스트는 시스템이 구체화된 요구 사항을 만족하는지 평가하기 위해 완전한 통합 환경에서 수행하는 테스트를 말한다.
시스템 테스트의 목표는 통합되어 있는 단위 간의 불일치를 찾아내는 것이다.
테스트 더블(test double)이나 모의 객체(mock object)는 복잡한 실제 객체의 동작을 모사할 수 있으므로,
의존하는 컴포넌트를 테스트에 통합하는 것이 불가능하거나 지금 당장은 사용할 수 없을 때 유용하게 사용할 수 있다.
모의 객체는 단위 테스트 수준에서도 자주 사용한다.
모의 객체는 일부 객체를 테스트에 통합할 수 없거나 통합할 필요가 없을 때, 그 객체를 대체하는 역할을 한다.
테스트 더블은 실제 객체를 대체하기 위해 만든 것으로 실제 객체의 동작을 비슷하게 흉내 내는 데 사용한다.
모의 객체나 테스트 더블은 이를 사용하는 객체의 동작을 테스트하기 위해 만든다.
외부 서비스, 속도가 느리고 구성이 어렵거나 접근하기 어려운 내부 서비스, 혹은 아직 사용할 수 없는 서비스와 통신해야 할 때 테스트 더블을 사용한다.
테스트 더블을 쓰면 테스트를 만들기 위해 다른 서비스가 완성될 때까지 기다릴 필요가 없어 유용하다.
인수 테스트
애플리케이션이 오류 없이 수행되는 것도 중요하다.
그러나 애플리케이션은 반드시 고객의 필요를 만족시켜야 한다.
인수 테스트는 애플리케이션이 고객이나 이해관계자가 정의한 목표를 달성하는지 확인하기 위해 사용한다.
인수 테스트는 최상위 수준의 테스트로,
인수 테스트를 활용하면 애플리케이션이 비즈니스 목표를 달성하고 기획한 작업을 제대로 하고 있는지와 같은 필수적인 질문에 답할 수 있다.
인수 테스트는 Given, When, Then 같은 키워드를 사용하여 표현할 수 있다.
이 키워드들을 사용한다는 것은 결국 사용자가 시스템과 상호작용한다는 시나리오를 따르게 되는 것이다.
Then 단계의 검증은 단위 테스트처럼 보일 수 있으나, 결과를 확인하고 ‘비즈니스 목표를 달성하였는가?’ 같은 본질적인 질문에 대한 답을 제공한다.
블랙박스 테스트 vs 화이트박스 테스트
블랙박스 테스트
블랙박스 테스트는 시스템의 내부 상태나 동작에 대한 지식이 없을 때 수행하는 테스트다.
블랙박스 테스트는 정확성을 검증하기 위해 외부적인 시스템 인터페이스를 사용한다.
이름에서부터 알 수 있듯 블랙박스 테스트는 시스템을 블랙박스로 취급한다는 특징이 있다.
시스템을 제대로 테스트하기 위해 알아야 하는 것은 시스템의 기능적 명세다.
일반적으로는 프로젝트 초기에 명세를 작성하므로 블랙박스 테스트는 비교적 일찍 시작할 수 있다.
QA 엔지니어, 개발자, 고객 등 누구나 시스템을 테스트할 수 있다는 것도 블랙박스 테스트의 특징이다.
웹 인터페이스를 제공하는 애플리케이션에 블랙박스 테스트를 사용한다.
HtmlUnit이나 Selenium 같은 테스트 도구를 사용하여 선택 항목, 푸시 버튼 등의 프런트엔드 컴포넌트와 상호작용하고,
동작 결과나 페이지의 내용 등의 결과를 확인할 수 있다.
화이트박스 테스트
화이트박스 테스트는 구현에 관한 해박한 지식을 바탕으로 테스트를 만들고 테스트 프로세스를 진행한다.
화이트박스 테스트를 만들기 위해서는 컴포넌트 구현을 이해하는 것 외에도 테스트 프로세스가 다른 구성 요소와 상호작용하는 방식을 알아야 한다.
그러므로 화이트박스 테스트를 실행하는 데에는 개발자가 적격이다.
GUI를 사용할 수 있을 때까지 기다릴 필요가 없으므로 화이트박스 테스트는 프로젝트 초기 단계에서 구현할 수 있다.
게다가 다양항 실행 경로를 커버할 수 있다.
1
2
3
4
5
6
7
Given 이코노미 항공편에서
When 일반 승객은
Then 추가할 수 있고, 삭제할 수도 있다.
Given 이코노미 항공편에서
When VIP 승객은
Then 추가할 수 있지만 삭제할 수는 없다.
위 예시처럼 화이트박스 테스트를 만들기 위해서는 애플리케이션 내부에 대한 지식이 필요하다는 장벽이 있지만,
외부 사용자가 미처 알 수 없는 다양한 실행 경로를 다룰 수 있다.
여기서 시나리오의 각 스템이 기존 API를 사용해서 만든 소스 코드에 대응된다.
그래서 개발자는 GUI 없이도 처음부터 소스 코드를 테스트할 수 있다.
장단점 비교
장점
블랙박스 테스트 화이트박스 테스트 사용자 중심적이며 설계 명세와 다른 부분이 무엇인지 바로 알 수 있다. 프로젝트 초기부터 테스트를 구현할 수 있다. 테스터가 비개발저여도 상관없다. GUI가 필요하지 않다. 개발자와 독립적으로 수행할 수 있다. 개발자가 제어하므로 다양항 실행 경로를 커버할 수 있다. 단점
블랙박스 테스트 화이트박스 테스트 입력할 수 있는 경우의 수가 제한적이다. 프로그래밍 지식이 있는 사람만 테스트를 구현할 수 있다. 커버되지 않은 실행 경로가 많을 수 있다. 구현을 변경하려면 테스트를 다시 작성해야 한다. 테스트가 중복될 수 있다.
세부 정보가 없다는 것은 동일한 실행 경로를 여러 번 커버할 수 있다는 것을 의미한다.테스트와 구현이 밀접하게 결합되어 있다.
정리
단위 테스트의 장점은 여러 개가 있고, 4가지 테스트 유형이 있지만 가장 작은 단위인 단위 테스트를 효과적으로 하는 것이 가장 중요한 것 같다.
블랙박스 테스트와 화이트박스 테스트는 상황에 맞춰 고려하는 것이 합리적인 것 같다.