본문 바로가기

Develop

[Test] 런던파 vs 고전파

반응형

들어가면서

단일 테스트의 필요성은 많이 알려져있습니다. 테스트 코드를 작성할 때, 개발자가 고민해야 할 점들을 정리해두고자 합니다.

 

단위 테스트란?

  • 단위 테스트란:
    • 작은 코드 조각(단위)을 검증하고,
    • 빠르게 수행하고,
    • 격리된 방식으로 처리하는 자동화된 테스트입니다.

여기서 "격리된 방식"에 대하여 두가지 관점이 존재합니다. 책 "테스트 주도 개발"을 지은 켄트 백의 입장을 표명하는 고전파의 관점과, 런던의 개발 커뮤니티에서 시작된 런던파의 관점이 있습니다. 이때 런던파를 칭하는 표현으로 목 추종자(좋은 의미로 쓰이진 않는 듯 합니다. 하지만 런던파가 어떤 방식을 선호하는지 확 와닿습니다)가 있습니다.

 

격리된 방식에 대한 두 분파의 관점

각 분파의 차이에 대한 요약표를 살펴보도록 하겠습니다.

  격리 주체 단위의 크기 테스트 대역 사용 대상
런던파 단위 단일 클래스 불변 의존성 외 모든 의존성
고전파 단위 테스트 단일 클래스 또는 클래스 세트 공유 의존성
  • 테스트 대역 (test double)이란
    • 배우 담당 스턴트맨을 영어로 stunt double이라고 합니다. 여기선 실제 의존성 대신 가짜 의존성을 사용한다는 의미에서 test double이라는 이름이 붙었습니다.
    • 정의: 테스트 대역은 실행과 관련 없이 모든 종류의 가짜 의존성을 설명하는 포괄적인 용어로, 하나의 종류로 "Mock"이 있다.
  • 의존성이란 
    • 공유 의존성 (shared dependency):
      • 테스트 간에 공유되고 서로의 결과에 영향을 미칠 수 있는 수단을 제공하는 의존성
      • 정적 가변 필드, 데이터 베이스가 전형적인 예입니다.
    • 비공개 의존성 (private dependency):
      • 공유하지 않은 의존성
      • 싱글턴 의존성은 각 테스트에서 새 인스턴스를 만들 수만 있으면 공유되지 않습니다. 그렇기 대문에 일반적으로 이러한 의존성은 비공개 의존성입니다.
    • 프로세스 외부 의존성 (out-of-process dependency):
      • 애플리케이션 실행 프로세스 외부에서 실행되는 의존성이며, 아직 메모리에 없는 데이터에 대한 프록시입니다.
      • 프로세스 외부 의존성은 대부분 공유 의존성에 해당합니다. DB가 하나의 예입니다.
      • 데이터베이스나 파일 시스템 등의 공유 의존성에 대한 호출은 비공개 의존성에 대한 호출보다 더 오래 걸립니다. 이러한 호출을 포함하는 공유 의존성을 가진 테스트는 통합 테스트 영역으로 넘어갑니다.
    • 불변 의존성:
      • 불변 객체에 대한 의존성
      • ENUM 클래스와 같은 불변 객체는 런던파도 격리하지 않습니다. 하지만 이 외에는 모두 격리합니다.

의존성 (출처: https://www.kimcoder.io/books/unit-testing/2)

런던파 vs 고전파

고전파는 단위 테스트를 격리합니다.

void Purchase_succeeds_when_enough_inventory(){
	// 준비
    Store store = new Store();
    store.addInventory(Product.Shampoo, 10);
    Customer customer = new Customer();
    
    // 실행
    bool success = customer.Purchase(store, Product.Shampoo, 5);
    
    // 검증
    Assert.True(success);
    Assert.Equal(5, store.GetInventory(Product.Shampoo)); // <- 상점 제품 5개 감소한 지 확인
}

코드에서 볼 수 있듯이, 위 테스트 코드의 테스트 대상은 "customer.Purchase()"라는 동작(메서드)입니다. Store 클래스의 객체는 테스트 대상과 의존성을 맺고 있습니다. 하지만 비공유 의존성 (변경가능한 의존성이긴 하지만)이라 격리 대상이 아니라 실제 객체를 생성해서 사용합니다.

런던파는 협력자를 모두 격리합니다.

협력자는 공유하거나 변경 가능한 의존성입니다. 

//C# 코드를 java 스타일로 변경한 코드입니다. 흐름 파악 목적 정도로만 봐 주세요.
void Purchase_succeeds_when_enough_inventory(){
	// 준비
    Store storeMock = new Mock<IStore>();
    storeMock
    	.Setup(x => x.HasEnoughInventory(Product.Shampoo, 5))
        .Returns(true);
	Customer customer = new Customer();
    
    // 실행
    bool success = customer.Purchase(storeMock.Object, Product.Shampoo, 5);
    
    // 검증
    Assert.True(success);
    storeMock.Verify( x => x.RemoveInventory(Product.Shampoo, 5), Times.Once);
}

런던파에서는 테스트 대상이 단일 클래스입니다. 그렇기 때문에 테스트 대상인 Customer 클래스 외의 다른 클래스들, 즉 모든 협력자들을 테스트 대역을 사용합니다. 여기서는 Store 객체를 Mock을 활용하여 테스트합니다.

  • 장점
    • 입자성이 좋다. 테스트가 세밀해서 한 번에 하나의 클래스만 확인합니다.
    • 서로 연결된 클래스의 그래프가 커져도 테스트하기 쉽습니다. 모든 협력자가 테스트 대역으로 대체되기 대ㅜㅁㄴ에 테스트 작성 시 걱정할 필요가 없습니다.
    • 테스트가 실패하면 어떤 기능이 실패했는지 확실히 알 수 있습니다.

개념 정리

  • 테스트 스위트 구조
    • 제품 코드의 각 클래스에 대해 테스트 클래스가 하나씩 있는 구조

 

 

 

반응형

'Develop' 카테고리의 다른 글

[Test] 단위 테스트 구조  (0) 2023.10.16
[Develop] Redis 캐시로 사용하기  (0) 2023.10.07