본문 바로가기
독서이야기/엘레강트 오브젝트 - 새로운 관점에서 바라본 객체지향

[엘레강트 오브젝트] 2.8 모의 객체(Mock) 대신 페이크 객체(Fake)를 사용하세요

by 사랑꾼이야 2023. 3. 12.
반응형

이 내용은 엘레강트 오브젝트 를 읽으면서 정리한 내용을 포함하고 있습니다.

  • 페이크(Fake)
  • 한걸음 더 나아가기
  • 정리

페이크(Fake)

테스트를 최적화하기 위한 도구인 모킹(Mocking)을 많이 사용하는데, 하지만 모킹 대신 페이크 객체를 사용할 것을 제안한다.

  • 페이크 클래스는 인터페이스의 일부이며 인터페이스와 함께 제공한다.
  • 페이크 클래스가 실제 클래스보다 더 복잡한 경우도 존재한다.
  • 페이크 클래스를 만족하도록 테스트를 작성하지 말고, 페이크 클래스가 테스트를 올바르게 지원하도록 작성한다.

프로덕션 코드 변경 시 단위 테스트 코드를 수정하지 않으면 실패하게 된다.

  • 단위 테스트도 신경써서 수정해야 한다.

페이크 클래스가 존재하는 상황에서 해당 인터페이스를 변경하기 위해서는 자연스럽게 해당 인터페이스 클래스의 구현도 함께 변경해야 한다.

  • 페이크 클래스를 사용하면 테스트를 충분히 유지보수 가능하게 만들 수 있다.

실습을 통해서 좀 더 자세히 알아보도록 하겠다.

실습 환경

  • Java 11
  • Spring Boot 2.7.2
  • Gradle 7.5
  • Database - H2
  • MessageQueue - RabbitMq

실습해보기

먼저는 @Mock 을 통해 테스트 코드를 작성하였을때를 알아보도록 하겠다. 회원 서비스인 CustomerService 에서는 회원가입 서비스도 제공하고 있다.

회원가입 시 가입 정보를 처리를 Queue를 통해서 전달한다고 가정해보자.

서비스코드
  • Queue의 메세지를 보낼때 만약 메세지 전송의 실패가 발생하면 RuntimeException 으로 감싸는 것이 잘 수행되는지를 테스트 하고 싶다.
  • CustomerService 에서 의존중인 다른 모듈둘에 대해서 회원가입에서는 사용하고 있지 않다. 오직 rabbitTemplate 만 사용하고 있다.
    • CustomeRepository, CustomerOrderRepository, ProductService 등

익숙한 방식인 @Mock 은 다음과 작성해볼 수 있다.

Mock_단위테스트코드
  • 보시는 것과 같이 간단하게 작성할 수 있는데, 간단하게 작성할 수 있다는 것이 포인트이다.
  • 대상 객체인 CustomerSerice 가 테스트 하기 어려울 정도로 의존성이 결합되어 있지만 아주 간단하게 테스트 코드가 작성된 것이다.

@Mock 이 없이 테스트 코드를 작성한다면 어떻게 작성할 수 있을까?

Mock없이_단위테스트코드
  • 의존해야될 대상이 많다보니 자연스럽게 Mocking 해야할 대상이 너무 많다.
  • CustomerService 가 의존하고 있는 모든 Bean 들의 MockClass 생성이 필요해서 테스트 코드 작성하기가 정말 힘들다.

테스트 하기 좋은 코드

그럼 테스트 하기 좋은 코드로 될려면 어떻게 바꿔야할까?

  • 테스트 대상인 sendMessage 메소드가 의존하고 있는 의존성은 RabbitTemplate 뿐이다.
  • 그래서 Queue 에 메세지를 보내는 sendMessage를 별도의 컴포넌트로 분리하면 된다.

다음과 같이 변경해볼 수 있다.

Mock없이_단위테스트코드
  • 새로운 서비스인 AmqpMessageSender 를 생성하면서 RabbitTemplate 의존성을 가져왔다.
  • 더 이상 많은 모듈을 의존하지 않고 실제 사용하는 서비스의 모듈만 의존하고 있다.

이전보다 테스트 코드 작성하기가 굉장히 쉬워졌다.

Mock없이_단위테스트코드
  • 분리된 해당 클래스만 테스트 하면 된다.
  • 한걸음 더 나아가기

Mock만 사용하지 않는다고 해결이 되는 것은 아니다.

  • 테스트 코드를 작성하면서 가장 염려해야될 부분이 프로덕션 코드와 테스트 코드가 같이 움직이지 않는 것이다.
  • 그러기 때문에 프로덕션 코드의 내용이 변경된다면 테스트는 실패하게 된다.
  • 해당 챕터에서는 Fake 클래스에 대해서 제안하고 있다.

아래와 같이 기존에 구현한 클래스의 인터페이스를 추가하고 Fake inner 클래스를 생성하도록 하겠다.

Mock없이_단위테스트코드
  • 기존 프로덕션 인터페이스의 테스트 코드에서 사용할 동작을 지정한다.

테스트 코드는 다음과 같이 작성할 수 있다.

Mock없이_단위테스트코드
  • 프로덕션 코드가 변경된다면 컴파일 에러를 통해서 같이 수정이 일어나게 된다.
  • 프로덕션 코드와 테스트 코드가 같이 움직인다.

정리

  • 페이크 클래스가 존재하는 상황에서 해당 인터페이스를 변경하기 위해서는 자연스럽게 해당 인터페이스 클래스의 구현도 함께 변경해야 한다.
  • 페이크 클래스를 사용하면 테스트를 충분히 유지보수 가능하게 만들 수 있다.

관련해서 생각해볼 내용

  • 운영 인터페이스 코드에 테스트 관련 소스를 내보내는 것이 과연 안전한 방법인가?
반응형

댓글