본문 바로가기

Development/Tip

[Tip]테스트 코드를 작성하는 이유, Given When Then, FIRST 속성

이미지 출처: https://miro.medium.com/v2/resize:fit:1400/1*v-usjN8zMK1JLJKV9Dzwuw.png

테스트 코드

테스트 코드는 우리가 작성한 코드나 비즈니스 로직 자체를 테스트하기 위해 작성된 코드를 의미한다.
Agile 방법론 중 하나인 TDD(Test Driven Development)도 등장한 만큼, 테스트 코드를 더 잘 작성하고 활용하기 위해 고민해 볼 필요가 있다.

테스트 코드의 중요성

개발 과정에서 문제를 미리 발견할 수 있다.

  • 문제를 미리 발견함으로써 문제 상황을 실제 서비스 배포 이전에 방지할 수 있다.
  • 문제가 발생할 수 있는 여러 상황에 맞춰 테스트 코드를 작성하는 것이 중요하며, 이를 통해 잠재된 문제를 발견하는 것에 도움이 된다.
  • 일부러 오류가 발생할 수 있는 테스트 코드를 작성하여 예외 처리가 잘 작동하는지를 확인하거나, 정확히 의도한 비즈니스 로직에 맞춰 테스트 코드를 작성해서 결괏값이 잘 나오는지 검토하는 등 여러 테스트 코드를 통해 애플리케이션 코드를 검증해야 한다.

리팩토링의 리스크가 줄어든다.

  • 일반적으로 애플리케이션이 개발되면 서비스 업데이트를 위해 계속해서 코드를 추가하고 수정하는 작업을 진행한다.
  • 이는 다른 코드에 영향을 줄 수 있는 작업인데, 테스트 코드가 없다면 코드가 수정될 때 마다 수시로 애플리케이션의 동작을 검토해야 한다. 테스트 코드는 이러한 부작용에 대비할 수 있다.

하나의 명세 문서로서의 기능을 수행한다.

  • 개발자들은 협업을 통해 프로젝트를 진행하며, 다른 사람이 작성한 코드를 바로 이해하는 것은 쉽지 않다. 그러므로 코드를 이해하기 쉽게 개발자가 직접 명세를 작성하고 공유하는 과정을 거쳐야 하는데, 이때 테스트 코드는 큰 도움이 된다.
  • 동작 검증을 위해 작성돼 있는 테스트 코드를 애플리케이션 코드와 비교하면서 보면 작성자 의도가 파악되어 동료의 코드를 이해하기가 쉬워진다.

이외에도 다음과 같은 장점들이 있다.

  • 애플리케이션을 직접 실행시켜 테스트하는 것보다 속도가 빠르다.
  • 코드가 작성된 목적을 명확하게 표현할 수 있으며, 불필요한 내용이 추가되는 것을 방지한다.

테스트 코드를 작성하는 방법

Given-When-Then

Given-When-Then 패턴은 테스트 코드를 표현하는 방식 중 하나이며, 각 단계의 목적에 맞게 코드를 작성한다.

  • Given: 테스트를 수행하기 전에 테스트에 필요한 환경을 설정하는 단계이다. 테스트에 필요한 변수를 정의하거나 Mock 객체를 통해 특정 상황에 대한 행동을 정의한다.
  • When: 테스트의 목적을 보여주는 단계이다. 실제 테스트 코드가 포함되며, 테스트를 통한 결괏값을 가져오게 된다.
  • Then: 테스트의 결과를 검증하는 단계이다.

Given-When-Then 패턴은 테스트 주도 개발(TDD)에서 파생된 BDD(Behavior-Driven-Development; 행위 주도 개발)를 통해 탄생한 테스트 접근 방식이다.

일반적으로 단위 테스트보다는 비교적 많은 환경을 포함해서 테스트하는 인수 테스트에서 사용하는 것에 적합하다고 알려져 있지만 단위 테스트에서도 유용하게 활용할 수 있다.

 

GIVEN-WHEN-THEN 패턴을 사용한 테스트 코드 예시

@Test
public void shouldReturnProductById() {
    // Given
    Long productId = 1L;

    // When
    Product product = productService.getProductById(productId);

    // Then
    assertThat(product).isNotNull();
    assertThat(product.getName()).isEqualTo("Product 1");
    assertThat(product.getPrice()).isEqualTo(BigDecimal.valueOf(9.99));
}

좋은 테스트를 작성하는 5가지 속성: F.I.R.S.T

F.I.R.S.T는 테스트 코드를 작성하는 데 도움이 될 수 있는 5가지 규칙을 의미한다. 이 규칙은 대체로 단위 테스트에 적용할 수 있는 규칙이며, 각 속성은 다음과 같다.

Fast

  • 테스트는 빠르게 수행돼야 한다.
  • 테스트가 느리면 코드를 개선하는 작업이 느려져 코드 품질이 떨어질 수 있다.
  • 테스트 코드의 목적을 단순하게 설정해서 작성하거나 외부 환경을 사용하지 않는 단위 테스트를 작성하는 것 등을 빠른 테스트라고 할 수 있다.

Isolated

  • 하나의 테스트 코드는 목적으로 여기는 하나의 대상에 대해서만 수행돼야 한다.
  • 만약 하나의 테스트가 다른 테스트 코드와 상호작용하거나 관리할 수 없는 외부 소스를 사용하게 되면 외부 요인으로 인해 테스트가 수행되지 않을 수 있다.

Repeatable

  • 테스트는 어떤 환경에서도 반복 가능하도록 작성해야 한다.
  • 테스트는 개발 환경의 변화나 네트워크의 연결 여부와 상관없이 수행돼야 한다.

Self-Validating

  • 테스트는 그 자체만으로도 테스트의 검증이 완료돼야 한다.
  • 테스트가 성공했는지 실패했는지 확인할 수 있는 코드를 함께 작성해야 한다.
  • 만약 결괏값과 기댓값을 비교하는 작업을 코드가 아니라 개발자가 직접 확인하고 있다면 좋지 못한 테스트 코드이다.

Timely

  • 테스트 코드는 테스트하려는 애플리케이션 코드를 구현하기 전에 완성돼야 한다.
  • 너무 늦게 작성된 테스트 코드는 정상적인 역할을 수행하기 어려울 수 있다.
  • 또한 테스트 코드로 인해 발견된 문제를 해결하기 위해 소모되는 개발 비용도 커지기 쉽다.
  • 단, 이 개념은 테스트 주도 개발 원칙을 따르는 테스트 작성 규칙으로, 테스트 주도 개발을 기반으로 개발하는 것이 아니라면 이 규칙을 제외하고 진행하기도 한다.

참고 자료

  • 스프링 부트 핵심 가이드 "스프링 부트를 활용한 애플리케이션 개발 실무" , 장정우, 2022