목차

런던파

주요 특징

  • Mock 사용: Mock 객체로 외부 의존성을 대체하여 단위 테스트를 독립적으로 수행
    • 특정 객체나 메서드의 동작을 검증하기 위해, 의존성을 실제 구현 대신 Mock 객체로 대체하여 테스트
  • 행동 기반 테스트 (Behavior Verification): 객체가 올바른 메서드를 호출했는지, 호출된 횟수는 적절한지 등을 확인
  • 격리성: 테스트 대상 객체는 의존성과 완전히 격리
  • 단위 테스트에 집중: 특정 기능이나 메서드의 동작을 작게 쪼개어 검증

장점

  • 다른 모듈/시스템의 변경에 영향을 받지 않음
  • 특정 메서드 호출이나 내부 동작 검증에 적합

단점

  • Mock이 많아지면 테스트 코드가 복잡해지고, 유지보수가 어려워질 수 있음
  • 실제 시스템과의 상호작용을 놓칠 가능성 있음
  • Mock 객체에 의존하여 테스트가 비현실적

고전파

주요 특징

  • 상태 기반 테스트 (State Verification): 테스트 결과로 반환된 상태나 값을 검증.
  • 실제 구현 사용: 의존성에 Mock을 사용하는 대신, 가능한 실제 구현체나 시스템을 사용
  • 결과 중심: 행동(메서드 호출)이 아닌 결과 상태(출력, 반환값 등)를 검증
  • 통합 테스트 지향: 시스템 전체 흐름을 테스트하는 데 적합

장점

  • 실제 의존성을 사용하기 때문에 현실에 가까운 테스트를 수행가능
  • 시스템 전반의 통합 및 작동 여부를 확인하기에 적합

단점

  • 외부 시스템의 변경에 민감하며, 테스트의 안정성이 떨어질 수 있음
  • 특정 메서드 호출이나 내부 동작을 검증하기 어려움
  • 의존성 때문에 테스트 속도가 느려질 수 있음

 

런던파와 고전파의 비교

  런던파 고전파
핵심 특징 Mock을 사용하여 독립적이고 행동 중심의 테스트 수행 실제 의존성을 사용하여 상태 중심의 테스트 수행
검증 방식 메서드 호출, 행동 검증 상태와 출력검증
테스트 범위 작은 단위 (메서드 단위) 통합 또는 큰 단위
속도 빠름 ( 외부 시스템 미사용) 느림(외부 시스템 사용 시)
테스트 안정성 외부 변화에 둔감 외부 시스템 변경시 취약
유지 보수성 Mock 이 많아지면 복잡 간결하지만 외부 의존성 변경시 취약
적용 사례 단위 테스트, 서비스 내부 로직 테스트 통합 테스트, 시스템 흐흠 검증
적합한 경우 테스트 속도가 중요할 때
객체의 행동을 검증할 경우
외부 의존성이 복잡하거나 불안정한 경우
전체 시스템의 실제 동작과 통합을 검증할 경우
현실적인 테스트 환경을 유지하려는 경우
주요 관심사가 결과 상태와 출력 일때

 

현대적인 테스트 접근 방식에서는 두 방법론을 혼합하여 사용하는 경우가 많으며, 테스트의 목적과 환경에 따라 적절히 선택하는 것이 중요

'TDD > 개념' 카테고리의 다른 글

[ TDD ] [ Fixture ] Test Fixture?  (0) 2024.11.16

목차

Test Fixture 란

소프트웨어 테스트에서 테스트 환경을 설정하고 관리하기 위한 코드 집합을 의미합니다. 테스트가 정확하고 일관성 있게 실행될 수 있도록 준비하는 과정입니다. 이는 테스트를 위해 필요한 모든 상태나 환경을 포함합니다. 예를 들어, 데이터베이스를 설정하거나, 필요한 객체를 생성하거나, 특정 상태로 초기화하는 작업이 포함됩니다.

Test Fixture를 구성하는 기본 원칙

  • 독립적인 테스트 환경 구성: 이전 테스트의 실행이 이후 테스트에 영향을 주지 않도록 하기 위해, beforeEach 또는 afterEach를 사용해 환경을 초기화하고 정리
  • Setup과 Teardown 구분 
    • SetUp : beforeAll, beforeEach 메서드를 사용하여 필요한 초기 설정
    • Teardown : afterAll, afterEach를 사용해 자원을 정리
  • 재사용 가능한 코드 작성: 공통된 설정이나 데이터 준비 작업을 별도의 헬퍼 함수나 클래스로 추출하여, 필요할 때 재사용할 수 있게 작성

Test Fixture를 사용하는 이유

  1. 테스트가 항상 동일한 조건에서 실행되며, 테스트의 독립성과 신뢰성을 보장
  2. 사용하면 코드가 간결해지고, 유지보수성이 높아지며, 자동화된 테스트 실행이 용이

예제 상황

  1. 데이터베이스 테스트: 데이터베이스를 사용하는 애플리케이션의 경우, 각 테스트 전에 데이터베이스를 특정 상태로 초기화하고, 테스트 후에 정리하는 작업을 Test Fixture로 구성
  2. HTTP 요청 테스트: API 테스트에서는 각 요청에 대한 환경 설정(예: 모의 데이터 또는 인증 토큰 설정)을 Test Fixture로 구성

NestJS에서의 Test Fixture

NestJS에서 테스트를 작성할 때, beforeEach, afterEach, beforeAll, afterAll을 사용하여 테스트 픽스처를 구성할 수 있습니다. 주로 TestingModule을 사용하여 애플리케이션 모듈의 인스턴스를 생성하고, 의존성을 주입하는 작업이 포함

 

구성 요소 설명

  • TestingModule: NestJS의 테스트 모듈을 생성하여 의존성을 주입하고, 각 컴포넌트가 올바르게 동작하는지 확인합니다.
  • beforeAll: 테스트 스위트 내에서 한 번만 실행되는 초기화 코드입니다. 여기서 TestingModule을 컴파일하고 컨트롤러와 서비스를 인스턴스화합니다.
  • beforeEach: 각 테스트가 실행되기 전에 실행되어, 테스트 상태를 초기화하거나 Jest의 mock을 재설정합니다.
  • afterAll: 테스트가 모두 종료된 후 한 번 실행되어, 테스트 환경을 정리하거나 자원을 해제합니다.

NestJS와 Jest를 사용하여 테스트 픽스처를 구성하는 방법을 보여주는 예제

import { Test, TestingModule } from '@nestjs/testing';
import { MyService } from './my.service';
import { MyController } from './my.controller';

describe('MyController', () => {
  let controller: MyController;
  let service: MyService;

  beforeAll(async () => {
    // 모든 테스트 전에 실행되는 setup 작업 (한 번만 실행)
    const module: TestingModule = await Test.createTestingModule({
      controllers: [MyController],
      providers: [MyService],
    }).compile();

    controller = module.get<MyController>(MyController);
    service = module.get<MyService>(MyService);
  });

  beforeEach(() => {
    // 각 테스트 전에 필요한 초기화 작업 수행
    jest.clearAllMocks(); // Jest의 모든 mock 초기화
  });
  
  afterEach(() => {
    // 각 테스트 후 자원 해제나 초기화 작업
  });
  
  // 테스트
  it('should return expected data', async () => {
    const result = 'expected result';
    jest.spyOn(service, 'getData').mockImplementation(() => result);

    expect(await controller.getData()).toBe(result);
  });
  

  afterAll(() => {
    // 모든 테스트 후에 실행되는 teardown 작업 (한 번만 실행)
    // 테스트 모듈 정리 또는 자원 해제
  });
});

 

 

반복되는 코드를 모두 Fixture를 만들어서 해야 할까?

 

경우에 따라 다르겠지만, 테스트 상황에서 기본적으로 몰라도 되는 확인해보지 않아도 되는 코드는  Fixture를 생성해서 구현하는 것이 좋아 보인다. 예를 들어, 데이터 베이스 초기화같은 것이 있겠다.

 

하지만, 유저 생성, 토큰생성 등을 beforeAll 이나 beforeEach 에 구현해 버리면 각 테스트 마다 확인하기 위해 코드의 위 아래를 오가야하므로 오히려 비효율적이라고 생각한다.

 

차라리, 해당 기능들을 함수나 클래스를 만들어서 각 테스트에서 호출하여 사용하는 방법이 해당 테스트를 더 빨리 이해할 수 있을 것 같다.

  

 

'TDD > 개념' 카테고리의 다른 글

[ TDD ] 런던파 vs 고전파  (0) 2024.12.02

+ Recent posts