앞선 포스트에서는 테스트 코드란 무엇인지, 또 테스트 코드를 작성하는 이유는 무엇인지에 대해 간단하게 알아보았다.
이번에는 테스트 코드의 다양한 종류와 각 종류 별로 소프트웨어의 어떤 부분에 집중하는지, 그리고 그중에서도 특히 단위 테스트와 통합 테스트에 대해 자세하게 알아보겠다.
테스트 코드의 종류
테스트 코드에는 단위 테스트, 통합 테스트, 부하 테스트 등 우리가 흔히 들어본 테스트 방식뿐만 아니라 인수 테스트, 회귀 테스트, E2E 테스트 등 정말 많은 종류의 테스트가 존재한다. 여기에서는 흔히 개발자들이 접할 수 있는 테스트에 대해서 정리해 보겠다.
- 단위 테스트(Unit Test): 소프트웨어의 가장 작은 단위인 함수, 메서드, 또는 클래스 등의 개별 단위를 테스트하는 것이다. 단위 테스트는 주로 프로그램의 각 부분이 의도대로 정확하게 동작하는지 확인하는 데 사용된다.
- 통합 테스트(Integration Test): 단위 테스트가 완료된 모듈들을 조합하여 전체 시스템이 예상대로 작동하는지 확인하는 테스트이다. 모듈 간의 상호 작용과 데이터 흐름을 테스트하는 데 중점을 둔다.
- 인수 테스트(Acceptance Test): 소프트웨어가 사용자 또는 시스템 요구사항을 충족하는지 검증하는 테스트이다. 사용자의 관점에서 소프트웨어가 예상대로 동작하는지 확인한다.
- 성능 테스트(Performance Test): 특정 상황에서의 응답 시간, 처리량 등을 측정함으로써 소프트웨어의 성능을 평가한다.
- 부하 테스트(Load Test): 소프트웨어가 특정 부하 조건에서 어떻게 동작하는지 확인하는 테스트이다. 다수의 동시 사용자나 많은 데이터 양 등의 부하 조건을 특정 시간 동안 가해서 소프트웨어의 안정성을 평가한다.
- E2E 테스트(End-to-End Test): 소프트웨어 시스템의 전체적인 흐름을 시뮬레이션하여 사용자의 관점에서 애플리케이션이 예상대로 동작하는지를 확인하는 테스트이다. 인수 테스트와 약간 비슷하다고 느껴질 수 있는데, 인수 테스트는 요구사항을 중심으로 테스트가 진행되는 반면, E2E 테스트는 전체 시스템을 시뮬레이션하는 것이다.
단위 테스트
위에서 단위 테스트는 소프트웨어의 가장 작은 단위인 메서드 등을 테스트하는 것이라고 하였다. 그렇다면 실제로 테스트를 진행할 때는 어떻게 진행되는 것일까?
실제 프로그램이 실행되는 상황을 생각해 보면, 하나의 모듈만으로 프로그램이 완성되는 것이 아닌 다른 모듈이나 외부 라이브러리와 상호작용을 통해 프로그램의 전체적인 과정이 완성된다. 그렇기에 일반적인 방법으로 하나의 모듈 단위만이 의도대로 잘 작동하는지 확인하는 것은 매우 어렵다고 볼 수 있다.
이런 상황에서 어떻게 해야 단위 테스트가 진행될 수 있을까? 단위 테스트는 하나의 모듈 단위만 테스트하는 것이기에 다른 모듈의 결과에 영향을 받아서는 안된다. 즉, 다른 모듈에서는 테스트 시나리오에서 의도한 대로 응답이 와야 하는 것이다. "테스트 대상 모듈이 이러한 시나리오에서 이러한 응답을 하는지"를 테스트할 것이기에 다른 모듈들은 "이러한 시나리오"를 만들어주기만 하면 단위 테스트를 진행할 수 있는 것이다.
위에서 말한 다른 모듈들이 "이러한 시나리오"를 만들어줄 수 있게 하는 데 사용되는 것이 바로 Mock(모의 객체)이다. Mocking을 통해 현재 테스트를 진행하려고 하는 모듈에 영향을 주는 다른 모듈들이 지정된 응답만을 반환하도록, 즉 "흉내" 내게 하여 테스트 시나리오를 구축하고, 그러한 상황 속에서 테스트 대상 모듈이 의도한 응답을 하는지 확인할 수 있게 하는 것이다. "의존성"으로 인한 단위 테스트 시의 어려움을 Mock을 사용해 한 번에 해결할 수 있다.
간단한 예를 들어보겠다. 현재 MVC 패턴을 따르는 Spring 프로젝트를 개발하고, 이를 테스트하려고 한다고 가정해 보자. 특정 Controller 계층에 대해 단위 테스트를 진행하려고 한다면, Controller 계층은 당연하게도 Service 계층의 결과에 영향을 받게 된다.
Service 계층에서 A가 반환되면 Controller 계층에서는 A'라는 결과, Service 계층에서 B가 반환되면 Controller 계층에서는 B'라는 결과가 나오는 방식을 테스트해 보겠다고 하자. 그렇다면 Service 계층에서 A 또는 B라는 결과가 반환되도록 Mocking을 진행하면 된다. Mocking 후 Controller 계층에서 원하는 결과가 나오는지 확인하면 정말 간단하게 단위 테스트가 완료되는 것이다.
만약 Mocking이 없었다면, Service 계층에서 A 또는 B가 나오도록 요청이나 테스트 환경에 대한 설계를 더 세밀하게 조정해야 하므로 테스트에 쏟아야 하는 리소스도 커지게 됐을 것이다. 또한 위와 같은 방식의 테스트는 다른 모듈들과 결합한 상태의 테스트이기에 단위 테스트라는 개념에도 어긋나는 테스트가 돼버린다.
통합 테스트
통합 테스트는 위에서 살펴본 단위 테스트와 다르게 당연히도 Mock이 필요가 없는 작업이다. 통합 테스트의 목표 자체가 여러 모듈의 조합을 통해 만들어진 전체 시스템이 예상대로 돌아가는 것을 확인하는 것임을 생각해 보면 쉽게 그 이유가 이해될 것이다.
전체 시스템에 대한 테스트이다 보니, 개발자가 개발 단계에서 실제로 프로그램을 구동시켜 놓고 자체적으로 테스트를 진행하는 경우와 매우 유사한 시나리오에 대한 테스트가 될 수밖에 없다. 그렇기 때문에 제일 처음 테스트를 접했을 때 가장 위 화감 없이 테스트 코드를 작성하고, 진행할 수 있었던 테스트였다.
하지만 통합 테스트의 어려움은 전체 시스템에 대한 테스트이기 때문에 DB에 값이 삽입되고, 변경되고, 또 삭제되기도 하면서 자칫 테스트의 설계가 잘못된다면 실제 DB에 저장된 값에 영향을 끼치는 최악의 경우가 발생할 수 있다는 점이다. 물론 이를 대비하여 실제 DB가 아닌 다른 테스트 DB를 로컬이나 다른 서버에 마련하여 테스트를 진행하는 방식으로 간단하게 해결할 수 있지만, 테스트를 처음 접한 초보 개발자의 경우 자칫 쉽게 실수할 수 있는 경우이다.
예를 들어보겠다. 위와 동일한 상황으로 Controller 계층에 대한 통합 테스트를 진행한다고 해보겠다. Mocking을 해제하고, 요청이나 테스트 환경에 대한 설계를 세밀하게 조절하여 Controller가 의도한 응답을 하도록 조정하면 된다.
테스트 코드들의 다양한 종류와 단위/통합 테스트에 대해 자세하게 알아보았다. 다음 포스팅에서는 실제로 곰터뷰에서 어떤 방식으로 단위/통합 테스트를 진행했는지 실제 코드와 함께 살펴보겠다.
'개발 > 곰터뷰🐻' 카테고리의 다른 글
테스트 코드는 왜 만들까? (3) | 2024.01.19 |
---|---|
곰터뷰와 IDrive e2 (3) - AWS S3 SDK for JavaScript를 활용한 IDrive e2 사용법 (0) | 2023.12.13 |
곰터뷰와 IDrive e2 (2) - IDrive e2를 사용한 비디오 저장/조회 로직 (0) | 2023.12.06 |
곰터뷰와 IDrive e2 (1) - 왜 IDrive e2를 사용하게 되었나? (0) | 2023.11.18 |