인생을 코딩하다.

[Spring] 스프링 테스트 전략 본문

Spring

[Spring] 스프링 테스트 전략

Hyung1 2020. 12. 27. 21:16
728x90
반응형

통합테스트 

 

장점

  • 모든 빈을 올리고 테스트를 진행하기 때문에 운영환경과 가장 유사하게 테스트 가능
  • API를 테스트 할 경우 요청부터 응답까지 전체적인 테스트 진행 가능

단점

  • 모든 빈을 올리고 테스트를 진행하기 때문에 테스트 시간이 오래걸리고 무거움
  • 테스트의 단위가 크기 때문에 테스트 실패시 디버깅이 어려움
  • 외부 API 콜 같은 Rollback 처리가 안되는 테스트를 진행하기 어려움

 

단위테스트

 

장점

  • TDD 의 첫 단계의 기능 단위의 테스트 코드 작성
  • 단위테스트를 진행하면 여러 문제점들을 사전에 차단하여 더 빠른 오류 검증 및 수정을 빠르게 해결 가능

단점 

아래 참고

 

테스트 격리 : 테스트를 서로 격리하여 한 테스트를 실행해도 다른 테스트에 영향을 주지 않도록 해야한다.

즉, 테스트는 순서에 상관없이 독립적으로 실행되며 결정적으로 수행되어야 한다.-> 같은 입력 값이면 언제나 같은 결과를 반환

 

프로덕션 코드와 테스트 코드를 작성하여 테스트 영역

텍스트 격리가 근본적으로 필요한 이유는 각각의 테스트에서 사용되는 데이터들이 공유되기 때문이다. 

이 데이터들이 언제, 어떻게 변화될지 모르기 때문에 테스트에 결정적인 수행을 하지 못하고 불안전한 테스트가 된다. 따라서 데이터 베이스를 얼마나 의존하지 않고 테스트를 실행 할 수 있는지, 혹은 얼마나 효과적으로 데이터베이스 상태를 테스트 이전의 상태로 돌려 놓을지에 고민하며 구현하는 것이 좋은 방식이다.

 

Domain(POJO) 

  • 순수 자바 객체로, 애플리케이션의 POJO(Model, Utils etc..)는 Junit으로 테스트
  • 객체는 new 연산자로 간단히 인스턴스화
  • 각각의 테스트가 실행되기 전에 @BeforeEach에서 인스턴스 초기화
  • 데이터 베이스를 사용하지 않기 때문에 걱정할 필요가 없음
  • POJO를 테스트 하므로 테스트 속도 및 난도가 낮지만 안정성을 가짐

 

Service

  • 실질적인 비즈니스 로직을 수행
  • 실제 데이터베이스 사용
  • 트랜잭션이 끝나면 데이터베이스 상태 변경
  • 테스트간 격리 필요

 

하지만 Mockito를 이용하면 데이터베이스를 사용하지 않기 때문에 테스트 격리를 고민할 필요가 없다.

 

Controller 

  • Spring IoC로 실제 컨트롤러 빈을 사용해서 테스트
  • 실제 데이터베이스 사용
  • 통합테스트

 

MockMvc RestAPI 클라이언트 테스트 도구를 사용하여 데이터베이스를 사용하지 않고 단위테스트 수행

 

Repository  

  • @DataJpaTest를 이용하여 InMemory로 테스트를 수행
  • 위의 어노테이샨을 이용하면 자동적으로 @Transactional(rollback=true)가 사용되기  때문에 테스트 격리를 걱정하지 않아도 된다.
  • 테스트 범위가 작기 때문에 실제 환경가 차이가 발생

 

인수테스트란?

 

시스템이 실제 운영 환경에서 사용될 준비가 되었는지 최종적으로 확인하는 단계

  • 실제 운영환경에 맞게 서버를 띄우고 데이터베이스를 사용
  • 테스트 격리를 신경쓰지 않으면 테스트가 실패하기 쉬움
  • 테스트 단위가 커서 한번 실패하면 디버깅하기 까다로움
  • mock객체를 사용하면 데이터베이스를 사용하는 인수테스트의 목적에 맞지 않아 사용항 수 없음

 

그럼 어떻게 하면 인수테스트에서 각 테스트를 격리 할 수 있을까?

1. 롤백이 적용되는 @Transactional 이용

2. 매번 테스트 종료시 생성한 픽스처 및 데이터 삭제

3. 매번 테스트 종료시 테이블 Truncate (@Sql로 SQL파일 실행, EntityManager 이용)

등이 있지만 위의 세가지의 경우에는 많은 문제점들이 따른다.

(허나 아직 나는 인수테스트를 할 단계가 아니기 때문에 할 때가 올 때, 문제점에 관해 고민한 후 보완해서 내가 구현한 로직을 예제로 이 글을 리팩토링 하겠다.)

 

 

테스트를 할 때 중요한 것은 뭘까?

 

테스트를 할 때 중요한 것 중

하나는 테스트 안에 의도와 목적이 드러나도록 작성해야한다.

@Test
{
    어떠한 조건에서
    무엇을 수행했을 때
    어떤 결과가
}

좋은 프로젝트라는 것은 누군가 왔을때, 테스트코드만 봐도 이 서비스의 비즈니스 플로우를 어느정도 파악 할 수 있어야 한다. 즉 프로덕션 코드만큼 테스트코드도 가독성이 좋아야 한다.

 

둘째는 테스트코드도 프로덕션 코드처럼 리팩토링을 꾸준히 해주어야 한다. 코드가 늘어남에 따라 테스트 코드도 계속 늘어날텐데, 그럼 중복된 코드라던지 여러 문제점이 발생 할 수 있다. 코드양에 대한 이야기가 아닌 가독, 안정성, 요구사항 정리 등 비즈니스 코드와 동일한 수준의 리팩토링이 함께 이루어져야 한다. 

 

셋째는 어떻게 테스트 할 것인가? 이다

1. 테스트 불가능한 역역을 바운더리 레이어까지 올려서 테스트 가능하도록 변경

2. Context, Framework 종속적이지 않도록 테스트

3. Test Double 사용

4. Embedded Sytem 사용

5. EndPoing Test 도구 사용하여 내부 API Spec 테스트

6. Spring Cloud Contract로 외부 API  Spec 테스트

 

잘 격리된 테스트는 유지 보수가 수월하고 더욱 안전한 테스트 작성으로 코드의 품질을 보장해준다.

 

그래서 나는?

 

단위테스트와 통합테스트가 있지만 단위테스트로 진행하려한다. 그냥 순수한 단위테스트가 더 좋은 설계라고 생각한다. 정말 말 그대로 단위, 단위로 쪼개서 테스트 하는 것이 컨테이너 까지 올리면서 하는 통합테스트보다 더 효율 적이라고 생각한다. 모든 테스트의 순서와 관계를 생각하며 테스트를 작성하기는 어려울 것이다. 단위로 쪼개 독립적으로 작성하는게 더 효율적이고 정확할 것이다. 물론 언젠가 통합테스트로 할 때도 오겠지만, 해야할 단위의 테스트를 정확히 잘 하는 것이 좋은 설계라고 생각한다.

 

 

출처 및 참고 :

www.youtube.com/watch?v=YdtknE_yPk4

www.youtube.com/watch?v=OM_bN4wzd0g

728x90
반응형

'Spring' 카테고리의 다른 글

[Spring] IOC와 DI  (0) 2021.02.09
[Spring] 레이어 구조에 관한 고찰  (0) 2020.12.27
[Spring] RestTemplate  (0) 2020.12.17
[Spring] @CrossOrigin  (0) 2020.12.10
Junit4 -> Junit5 (목적(쓰임새)은 같지만 4 -> 5 로직 비교)  (0) 2020.11.30
Comments