테스트 대역과 식별할 수 있는 동작

목(테스트 대역)이란?

테스트 대상의 실제 의존성을 대체하는 가짜 의존성

연기를 대신하는 스턴트맨(대역배우) 개념에서 비롯됨

5가지 테스트 대역

제라드 메스자로스(Geard meszaros)는 테스트 대역에 5가지 유형이 있다고 설명함

테스트 대역에는 , 스파이, 더미, 스텁, 페이크 5가지 유형이 있다.

크게 두 유형으로 분류하면 , 스텁으로 구분되어진다.

  • 목(mock): 외부로 나가는 상호작용(사이드 이펙트를 일으키는 액션)을 모방 + 검사

    • mock, spy

  • 스텁(stub): 내부로 들어오는 상호작용(입력 데이터를 제공하는 호출)을 모방

    • stub, dummy, fake

스텁(Stub)

테스트가 수행되기 위해 필요한 입력 데이터를 제공하는 역할

입력 데이터와의 상호작용은 구현 세부사항에 속하므로 검증 테스트를 하지 않아야 한다.

테스트는 리팩터링 내성을 위해 구현 세부사항이 아닌, 최종 결과를 검증해야 한다.

최종 결과를 어떻게 생성하는지는 중요하지 않다. 어떻게가 아닌 무엇에 집중하자.

최종 결과 이외의 불필요한 동작, 세부정보를 검증하는 것을 과잉 명세(overspecification)라 한다.

CQS(Commend Query Separation)

명령 조회 분리 원칙, 모든 메서드는 명령이거나, 조회여야 한다. 둘 다는 안된다.

CQS with React

  • Commend(명령) → 반환값이 없는 사이드이펙트를 호출하는 액션을 의미한다. () => void

    • 목(mock)은 명령을 대체

  • Query(조회) → 반대로 사이드이펙트가 없고 값을 반환한다.

    • 스텁(stub)은 조회를 대체

SideEffect(부수효과)란 값을 반환하는 것 외에 부수적으로 일어나는 효과를 말한다.

외부 변수나 상태를 바꾸는 수정 등이 사이드이펙트에 해당된다.

식별할 수 있는 동작과 구현 세부 사항

모든 코드는 식별할 수 있는 동작구현 세부사항으로 나뉜다.

잘 설계된 API는 식별할 수 있는 동작만을 Public하게 노출하고, 구현 세부사항은 Private하게 감추어야한다. 즉, 캡슐화가 중요하다.

캡슐화가 잘되어있으면 구현 세부사항이 외부로 노출되지 않으므로, 테스트를 작성할 때 구현 세부사항을 검증하는것을 자동으로 방지할 수 있다.

캡슐화(encapsulation)란 외부로부터 객체 내부의 상태와 동작을 감추고, 외부와 상호작용하는 인터페이스만 노출하는 것을 의미한다.

또한 클라이언트가 노출된 구현 세부사항을 이용해 코드의 불변성을 깨트릴수 있기 때문에 캡슐화는 불변성을 지키는 행위이기도 하다.

식별할 수 있는 동작이란?

식별할 수 있는 동작은 클라이언트의 목표에 따라 결정되며, 클라이언트의 목표를 달성하기 위해 직접적으로 도움이 되는 작업 or 상태(데이터)를 의미한다.

예를 들어 클라이언트의 목표가 사용자의 이름을 변경하는 것이라면, 사용자의 이름을 바꾸는 작업이 식별할 수 있는 동작이 되고, 사용자 이름을 어떻게 바꾸는지에 대한 내용은 구현 세부사항이 된다.

class User {
  // 식별할 수 있는 동작
  public name: string;
  // 식별할 수 있는 동작
  public setName(name: string) {
    this.name =  this.nomalizeName(name);
  }
  
  // 구현 세부사항
  // 클라이언트의 목표는 이름을 변경하는 것이지, 어떻게 바꾸는지는 관심 없다.
  private nomalizeName(name: string) { 
    const result = name.trim();
    return result.slice(0, 50);
  }
}

클라이언트는 코드의 위치에 따라 달라진다.

같은 코드베이스, 외부 애플리케이션, 사용자 인터페이스 등이 될 수 있다.

구현 세부사항을 유출하는지 판단하는 Tip

단일 목표를 달성하기 위해 호출 해야하는 연산의 수가 1보다 크면 구현 세부사항을 유출하고 있는지 의심해봐야 한다.

단일 연산으로 단일 목표를 달성하는것이 가장 이상적이다.

도메인 계층, 애플리케이션 서비스 계층

애플리케이션은 보통 도메인 계층과, 도메인을 이용해 외부와 상호작용하는 애플리케이션 서비스 계층으로 구성된다.

도메인 계층은 애플리케이션 중심부로, 다른 애플리케이션과 차별화되는 도메인, 비즈니스 로직이 포함되있다.

애플리케이션 서비스 게층은 도메인 계층 위에 있고, 서비스 계층에서 도메인 계층으로 향하는 단방향 의존성 흐름이다

각 계층의 테스트는 동일한 동작을 서로 다른 수준으로 검증하는 프랙탈 특성이 있다.

애플레케이션 서비스 계층 → 클라이언트에게 제공하는 기능이 어떻게 실행될지를 검증

도메인 계층 → 비즈니스 로직의 동작과, 상태를 검증, (식별하는 동작만을 테스트)

도메인 계층

애플리케이션의 도메인 지식(사용 방법) 모음

  • 도메인 계층은 비즈니스 로직만을 책임져야 하며 다른 모든 책임에서 제외된다.

  • 도메인 계층은 외부 환경과 완전히 독립적이어야 한다 (관심사 분리)

애플리케이션 서비스 계층

실제로 클라이언트에게 가치를 제공하는 기능

  • 애플리케이션 서비스 계층 내에 어떤 비즈니스 로직도 포함되어서는 안된다.

  • 외부 환경과 도메인 계층 사이에서 상호작용을 연결하는 역할

시스템 내부 통신과 시스템 간 통신

애플리케이션은 시스템 내부 통신(구현 세부사항), 시스템 간의 통신(식별할 수 있는 동작)이 있다.

  • 시스템 내부 통신 → 애플리케이션 내의 클래스 끼리 통신하는 것

    • 연산을 수행하기 위한 도메인 클래스끼리의 상호작용은 식별할 수 있는 동작(클라이언트의 목표)에 해당되지 않기 때문에 구현 세부사항에 속한다.

    • 시스템 내부 통신을 검증하기 위해 목을 사용하는 것은 구현 세부사항을 검증하는 것이므로 피해야한다.

  • 시스템 간의 통신 → 서로 다른 애플리케이션 간에 통신하는 것

    • 시스템 외부환경과 통신하는 방식은 식별할 수 있는 동작을 나타낸다.

    • 단, 외부 환경과 통신한 결과를 외부에서 확인할 수 없는 케이스는 구현 세부사항이 된다.

    • 따라서 시스템 간 통신 중 해당 통신의 사이드 이펙트가 외부에서 확인이 될 수 있는 경우만 목을 사용하여 검증하는 것이 좋다. (즉, 테스트를 작성해도 좋다.)

Last updated