July 24, 2023
이 글은 Spring Boot(Kotlin)를 통해 Web-3-Layer 중 어떤 레이어 테스트가 무슨 테스트라고 생각하는지를 정리하는 글이다.
사실 단위 테스트와 통합 테스트는 개념상 정의기 때문에, 구분하기 불분명한 부분이 분명히 존재한다.
예를들어 하나의 함수에 대한 테스트는 단위 테스트라는 것에는 모두 동의할 것이다. 하지만 여러 메소드로 구성된 클래스는 분리될 수 있는 많은 코드 조각을 포함한다. 그렇다면 클래스에 대한 테스트도 단위 테스트일까?
보통 아니라고 대답할 것 같다. 하지만 현실의 문제는 이 예시보다 더 복잡하다
구글은 단위/통합테스트 대신 범위(Scope)와 크기(Size)를 통해 테스트를 구분한다.
단 하나의 프로세스에서 실행되는 테스트
이러한 제약의 목적은 테스트를 느리게하거나, 비결정적으로 만드는 원인들로부터 작은 테스트를 분리하는 것이다
여러 프로세스와 스레드 이용이 가능
여러대의 기기 활용 가능
단위 테스트의 범위가 좁다고 할때는 ‘실행’되는 코드가 아니라 ‘검증’되는 코드의 양이 기준이다. 하나의 클래스가 다른 여러 클래스를 의존하거나 참조하는 경우는 흔하며, 대상 클래스를 테스트 하는 과정에서 의존성을 자연스럽게 호출한다.
Mock object
같은 방법을 활용하여 테스트 대상 시스템 바깥 코드가 실행되는 일을 피하고는 한다테스트를 크기로 구분하는 것은 명확하지만, 범위로 구분하는 것은 모호한 면이 있다. 구글의 정의에 따르면 단위 테스트는 대체로 작은 테스트에 속한다
Spring Boot의 Web Layer Test는 본질적으로 Application Context
(Spring의 DI/IoC Container)를 구성하기에 (순수한) 단위 테스트가 아니라고 생각할 수 있다. 하지만 이 글에서는 구글의 정의를 참조하기 때문에 실행되는 코드가 아니라 검증되는 코드에 집중한다. 또한 MockBean
은 의존성을 제거하는 기술로 이해한다.
위 테스트 코드를 보면 POST /bookmarks
가 Service 레이어의 create함수를 호출하는지 검증하고 있다. (상호작용 테스트)
이때 주목해야할 부분은 두가지이다
@MockkBean
어노테이션과 // given
주석 부분을 이용해 service 레이어를 Mock Object로 만들었다mockMvc
를 사용해 HTTP Client 부분을 대체했다왜 HTTP Client를 완전히 없애지 않았을까? 컨트롤러 레이어는 HTTP Client에 바로 맞닿아 있기 때문에 HTTP Request에 대한 의존성을 끊으면 실제 동작과 달라져서 테스트에 의미가 없어진다.
따라서 아래와 같은 이미지를 생각해볼 수 있다. 상위 레이어는 의존성을 완전히 끊지 않았지만 하위 레이어(Service)는 가짜 객체를 만들었다. (스프링에서는 이러한 테스트를 슬라이스 테스트라고 부르는듯 하다)
이러한 컨트롤러 테스트는 단위테스트와 통합 테스트 사이에 위치한 무엇인가이며, 구글에 정의에 따르면 작은크기 - 좁은 범위 테스트
해당 테스트 코드에서는 상위 레이어로의 의존성이 존재하지 않으며 하위 레이어인 Repository를 Mock up하여 온전히 서비스의 로직 테스트에 집중하고 있다.
단위테스트이자, 작은크기 - 좁은 범위 테스트이다.
해당 코드에 대해 설명하자면
AbstractIntegrationTest
클래스는 한마디로 Testcontainers
라는 기술을 통해 도커 MySQL 컨테이너를 띄우는 코드이다BookmarkRepositoryTest
클래스는 결국 docker에 띄워진 MySQL을 사용한다이렇게 구현한 이유는 간단하다. 컨트롤러 레이어처럼 결국 Database와의 의존성이 끊어지면 실제 동작과 달라지기 때문에 테스트의 의미가 사라진다.
특정 기술을 통해 In-memory database(H2
)로 대체해 테스트하는 경우에도 위와 마찬가지 문제가 발생한다. 그렇다고 매번 테스트마다 Localhost에 Database를 setup할 수는 없다.
만약 Localhost의 Database에 테스트를 의존한다면, 테스트 환경이 달라졌을때 고생을 생각한다면 (Git actions 등등을 사용한다고 생각해보면…)
크기나 범위의 정위와 무관하게 모든 테스트는 밀폐되어야 한다.
DB instance와 상호작용또한 검증하기 때문에, 통합테스트이자 중간 크기,범위 테스트이다
레이어 | 전통적 구분 | 구글의 구분 |
---|---|---|
컨트롤러 | 단위와 통합 사이 | 작은크기-좁은범위 |
서비스 | 단위 | 작은크기-좁은범위 |
레퍼지토리 | 통합 | 중간크기-중간범위 |
물런 이러한 구분과 결론은 온전히 작성자 본인의 생각이며, 위 예시 코드에 대해 구분한 것이다.