ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 7. 가치 있는 단위 테스트를 위한 리팩터링
    개발 도서/Unit Testing 2024. 6. 20. 01:25

     

    4, 5장에서는 가치 있는 테스트를 식별하기 위한 기준을 소개했다.
    6장에서는 코드 베이스를 함수형 아키텍처로 전환하고, 출력 기반 테스트를 적용하는 방법을 소개했다.
    이번 장에서는 함수형 아키텍처를 적용할 수 없는 애플리케이션을 포함해,
    더 넓은 범위의 애플리케이션에 대한 방식으로 일반화하여 더 가치 있는 테스트를 작성하는 방법과 관련해 실용적인 지침을 알아본다.

     

    7.1 리팩터링할 코드 식별하기

     

    7.1.1 코드의 네 가지 유형

     

    코드 복잡도

    • 코드에서 의사 결정 지점 수에 따라 명시적으로 그리고 암시적으로 정의된다.
      도메인 유의성
    • 프로젝트의 문제 도메인에 대해 코드가 얼마나 중요한지를 보여준다.
    • 복잡한 코드는 종종 도메인 유의성이 높고 그 반대의 경우도 있지만, 모든 경우에 100%에 해당하지 않는다.

    복잡한 코드와 도메인 유의성을 갖는 코드는 해당 테스트의 회귀 방지가 뛰어나기 때문에 단위 테스트에서 가장 이롭다.

    협력자가 많은 코드를 다루는 테스트는 유지비가 많이 든다.
    이러한 테스트는 협력자를 예상 상태로 만들고 나서 상태나 상호 작용을 확인하고자 공간을 많이 필요로 한다.

    모든 제품 코드는 복잡도 또는 도메인 유의성과 협력자 수에 따라 네 가지 유형의 코드로 분류할 수 있다.

     

    도메인 모델과 알고리즘

    • 단위 테스트에 대한 노력 대비 가장 이롭다.

    간단한 코드

    • 테스트할 가치가 전혀 없다.

    컨트롤러

    • 통합 테스트를 통해 간단히 테스트해야 한다.

    지나치게 복잡한 코드

    • 컨트롤러와 복잡한 코드로 분할해야 한다.

    코드가 중요하거나 복잡할 수록 협력자가 적어야 한다.

     

    7.1.2 험블 객체 패턴을 사용해 지나치게 복잡한 코드 분할하기

     

    험블 객체 패턴은 해당 코드에서 비즈니스 로직을 별도의 클래스로 추출해 복잡한 코드를 테스트할 수 있는 데 도움이 된다. 그 결과, 나머지 코드는 비즈니스 로직을 둘러싼 얇은 험블 래퍼, 즉 컨트롤러가 된다.

     

    육각형 아키텍처와 항수형 아키텍처는 험블 객체 패턴을 구현한다.
    육각형 아키텍처는 비즈니스 로직과 프로세스 외부 의존성과의 통신을 분리하도록 한다.
    함수형 아키텍처는 프로세스 외부 의존성뿐만 아니라 모든 협력자와의 통신과 비즈니스 로직을 분리한다.

     

    코드의 깊이와 너비 관점에서 비즈니스 로직과 오케스트레이션 책임을 생각하라.
    코드는 깊을 수도 있고(복잡하거나 중요함) 넓을 수도 있지만(협력자가 많음), 둘 다는 아니다.

     

    7.2 가치 있는 단위 테스트를 위한 리팩터링하기

     

    예제를 통해 위 그림의 '지나치게 복잡한 코드'를 '도메인 모델 및 알고리즘'과 '컨트롤러'로 분리하는 과정을 소개했다.
    그 과정에서 '팩토리 메서드', '단일 책임 원칙' 등의 키워드가 사용된다.

     

    7.3 최적의 단위 테스트를 위한 커버리지 분석

     

    7.3.3 전제 조건을 테스트해야 하는가?

     

    도메인 유의성이 있으면 전제 조건을 테스트하고, 그 외의 경우에는 테스트하지 않는다.
    해당 도메인에 대해 의미가 있는 전제 조건만 테스트 하라.

     

    7.4 컨트롤러에서 조건부 로직 처리

     

    비즈니스 로직과 오케스트레이션을 분리할 때는 다음과 같이 세 가지 중요한 특성이 있다.

    • 도메인 모델 테스트 유의성 : 도메인 클래스 내 협력자 수와 유형에 대한 함수
    • 컨트롤러 단순성 : 컨트롤러에 의사 결정 지점이 있는지에 따라 다름
    • 성능 : 프로세스 외부 의존성에 대한 호출 수로 정의
      항상 세 가지 특성 중 최대 두 가지를 가질 수 있다.

    비즈니스 로직 도중에 그 중간 결과로 외부 의존성을 호출하고, 다시 비즈니스 로직을 수행할 수도 있다. 이런 경우 육각형 아키텍처가 제대로 작동하지 않는다.
    이 같은 상황에서는 세 가지 방법이 있다.

    1. 외부에 대한 모든 읽기와 쓰기를 비즈니스 연산 가장자리로 밀어내기
      • 컨트롤러를 단순하게 유지하고 도메인 모델 테스트 유의성을 지키지만, 성능이 저하된다.
    2. 도메인 모델에 프로세스 외부 의존성을 주입하기
      • 성능을 유지하고 컨트롤러를 단순하게 하지만, 도메인 모델의 테스트 유의성이 떨어진다.
    3. 의사 결정 프로세스 단계를 더 세분화하기
      • 성능과 도메인 모델 테스트 유의성을 지키지만, 컨트롤러의 단순함을 포기한다.

    7.4.1 CanExecute/Execute 패턴 사용

     

    각 Do() 메서드에 대해 CanDo()를 두고, CanDo()가 성공적으로 실행되는 것을 Do()의 전제 조건으로 한다. 이 패턴은 Do() 전에 CanDo()를 호출하지 않을 수 없기 때문에 컨트롤러의 의사 결정을 근본적으로 제거 한다.

     

    위 방법을 구현하는 방법은 여러가지가 있지만, 도메인 객체 자신이 직접 로직을 실행할 수 있는 상태인지를 확인하고 가능하다면 실행하도록 검증 로직을 추가하는 방법이 가장 좋아 보인다.

     

    7.4.2 도메인 이벤트를 사용해 도메인 모델 변경 사항 추적

     

    도메인 이벤트는 종종 시스템에서 발생하는 중요한 변경 사항을 외부 애플리케이션에 알리는 데 사용된다.

    구현 관점에서 도메인 이벤트는 외부 시스템에 통보하는 데 필요한 데이터가 포함된 클래스다.

     

    도메인 이벤트는 도메인 모델의 중요한 변경 사항을 추적하고 해당 변경 사항을 프로세스 외부 의존성에 대한 호출로 변환한다. 이 패턴으로 컨트롤러에서 추적에 대한 책임이 없어진다.

     

    도메인의 비즈니스 로직에서 변경 사항이 감지될 때 이벤트를 저장, 컨트롤러는 저장된 이벤트를 메시지 버스의 메시지로 반환한다.

     

    7.5 결론

     

    추상화할 것을 테스트하기보다는 추상화를 테스트하는 것이 더 쉽다.
    도메인 이벤트는 프로세스 외부 의존성 호출 위의 추상화에 해당한다.
    도메인 클래스의 변경은 데이터 저장소의 향후 수정에 대한 추상화에 해당한다.

Designed by Tistory.