1. 왜 Test 코드를 작성하는가?
1) Test 코드를 작성하지 않고 결과를 검증하는 과정은 비용이 많이 든다.
Test코드 사용 X (싱글 애플리케이션 (Monolithic Arichitecture)에서의 모습)
- 검증 코드 작성
- 애플리케이션 실행
- PostMan 혹은 브라우저 Request 요청
- log 혹은 print로 결과 검증
- 원하지 않는 결과 발생 시 애플리케이션 종료
- 다시 코드 작성
위와 같은 로테이션이 원하는 결과를 얻을 때까지 돌아가게 된다. 이처럼 간단한 애플리케이션이라도 실행하고 종료하는데 비용이 많이 들지만 Test 코드를 작성한다면 이야기가 달라진다.
Test 코드를 사용 O
- Test 코드 작성
- Test 코드 실행
- 결과 검증
- Test 코드 수정
위와 같이 애플리케이션을 실행, 종료할 필요가 없다. 따라서 비용이 줄어들고, Test 코드를 통해서 명확한 결과 검증이 가능해진다. Test 코드를 작성하지 않았을 때와 비교하면 아래와 같은 이점이 존재한다.
- Spring은 편리하고 빠른 테스트에 큰 가치를 두고 있다.
- 구현한 기능을 직접 동작시키는 방법으로 테스트를 한다면 반드시 필요한 모든 기능들이 구현이 되어 있어야 하는데, 작은 단위의 테스트를 진행해주면 에러가 났을 때의 위치도 파악하기 쉬워지고, 에러는 없지만 기능이 제대로 동작하지 않는 경우에도 문제의 원인을 찾기가 수월해진다.
- 실제 개발 소스코드와 테스트 소스코드를 구조적으로 분리하면 개발에도 큰 도움이 될 것이고, 코드를 작성하면서 머릿속에서 이루어졌던 테스트들을 테스트 코드로 구현하여 로직 작성에 더욱 집중할 수 있게 된다.
- 다양한 조건에 따른 테스트가 가능하기 때문에 오류를 발생시키는 값의 범위를 분석할 수 있다.
- 테스트 코드를 작성해두면 프레임워크나 제품을 업그레이드 했을 때 새로운 버그가 발생하는지를 테스트할 수 있다.
Spring은 계층 구조로 일반적으로 아래와 같이 구성돼 있다.
- Controller : 클라이언트 요청을 받고 클라이언트에게 결과를 반환 (Presentation Layer)
- Service : 비즈니스 로직을 실행하고 결과 반환(Service Layer)
- Repository : database에 쿼리를 이용해서 CRUD를 하는 계층(Data Access Layer)
- Domain : Entity 클래스
그렇기에 애플리케이션을 실행해서 Test를 진행한다면, 어느 계층에서 잘못된 코드가 있는지 파악하는데 많은 비용이 든다. 하지만 Test 코드를 통해서 계층별로 Test를 진행한다면 어느 부분이 잘못되었는지 쉽게 파악할 수 있다.
2. Springboot Test
Spring Initailizer를 통해서 프로젝트를 생성하면 spring-boot-starter-test dependency가 자동으로 추가된다. 우리는 이를 이용해서 Test 코드를 작성하면 된다.
1) spring-boot-test-starter 구성요소
- spring-boot-test: 테스트에 필요한 핵심 기능 라이브러리
- spring-boot-test-autoconfigure: 테스트 진행 위한 Configuration 라이브러리
2) Junit 이란?
- Java에서 독립된 단위 테스트를 지원해주는 프레임워크
- Assert(검증)을 이용해서 결과를 기댓값과 실제 값을 비교
- @Test 어노테이션마다 독립적으로 테스트가 진행
- 크게 Jupiter, Platform, Vintage 모듈로 구성되어있다.
자세한 내용은 Junit 포스팅에 따로 만들도록 하겠다.
3. 단위 테스트와 통합 테스트
- Development: 개발
- Unit Tests (단위 테스트): 개발자 테스트
- QA Testing:
- 블랙박스 테스팅
- 주로 QA 팀이 Production 환경과 유사한 환경(Stage)에서 테스팅
- Production: 실 서비스 운영 환경
단위(unit) 테스트: 하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 단위 테스트 -> 쉽게 말하면 하나의 기능 혹은 메서드라고 이해하면 된다.
통합(integration) 테스트: 모듈을 통합화는 과정에서 모듈 간의 호환성을 확인하는 테스트 -> unit이 하나였다면 반대로 여러 개의 계층이 테스트에 참여한 것이라고 생각하면 쉬울 거 같다.
1) 단위 테스트 장단점
장점
- 새로운 기능에 대해서 빠르게 작성 가능
- Test 코드 자체가 하나의 문서
- 시간과 비용의 절감
단점
- 독립적인 테스트이므로 다른 객체와 상호작용 처리를 위해서 가짜 객체 정의 필요함
- 가짜 객체의 답변 작성 필요함
- 실제 운영 환경과 다른 답변을 내놓을 수 있는 가능성이 있음
2) 통합 테스트 장단점
장점
- 실제 객체를 사용하므로 가짜 객체 사용하지 않아 정의하지 않아도 됨
- 실제 운영 환경과 같은 값을 도출 가능함
단점
- 테스트 하나의 많은 비용이 들어감
- 어느 계층에서 발생한 문제인지 파악하기 힘듦
3) 단위 테스트, 통합 테스트 선택
단위 테스트, 통합 테스트 모두 장단점이 명확하다. 하지만 통합 테스트의 경우 비용을 절감할 수 있는 방법이 없다. 단위 테스트는 단점들을 개선해 나갈 수 있다. 그래서 좋은 단위 테스트 작성에 대해서 알아보자.
4) 좋은 단위 테스트
1. 1개의 테스트는 1개의 기능에 대해서만 테스트
2. 테스트 주체와 협력자를 구분하기. ( 여기서 주체는 테스트를 할 객체이며, 협력자는 테스트를 진행하기 위해 정의하는 가짜 객체입니다.)
3. Given, when, then으로 명확하게 작성하기
- Given: 테스트를 진행할 행위를 위한 사전 준비
- when: 테스트를 진행할 행위
- then: 테스트를 진행한 행위에 대한 결과 검증