2. HOW - 테스트 코드를 어떻게 작성해야 하는가?

2.1 테스트 케이스 선택 방법

  • 첫 번째 테스트의 중요성: 구현하기 가장 쉬운 테스트부터 시작하는 것이 좋습니다. 예외적인 상황이나 가장 빠르게 개발할 수 있는 테스트 케이스를 먼저 작성하고, 점차 확장해 나갑니다.
  • 점진적 확장: 쉬운 테스트부터 시작해 점차 복잡한 테스트로 나아가면서 시스템의 안정성을 검증합니다.

2.2 TDD (Test-Driven Development) 방법론

TDD는 테스트 주도 개발 방식으로, 테스트 코드를 먼저 작성하고 이를 기반으로 프로덕션 코드를 작성하는 방식입니다. TDD는 다음과 같은 세 단계를 따릅니다:

  1. 레드 단계: 실패하는 테스트를 작성합니다. 이때, 아직 프로덕션 코드는 작성되지 않았기 때문에 테스트는 실패합니다.
  2. 그린 단계: 최소한의 코드로 테스트를 통과시킵니다. 테스트를 성공시키기 위한 코드만 작성하여 빠르게 테스트를 통과합니다.
  3. 리팩터 단계: 중복을 제거하고 코드 구조를 개선합니다. 테스트가 통과한 후 코드의 가독성이나 유지보수성을 높이기 위해 리팩터링을 진행합니다.

이 과정을 반복하면서 점진적으로 시스템을 구축하고 테스트의 커버리지를 높여갑니다.

2.3 다양한 테스트 종류와 계층 구조 이해

  • 단위 테스트: 개별 모듈이나 메서드를 테스트하는 방식으로, 가장 기본적인 테스트 방법입니다. 빠르고, 독립적으로 실행될 수 있습니다.
  • 통합 테스트: 여러 모듈이 함께 작동하는지를 테스트하는 방식입니다. 단위 테스트보다 더 복잡한 시나리오를 검증할 수 있습니다.
  • 시스템 테스트: 전체 시스템이 의도한 대로 작동하는지를 검증하는 테스트로, 사용자의 관점에서 테스트를 진행합니다.

2.4 JUnit5 활용

JUnit5는 자바 테스트를 위한 표준 프레임워크입니다. 다음과 같은 주요 구성 요소와 기능을 갖추고 있습니다:

  • 주요 어노테이션
    • @Test: 테스트 메서드를 나타냅니다.
    • @BeforeAll, @BeforeEach: 각각 전체 테스트 전, 개별 테스트 전 실행될 메서드를 정의합니다.
    • @AfterEach, @AfterAll: 각각 개별 테스트 후, 전체 테스트 후 실행될 메서드를 정의합니다.
  • 주요 어서션 메서드
    • assertEquals(expected, actual): 기대값과 실제값이 일치하는지 확인합니다.
    • assertNull(object): 객체가 null인지 검증합니다.
    • assertThrows(): 예외가 발생하는지 검증합니다.

참조 코드:

JUnit 기본 테스트 예시 - UserServiceImplTest

https://github.com/junhkang/springboot-testing-from-zero-to-hero/blob/main/src/test/java/io/github/junhkang/springboottesting/service/impl/JpaUserServiceImplTest.java

2.5 Mockito와 같은 Mocking 프레임워크 사용

Mocking은 외부 의존성을 모방하여 테스트하는 방법입니다. Mockito와 같은 프레임워크를 사용하여 쉽게 Mock 객체를 생성하고 테스트할 수 있습니다.

  • 테스트 더블의 종류
    • Dummy: 사용되지 않는 매개변수에 전달되는 객체입니다.
    • Stub: 미리 정의된 결과를 반환하는 객체입니다.
    • Mock: 동작을 검증할 수 있는 객체로, 메서드 호출 여부 등을 검증합니다.
    • Spy: 실제 객체의 동작을 일부 모니터링하거나 수정하는 객체입니다.
    • Fake: 실제 동작을 구현하지만, 단순하게 동작하는 테스트 객체입니다.

참조 코드: https://github.com/junhkang/springboot-testing-from-zero-to-hero/blob/main/src/test/java/io/github/junhkang/springboottesting/controller/UserControllerTest.java

Mockito를 활용한 UserControllerTest에서 Mock 객체 활용 예시

https://github.com/junhkang/springboot-testing-from-zero-to-hero/blob/main/src/test/java/io/github/junhkang/springboottesting/controller/UserControllerTest.java

2.6 다양한 테스트 어노테이션 및 도구 활용

가독성 향상된 테스트 트리 예시

@Nested@DisplayName을 적절히 사용하면 아래와 같이 가독성이 높은 테스트 트리를 구성할 수 있습니다:

참조 코드: https://github.com/junhkang/springboot-testing-from-zero-to-hero/blob/main/src/test/java/io/github/junhkang/springboottesting/service/impl/JpaOrderServiceImplTest.java

SpringBootTest와 다양한 어노테이션 활용 예시 - OrderControllerTest

https://github.com/junhkang/springboot-testing-from-zero-to-hero/blob/main/src/test/java/io/github/junhkang/springboottesting/service/impl/JpaOrderServiceImplTest.java  

2.7 MockMvc와 WebTestClient를 사용한 웹 레이어 테스트

웹 레이어 테스트를 위해 MockMvcWebTestClient를 사용합니다.

  • MockMvc: Spring MVC를 모킹 하여 웹 애플리케이션의 HTTP 요청 및 응답을 테스트합니다.
  • WebTestClient: WebFlux를 지원하는 비동기식 테스트 클라이언트로, 웹 애플리케이션의 반응형 동작을 검증할 수 있습니다.

참조 코드: https://github.com/junhkang/springboot-testing-from-zero-to-hero/blob/main/src/test/java/io/github/junhkang/springboottesting/controller/UserControllerTest.java

MockMvc를 사용한 Web Layer 테스트 예시 - UserControllerTest

https://github.com/junhkang/springboot-testing-from-zero-to-hero/blob/main/src/test/java/io/github/junhkang/springboottesting/controller/UserControllerTest.java