인수 테스트 기반 TDD

이번 미션에서는 이전에 학습했었던 ATDD(인수 테스트 주도 개발)에서 한발 더 나아가서,
인수 테스트를 TDD나 리팩터링에 어떻게 활용하는지 경험할 수 있었다.

TDD 에서는 특히 단위 테스트를 정의 해보고,
협력객체의 통합과 고립이라는 측면에서 Classist vs Mockist의 관점의 차이를 많이 알게 되었다.
이러한 과정에서 Test Double 에는 Stub, Fake, Mock 이란 개념이 있다는 것을 알게되었다.
각각 기술과 차이에 대해서는 다른 포스트에서 다루어보려고 한다.
결론은 Top-down, Bottom-up 정답은 없다라는 것이다. 같이 일하는 사람들과 컨벤션이 있다면, 그것을 따르면 되는것이고,
컨벤션이 없다면 각자 취향에 맞춰서 Test double을 사용하거나 진짜 객체를 생성하는 테스트 코드를 작성하라는 것이었다.

리팩터링에서는 테스트 코드의 중요성을 강조했다.
리팩터링 시 테스트 코드를 먼저 작성하면 여러가지 장점들이 존재했다.

  1. 길을 잃지 않는다
    여러 소스코드를 왔다갔다 하다보면 ‘내가 뭘하려고 했더라?’ 오영수 할아버지가 빙의하는 순간이 있다.
    테스트 코드를 먼저 작성해놓으면 헷갈릴 때 리팩터링의 목적을 다시 상기시킬 수 있다.
  2. 사이드 이펙트의 피드백을 바로바로 받을 수 있다
    테스트 코드로 기존 코드를 보호해놨으므로, 사이드 이펙트가 생길 경우 바로바로 확인할 수 있다.
  3. 안심하고 작은 단위로 메서드를 분리
    테스트 코드로 보호했기 때문에, 안심하고 작은 단위로 메서드를 분리할 수 있다.(어차피 큰 테스트 코드 내에서 벗어나지 않으므로)

이런 리팩터링에서 가장 처음 해야 할 것이 바로 이전에 학습했던 인수 테스트작성이었다.

더 나아가서 리팩터링이 완료되어 기능 구현 후 운영관점에서 정책적인 가이드도 제시해주었다.

  • 스트랭글러 패턴(Strangler pattern)
    • 레거시 시스템의 일부를 새로운 애플리케이션이나 서비스로 교체(리팩터링)
    • 일정 기간이 지난 후, 대체된 레거시 기능을 제거한다.(기존 코드를 어느정도 두었다가 안정화되면 삭제)

이런 패턴은 라이브로 운영 되는 서비스를 리팩터링할 때 아주 유용하게 활용 할 수 있다.

회고

이번 미션에서 잘 한 점

강의 내용을 충실하게 이행

강의에서 가이드했던 방식대로

  1. 리팩터링 인수 테스트를 작성
  2. top-down을 베이스로, bottom-up 방식으로 구현

2 가지 프로세스를 충실하게 이행했다.
지하철의 경로를 조회하는 별도 라이브러리를 사용함에 있어서도
학습 테스트를 진행하여 라이브러리 활용 방법에 대해 숙지하고, 바로 활용 할 수 있었다.

예상했던 병목지점이었던 ‘지하철 경로 조회 기능 구현’ 에서는 최단거리 라이브러리를 사용하니 금방 구현할 수 있었다.
하지만, 또 예상외로 ‘요금 조회 기능 구현’ 에서는 나이에 따른 할인율을 직접 찾아보거나 정책을 인터페이스로 추상화 하는 등의
설계관점에서도 꽤나 고민할 것이 많아 예상보다 오래 걸리게 되었다.

그래도 강의의 내용대로 테스트 코드를 먼저 작성하면 좋은 점에 대해 확실히 공감하고 이해할 수 있었다.
중간 중간에 인수 테스트를 보면서 각 기능의 목적을 다시 확인하니 비교적 쾌적하게 기능 구현이나 리팩터링을 진행할 수 있었다.

어려웠던 점

Annotation의 활용

이번 미션에서 어려웠던 점은 인증과 관련된 Custom Annotation 을 적용하는 부분이었다.

토큰 발급 방식은 익숙했었는데, Resolver 부분에서 Annotation 기반으로 인증모듈을 별도로 적용했던 것은
구조를 파악하기 전엔 파격적이게 느껴졌다.

사실 어려웠던점 보다는 흥미로운점에 가까웠다.
이런 깔끔한 방법이 있다는 것을 처음 알게되었고,
Github에서 best-practice 같은 좋은 코드를 많이 찾아봐야겠다는 생각이 들기도 했다.

느낀점

TDD 의 장점을 확실히 느낄 수 있었던 미션이었다.

특히, 리팩터링 시 TDD를 활용한다는 점은 여러 이점이 있는 것처럼 느껴졌다.

다만, 미션의 특성상 TDD와 적합한 구현 난이도, 주제 등을 고려했을 때
이상적인 리팩터링 환경이 아니라면 어떻게 해야하는 지 생각해봐야할 것 같았다.

예를 들면, 기존 테스트 코드가 한개도 없는 상황이라면?, 스트랭글러 패턴을 적용할 수 없는 소스코드라면?
등의 문제는 사실 실제 업무를 보는 환경에서는 빈번하게 발생할 수 있는 상황이다.
테스트 코드가 없는 경우라면 오히려 적용해볼 수 있는 좋은 경우지만,
올드한 코드의 경우나 Service 레이어에 비즈니스 로직이 몰빵되어 있는 그런 경우라면,
사실 Mockito 같은 라이브러리를 써도 테스트 코드 하나가 Service 레이어의 특정 메서드에 집중될 수 밖에 없기 때문이다.

객체지향언어에서의 TDD는 객체지향이라는 장점을 잘 살린 코드에서 잘 적용할 수 있는 기술인 것 같다.