Spring

통합 테스트와 단위 테스트

leeheefull 2022. 7. 28. 22:55

글에서 나오는 예제의 구현 코드테스트 코드는 해당 링크에 있습니다.

 

이번에 팀 내에서 테스트 코드를 어떤 방법으로 작성하면 좋을지 고민하던 중, TDD와 BDD에 대해 다시 공부하게 됐습니다.

TDD와 BDD 외에도 DDD, ATDD 등 수많은 xDD가 존재합니다. xDD는 모두 소프트웨어 개발 방법론입니다.

x주도 개발이라고 해서, 어느 것을 중점으로 두고 개발을 진행할지 미리 정해 두는 것입니다.

저는 그중에서도 TDD와 BDD 그리고 테스트 코드에 대해 고민해 봤습니다.

 

 

 

TDD

 

[사진 출처](https://tv.kakao.com/channel/3693125/cliplink/414004682)

사진 출처

 

TDD는 테스트 주도 개발이라는 뜻입니다. 요구사항이 주어지면, 요구사항에 대한 에러를 고민하고 바로 에러에 대한 테스트 코드를 작성한 후, 해당 테스트를 통과하도록 구현 코드를 작성하는 개발 방법론입니다.

 

 

예제

 

예를 들어서, “3,4”를 입력하면 7이 나오는 양수 덧셈 계산기를 TDD 방식으로 구현해 보겠습니다.

TDD 방식의 개발 방법론은 위의 한 줄의 요구사항을 기반으로 아래와 같은 예외 처리를 생각할 수 있습니다.

 

  • 공백을 넣는다면 어떻게 될까?
  • 숫자를 넣어야 할 공간에 숫자 외의 문자를 넣는다면 어떻게 될까?
  • 음수를 넣는다면 어떻게 될까?
  • 콤마(,)가 아닌 다른 구분자를 넣는다면 어떻게 될까?
  • 시작과 끝에 구분자를 넣는다면 어떻게 될까?

 

위처럼 한 줄의 요구사항이 여러 개로 나눠지게 됩니다. 그리고 각각 문장들의 테스트 코드를 작성합니다.

 

https://user-images.githubusercontent.com/58816862/180246919-f02b6b5d-14b2-4c13-8804-b1ff813f069f.pnghttps://user-images.githubusercontent.com/58816862/180247318-676a9c14-30e9-443e-97fe-e8b8b0c2af3d.pnghttps://user-images.githubusercontent.com/58816862/180247626-85f4da95-d092-47b6-a210-b555008fe401.png

이렇게 테스트 코드를 미리 작성합니다. 그리고 테스트 코드에 맞춰 구현을 진행합니다.

 

https://user-images.githubusercontent.com/58816862/180677314-20240006-14aa-4aac-a76b-46e1a246f7d7.pnghttps://user-images.githubusercontent.com/58816862/180247986-c7913423-8371-4c11-acbc-c4841f7686bd.pnghttps://user-images.githubusercontent.com/58816862/180248116-3388bbf5-5048-40ba-9d3d-bada4fee33dc.png

그리고 각 객체에 해당하는 메서드를 구현합니다.

TDD는 위처럼 한 줄의 요구사항을 기준으로 예외가 발생할 수 있는 경우의 수를 가장 작은 단위로 나누어 테스트 코드를 작성하는 방식입니다. 이러한 테스트 코드를 단위 테스트라고 합니다.

 

 

Unit Test

 

단위 테스트란, 위에서 언급한 것처럼 테스트 가능한 가장 작은 단위(= 함수 단위)로 나누고 예상대로 동작하는지 확인하는 테스트입니다.

TDD를 진행하게 된다면, 자연스럽게 단위 테스트를 하게 됩니다. 그리고 단위 테스트는 자연스럽게 테스트 커버리지를 높게 만들기 때문에 소프트웨어의 신뢰도를 높여줄 수 있습니다.

 

 

단점

 

예상하셨겠지만, TDD 개발 방법론을 통해서 단위 테스트를 진행하는 것은 생산성 저하라는 큰 단점이 있습니다.

위의 덧셈 계산기처럼 어떤 예외가 발생할지 눈에 뻔히 보이는 경우가 많습니다.

그리고 이러한 요구사항을 작성하는 개발자는 괴리감이 들 수 있습니다.

 

또한, 프로젝트를 진행하면서 어려운 예외가 생길 수 있는데, 그것 때문에 고민하는 순간이 찾아오게 됩니다.

원칙을 깰 수는 없고 꼼수가 있기는 한데 그 꼼수를 위해서 구조를 바꾸자니 이건 아무래도 아닌 것 같고,

테스트는 말 그대로 테스트일 뿐 실제 코드가 더 중요한 상황인데도 불구하고 테스트 원칙 때문에 쉽게 넘어가지 못하는 그런 경우입니다.

 

사실 두 번째 단점은 TDD를 완벽하게 이해하고 자연스럽게 사용하는 수준이 된다면, 문제가 되지 않을 수 있습니다. 하지만 이 수준에 오르기 위해서는 높은 학습 곡선이 필요합니다.

그리고, 첫 번째 단점은 단위 테스트의 단점입니다. 함수 단위로 테스트를 나눴기 때문에 발생한 단점이라고 할 수 있습니다.

그래서 이러한 TDD의 문제점을 극복할 수 있도록 새롭게 TDD에서 파생된 개발 방법론인 BDD에 대해 설명드리겠습니다.

 

 

 

BDD

 

[사진 출처](https://tv.kakao.com/channel/3693125/cliplink/414004682)

사진 출처

 

TDD로 개발을 진행할 경우, 어떠한 테스트 케이스를 작성할지 고민하는 시간도 만만치 않습니다. 이러한 문제점을 해결해 줄 수 있는 것이 바로 BDD입니다.

BDD는 행위 주도 개발이라고 합니다. 쉽게 말해서, 행위(= 요구사항)를 그대로 테스트 코드에 적용하는 것입니다.

 

 

예제

 

이번에도 “3,4”를 입력하면 7이 나오는 양수 덧셈 계산기를 BDD 개발 방식으로 설명을 드리겠습니다.

 

https://user-images.githubusercontent.com/58816862/180247626-85f4da95-d092-47b6-a210-b555008fe401.png

위처럼 요구사항 그대로 테스트 코드를 작성합니다.

 

https://user-images.githubusercontent.com/58816862/180248116-3388bbf5-5048-40ba-9d3d-bada4fee33dc.png

그리고 테스트 코드에 대해 구현을 합니다.

 

해당 방식은 요구사항이 그대로 테스트 코드에 반영되기 때문에 빠르게 개발이 진행될 수 있습니다.

요구사항을 Given(주어진 환경), When(사용자의 행위), Then(결과) 구조로 테스트 코드를 작성하기 때문에 비개발자도 테스트 코드를 쉽게 이해할 수 있습니다.

또한, 기획서나 요구사항을 보는 듯한 느낌을 주어서 개발자가 고민하는 시간을 단축시켜주는 효과가 있습니다.

BDD 개발 방법론으로 테스트 코드를 작성하면, 단위 테스트보다 더 큰 동작을 테스트하기 위해 여러 모듈이 의도대로 협력하는지 확인하게 됩니다. 그리고 이것은 통합 테스트라고 합니다.

 

 

Integration Test

 

실제 업무에서는 단위 모듈이 개별적으로 존재하는 것이 아니라 각 모듈이 유기적인 관계를 맺고 있습니다. 이러한 모듈들이 결합한 상태에서 테스트를 진행하는 것을 통합 테스트라고 합니다.

사실 단위 테스트에서 오류가 발견되지 않더라도, 모듈을 통합하는 과정에서 오류가 발생할 수 있습니다.

 

 

단점

 

통합 테스트는 테스트 코드 작성 시간이 단축되지만, 단위 테스트처럼 모든 메서드에 대해 테스트하지 않고 유기적으로 연결된 모듈들에 한에서 테스트를 진행하기 때문에 상대적으로 소프트웨어 신뢰도가 낮을 수 있습니다.

실무에서 단위 테스트를 진행하게 된다면, 구현보다 테스트 코드에 시간을 더 들여야 할 수 있습니다. 또한, 통합 테스트만을 사용하기에는 작은 실수를 잡기에는 어려움이 있습니다.

그래서 전반적인 요구사항에 대해서는 통합 테스트를 진행하고 비즈니스 로직의 사용되는 핵심적인 메서드나 자주 사용하는 유틸 클래스, 테스트가 꼭 필요하다고 생각되는 부분에는 단위 테스트를 진행하는 것이 좋은 방법이라고 생각하게 됐습니다.

 

 

 

결론

 

통합 테스트로 전반적인 테스트를 하고 필요한 부분은 단위 테스트 방식으로 진행하면 좋을 것 같다고 생각합니다.

간단한 예제를 통해서 통합 테스트와 단위 테스트를 어떻게 적용하면 좋을지 이야기해보겠습니다.

 

요구사항: 포춘 쿠키 API

 

  • 포춘 쿠키를 만들 수 있다.
  • 포춘 쿠키를 뽑을 수 있다.
    • 단, 포춘 쿠키는 존재하는 쿠키 중에 랜덤으로 뽑힌다.

 

 

통합 테스트

 

  • MockMvc와 @SpringBootTest를 사용하여 한 API에 대해 관련된 모듈 통합 테스트 해보겠습니다.
  • 모든 빈을 컨테이너에 올리고 테스트하기 때문에 운영환경과 유사한 환경에서 테스트를 할 수 있습니다.

 

적용 예제

 

테스트 코드

테스트 코드

 

구현 코드

구현 코드

 

포춘 쿠키를 뽑았을 때, 뽑힌 쿠키는 랜덤 값이기 때문에 상태에 대한 검증만 할 수 있습니다. 그래서 해당 부분은 단위 테스트를 통해서 검증해보겠습니다.

 

 

단위 테스트

 

  • Mockito 라이브러리를 활용하여 각 레이어의 의존성을 제거하는 슬라이스 테스트를 진행해보겠습니다.
  • DB에 의존하지 않고 테스트가 가능합니다.
  • 빈을 컨테이너에 등록하지 않고 테스트 가능합니다.

 

언제 사용하는 게 좋을까?

 

랜덤이나 셔플 등과 같이 결과 값을 예측할 수 없거나 외부 라이브러리나 저장소 등의 권한 밖의 영역에서 테스트해야 할 경우 이외에도 테스트하기 어려운 상황들이 생길 수 있습니다.

통합 테스트와 달리 모든 빈을 컨테이너에 올리지 않고 테스트하기 때문에 테스트 속도가 빠릅니다. 또한, 테스트하려는 부분에 집중할 수 있습니다..

 

적용 예제

 

포춘 쿠키를 랜덤으로 뽑는 로직에 대해 단위 테스트를 진행해보겠습니다.

 

구현 코드

구현 코드

 

테스트 코드

테스트 코드

 

이렇게 통합 테스트에서 테스트를 하기 어려운 부분이거나 테스트가 개인적으로 필요한 부분에 대해서 따로 단위 테스트를 진행해봤습니다.

 

 

 

Reference