이 시리즈는 TDD를 숙달하기 전에 TEST 자체에 대한 이해를 높이기 위한 학습 시리즈입니다
테스트의 필요성
레거시 코드란?
마이클 C.페더스
"내게 레거시 코드란, 단순히 테스트 루틴이 없는 코드다. 다만 이 정의는 다소 불완전하다"
Regression
- 잘 돌아가던 코드가 최근 배포로 인해서 동작하지 않는 상황을
Regression
이라 합니다
- 이런 경험이 한 두번 쌓이게 되면, 전체적으로 수정과 배포 자체의 두려움이 커지게 됩니다
- 2005년 구글 웹서버의 생산성이 급격히 떨어지는 문제가 발생했습니다
- 설상가상 이 시기에는 릴리즈 주기도 길었고, 버그도 많아졌습니다
- 아무리 똑똑한 엔지니어를 투입해도 문제는 해결되지 않았고, 항상 불안에 떨며 배포를 했습니다
- 테크 리더는 자동 테스트를 도입하기로 결정합니다
- 팀원들은 자신있게 배포를 할 수 있게 되었고, 그 결과 생산성이 올라가게 됩니다
좋은 아키텍처란?
- 기본적으로
SOLID
원칙을 준수하기 위해 노력한 아키텍처는 좋은 아키텍처에 가깝습니다
- 또한
TEST
와 SOLID
원칙은 생각보다 긴밀한 관계를 맺고 있습니다
TEST
가 Regression
을 방지하기 위한 거라면, SOLID
원칙은 TEST
하기 쉬운 구조를 가지게 하는 상호보완적인 관계를 가지고 있습니다
SOLID
SRP(단일책임의 원칙: Single Responsibility Principle)
- 클래스는 단 한개의 책임을 가져야합니다
- 클래스를 변경하는 이유는 단 하나여야만 합니다
- 이를 지키지 않으면, 한 책임의 변경에 의해 다른 책임과 관련된 코드에 영향을 미칠 수 있게 됩니다
→ 이렇게 되면 유지보수가 매우 비효율적
- 테스트는 명료하고 간단하게 작성해야 하기 때문에, 단일 책임 원칙을 지키게 됩니다
- 테스트가 너무 많아져서 이게 무슨 목적의 클래스인지 눈에 안들어오는 지점이 생기게 됩니다
- 이 때가 클래스를 분할해야 하는 시점, 그러면서 책임이 자연스럽게 분배됩니다
OCP (Open-Closed) 개방-폐쇄 원칙
- 확장에는 열려있어야 하고, 변경에는 닫혀 있어야 합니다
- 즉, 기존의 코드를 변경하지 않고 기능을 수정하거나 추가할 수 있도록 설계해야 합니다
- 이를 지키지 않으면
instanceof
와 같은 연산자를 사용하거나, 다운 캐스팅 발생합니다
- 테스트 컴포넌트와 프로덕션 컴포넌트를 나눠 작업하게 되고 필요에 따라 이 컴포넌트를 자유자재로 탈부착이 가능하게 개발할 수 있게 됩니다
LSP (Liskov Substitution) 리스코프 치환 원칙
- 하위 타입 객체는 상위 타입 객체에서 가능한 행위를 수행할 수 있어야 합니다
- 즉, 상위 타입 객체를 하위 타입 객체로 치환해도 정상적으로 동작해야 함
- 이상적으로 테스트는 모든 케이스에 대해 커버하고 있으므로, 서브 클래스에 대한 치환 여부를 테스트가 알아서 판단해 줍니다
ISP (Interface Segregation) 인터페이스 분리 원칙
- 클라이언트는 자신이 사용하는 메소드에만 의존해야 한다는 원칙입니다
- 인터페이스는 해당 인터페이스를 사용하는 클라이언트를 기준으로 잘게 분리되어야 합니다
- 테스트는 그 자체로 인터페이스를 직접 사용해볼 수 있는 환경입니다. 따라서 불필요한 의존성을 실제로 확인할 수 있는 샌드박스입니다
DIP (Dependency Inversion) 의존 역전 원칙
- 의존 관계를 맺을 때, 변하기 쉬운 것(구체적인 것)보다는 변하기 어려운 것(추상적인 것)에 의존해야합니다
- 가짜 객체를 이용하여 테스트를 작성하려면, 의존성이 역전되어 있어야하는 경우가 생깁니다
테스트의 3분류
- 전통적으로 나누는 테스트 3분류는
API 테스트
부분을 제외하면 그 각층의 의미가 어느 선까지인지 정의가 사람마다 다를 수 있습니다
- 따라서 여기에선 규모로 구분지어둔 구글의 테스트 3분류를 보고 각 의미를 살펴보겠습니다
small(소형) 테스트
- 단일 서버
- 단일 프로세스
- 단일 스레드
- 디스크 I/O 사용해선 안됨
- Blocking call 허용 안됨
- 소형 테스트를
단위 테스트
라고 칭합니다
- 테스트에서 소형 테스트는 매우 중요합니다
- 소형 테스트에서 결과는 매우 확정된 결과로 나오고 이는 테스트 속도의 증가로 이어집니다
medium(중형) 테스트
- 단일 서버
- 멀티 프로세스
- 멀티 스레드
-> h2
같은 테스트 DB를 사용할 수 있습니다
- 소형 테스트보다 느립니다
- 멀티 스레드 환경에서 항상 동일한 동작을 보장하지 못할 수 있습니다
h2
같은 외부 모듈의 동작에 따라서 달라지기 때문입니다
- 스프링 개발자들이 너무 많이 만드는 테스트이기도 합니다
- 하지만 피라미드 구조를 보면 알 수 있듯이 소형 테스트가 전체 테스트의 80%를 차지해야 합니다
large(대형) 테스트