단위 테스트 AAA 패턴

theonde·2022년 10월 4일

AAA 패턴

  • AAA 패턴은 각 테스트를 준비(arrange), 실행(act), 검증(assert)이라는 세부분으로 나눌 수 있다.
예제 코드

public class Calculator {

    public double sum(double first, double second) {
        return first + second;
    }
}
테스트 코드(AAA)

	@Test
    void sumOfTwoNumber() {
        //준비
        double first = 10;
        double second = 20;
        Calculator calculator = new Calculator();

        //실행
        double result = calculator.sum(first, second);

        assertThat(result).isEqualTo(30);
    }
  • 모든 테스트가 단순하고 균일한 구조를 갖는데 도움이 된다. (일관성)

  • 테스트를 쉽게 읽고, 이해할 수 있다.

  • 구조

- 준비 구절에서는 테스트 대상 시스템과 해당 의존성을 원하는 상태로 만든다.

- 실행 구절에서는 SUT에서 메서드를 호출하고 준비된 의존성을 전달하며 (출력이 있으면) 출력 값을 캡처한다.

- 검증 구절에서는 결과를 검증한다.

Given-When_Then 패턴

  • AAA패턴과 유사한 패턴이다. 이 패턴도 테스트를 세 부분으로 나눈다.
- Given: 준비 구절에 해당

- When: 실행 구절에 해당

- Then: 검증 구절에 해당
  • AAA패턴과 차이점은 없다.

  • 프로그래머가 아닌 사람에겐 Given-When-Then 구조가 더 읽기 쉽다.

  • 테스트를 작성할 때 준비 구절부터 시작 후 실행, 검증 하는 방식이 자연스럽고 대부분의 경우에 효과적이다.

  • 검증 구절로 시작하는 것도 가능하다.

    TDD를 실천할 때, 즉 기능을 개발하기 전에 실패할 테스트를 만들 때는 아직 기능이 어떻게 동작할지 충분히 알지 못한다.
    따라서 기대하는 동작으로 윤곽을 잡은 후 개발을 한다.
    특정 동작이 무엇을 해야 하는지에 대한 목표를 생각하면서 시작하는 것이다.

여러 개의 준비, 실행, 검증 구절 피하기

  • 준비, 실행, 검증 구절이 여러개 가 있을 수 있다.
테스트 준비 -> 실행 -> 검증 -> 더 실행 -> 다시 검증

여러 개의 준비, 실행, 검증 구절은 테스트가 너무 많은 것을 한번에 검증한다는 의미다. 이러한 테스트는 나눠서 해결한다.

  • 여러 개의 동작 단위르 검증하는 테스트는 단위 테스트가 아니라 통합 테스트다.

  • 이러한 구조는 피하자. (리팩토링 하자)

  • 실행이 하나면 테스트가 단위 테스트 범주에 있게끔 보장하고, 간단하고, 빠르며, 이해하기 쉽다.

  • 각 동작을 고유의 테스트로 도출하도록 한다.

  • 통합 테스트의 속도를 높이기 위해 여러 실행과 검증이 있는 단일한 테스트로 묶을 수 있다.

    통합 테스트의 경우다.

  • 통합 테스트의 경우에도 속도가 빠르다면, 항상 여러 개의 테스트로 나누는 것이 좋다.

테스트 내 if문 피하기

  • 안티 패턴이다.

  • 단위 테스트든 통합 테스트든 분기가 없는 간단한 일련의 단계여야 한다.

  • if문은 테스트가 한 번에 너무 많은 것을 검증한다는 표시다. (여러개로 나눠야 한다.)

    쓰지 말자.

각 구절의 크기

  • 일반적으로 준비 구절이 가장 크다.

    크기가 많이 클 경우, 같은 테스트 클래스 내 비공개 메소드 또는 별도의 팩토리 클래스를 활용하자.

  • 실행 구절은 보통 코드 한 줄이다. 두 줄 이상인 경우 SUT의 공개 API에 문제가 있을 수 있다.

  • 단일 작업을 수행하는데 두개의 메소드 호출이 필요하다는 것은 문제가 있다.

    테스트 자체는 문제가 되지 않지만 클라이언트에게 메소드 호출을 더 강요해서는 안된다.
    그렇게 하지 않으면 클라이언트 코드가 첫 번째 메소드만 호출하고 두 번째 메소드를 호출하지 않을 때 모순이 생긴다. 이러한 모순을 불변 위반 이라고 하고, 잠재적 모순으로부터 코드를 보호하는 행위를 캡슐화 라고 한다.

  • 캡슐화를 지키자, 클라이언트 코드에 의존하지 말자, 불변 위반을 초래할 수 있는 잠재적인 행동을 제거해야 한다.

-비즈니스 로직은 필수지만, 유틸리티나 인프라 코드는 덜 적용된다.

검증 구절 크기

  • 단일 동작 단위는 여러 결과를 낼 수 있으며, 하나의 테스트로 그 모든 결과를 평가하는 것이 좋다.

  • 검증 구절이 너무 커지는 것은 제품 코드에서 추상화가 누락됐을 수 있다.

    SUT에서 반환된 객체 내에서 모든 속성을 검증하는 대신 객체 클래스 내애 적절한 동등 멤버랄 정의하는 것이 좋다.

테스트 대상 시스템 구별하기

  • 애플리케이션에서 호출하고자 하는 동작에 대한 진입점을 제공한다.

  • 동작은 여러 클래스에 걸쳐 있을 만큼 클 수도, 단일 메소드로 작을 수도 잇다.

    그러나 진입점은 오직 하나만 존재할 수 있다.

  • SUT와 의존성을 구분하는 것이 중요하다.

    테스트 대상을 찾는 데 시간이 많이 든다면 테스트 내 SUT이름을 sut로 하자.

	@Test
    void sumOfTwoNumber() {
        //준비
        double first = 10;
        double second = 20;
        Calculator calculator = new Calculator();

        //실행
        double sut = calculator.sum(first, second); // sut이름 sut

        //검증
        assertThat(result).isEqualTo(30);
    }

준비, 실행, 검증 주석 제거하기

  • AAA패턴을 따르고 준비 및 검증 구절에 빈 줄을 추가하지 않아도 되는 테스트라면 구절 주석들을 제거하라

    주석을 제거하고 빈 줄로 구분하자. 그렇지 않을 경우 주석으로 구분하자.
    의존성에서 SUT를 떼어내는 것이 중요하듯이, 테스트 내에서 특정 부분이 어떤 구절에 속해 있는지 파악하는데 시간이 많이 들이지 않도록 세 구절을 서로 구분하는 것이 중요하다.

profile
개발자ㅋ.ㅋ

0개의 댓글