티스토리 뷰

반응형

 

테스트 코드가 작성하는 것이 어렵다고 많이 이야기한다. 테스트 코드가 없는 상태로 성장한 프로젝트들, 그리고 외부 의존성(DB, 외부 API, 파일 쓰기 등)이 많은 프로젝트들의 코드가 보통 테스트가 어려운 경우가 많다. 과연 그때 우리는 어떤 선택을 해야할까?

추측하건데 보통은 그냥 작성하고 배포한 후, 직접 API를 호출해보거나 확인해볼 수 있는 UI를 통해서 테스트를 해볼것이다. 이런 사용자 테스트는 비용이 크다. 테스트할 여러가지 상황을 재현하기도 쉽지 않을 수 있고, 버그 발견 후 수정하고 다시 확인하는데도 오래 걸린다.

혹자는 실 사용자에게 테스트를 넘길 수도 있다. 테스트 없이 그냥 서비스를 배포하는 것이다. 이런 상황에서 예상되는 결과는 다음과 같다. 

  1. 문제없이 기능이 배포 되었다. 
  2. 문제가 발생해서 사용자에게 오류 리포트를 받는다.
  3. 문제가 발생했지만 피드백을 받지 못하고 나중에 알게된다.

진짜 3번은 최악인데, 은근 이런 일들이 발생한다. 고백하건데 필자도 이런 경험이 있다.

결국 문제는 "테스트가 어려운 상황에서 어떻게 최대한 적은 비용으로 테스트하여 안전하게 기능을 배포할 것이냐" 이다. 이 문제는 매우 자주 발생하는 개발자의 실생활 문제이고 이 문제를 잘 다루는 개발자가 결국 뛰어난 개발자가 될 것이다. 

최근 사내에서 스터디한 'Unit Testing Principles, Practices, and Patterns' 이 책에서 앞서 언급한 문제에 대한 해결책을 알게 되었다. 이 해결책이 어렵지 않지만 개인적으로는 큰 인사이트였다. 그래서 간단하게 적어보려고 한다.

Humble Object Pattern 소개

We extract the logic into a separate easy-to-test component that is decoupled from its environment.

테스트하기 어려운 의존성을 가지고 있고, 양이 많은 코드가 있다. 구체적으로 예를 들어 보면, DB에서 데이터를 조회하여 특정 로직을 통해서 값을 변경하고 다시 저장하고 그 결과를 외부 API로 호출한다. 여기서 쉽게 테스트할 수 있는 로직은 if-else 같은 분기문으로 표현될 수 있는 로직이다. 그리고 보통 이 로직이 개발자가 중요하게 테스트해야할 비즈니스 로직일 가능성이 높다! 이 로직들을 (입력과 출력만 있고 다른 행위를 하지 않는) 순수한 함수의 형태로 추출해 보면 쉽게 테스트가 가능하다. 왜냐하면 복잡한 의존이 없는 함수이기 때문이다. 그리고 기존 코드는 비즈니스 로직을 함수에 위임하는 형태로 변하게 되고 단순히 흐름을 제어하는 형태가 된다. 그 흐름만 제어하는 객체를 Humble(겸손한) 오브젝트라고 한다.

이러한 형태로 흐름을 제어하는 코드와 실제 비즈니스 로직을 수행하는 코드를 구분하는 방식은 다른 프로그래밍 패턴(MVC, MVP 등)이나 방식의 근간이 되었다. 

Humble Object Pattern 개념의 중요성

이 패턴은 어려운 내용은 아니다. 그냥 쉽게 테스트 가능한 로직을 추출해내고 테스트 하면 된다. 이 패턴은 쉽지만 개발자들에게 매우 중요하다고 생각한다. 내가 이 개념을 중요하다고 생각하는 이유는 바로 실용적이기 때문이다.

이 패턴은 어떤 소프트웨어(서버 소프트웨어, 프론트앤드 소프트웨어 등등)를 만드는 개발자이건 상관 없이 바로 적용가능한 방법이다. 테스트가 하기 어려운 환경과 중요한 비즈니스 로직은 어떤 소프트웨어든 가지고 있기 때문이다. 

또한 이 패턴으로 추출해낸 로직을 단위 테스트하는 것은 가치가 매우 크고 비용이 적다. 이 테스트는 분기문의 형태로 드러나는 비즈니스 로직을 테스트한다. 해당 비즈니스 로직은 순수 함수 형태로 작성되고 여러 케이스의 입력을 통해 반환된 출력을 검증하는 형태로 테스트를 작성할 것이다. (그렇게 해야한다!) 당연하게도 이런 함수를 테스트 하는 것은 매우 쉽고 빠르다. 이 테스트를 통해서 비즈니스 로직 수정을 통해 발생한  버그(회귀, regression)를 쉽게 찾을 수 있다. 당연히 리팩토링에도 도움이 될 것이다.

그렇다면 "외부 의존성은 자동화 테스트를 어떻게 하냐"라고 묻는 다면 외부 의존성을 모사(mocking)하거나 직접 환경을 구성하여 통합테스트를 작성하여 테스트할 수도 있을 것이다. 그러나 비용 대비 가치가 없을 수 있다. 잘 판단해서 통합 테스트 환경을 구축하거나 그냥 손으로 테스트하면 된다. 

개인적으로는 비즈니스 로직에 대한 다양한 케이스에 대해서는 단위 테스트를 한 후, 단순한 케이스에 대해서 실제 API 호출로 테스트를 하는 편이다. 단위 테스트 만으로도 자신감이 많이 올라간다.

정리, 그래서 어떻게 하라고??!!

흐름을 제어하는 코드와 실제 비즈니스 로직을 수행하는 코드를 구분하는 방식, 이 개념을 가지고 실제로 코드를 그렇게 구분하고 테스트하는 것이 중요하다. 만약 좋은 코드를 작성하는데 객체지향의 원칙이나 클린코드가 모호하다고 느껴졌다면 여기서부터 시작하는 것을 제안한다.

  1. 복잡한 프로덕션 코드에서 분기문으로 표현되는 비즈니스 로직을 함수나 다른 객체로 추출한다.
  2. 추출한 로직을 쉽게 테스트한다.

 

아마 하면서 분명히 어려운 순간들이 있을 텐데, 댓글이나 여러방법으로 질문 주시면 답변드려볼게요.

반응형
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함