본문 바로가기
가치관 쌓기/개발 돌아보기

[우아한테크코스Pro] 인수 테스트 기반 TDD - 1 [5/9]

by simplify-len 2021. 7. 1.

5주차 인수테스트 기반 TDD에서는 3주차에서 했던 인수테스트 주도 개발 에서 말하는 인수 테스트 이후 개발 흐름에 대해서 미션이 주어졌습니다.

 

인수테스트 > 테스트 주도 개발

 

 이번 미션에서는 총 4단계로 구성되어 각 단계마다 많은 코드 작성이 요구되었습니다.

 

이번 미션 목표

  • 테스트 주도 개발 접근 방법을 이해할 수 있습니다.
  • 단위 테스트에 대한 이해를 높일 수 있습니다.

 

테스트 주도 개발을 접근하는 방법으로 크게 2가지가 있습니다.

1. Outside In

일반적으로 TDD는 Inside-Out 의 방식을 말합니다. 그러므로, Outside-In는 Top Down으로 진행하고, London School TDD라고도 부르고, Mockist Approach라고도 불린다고 합니다.

 

아래 그림 1 에서 보여지는 것과 같이 최상위 레이어에서 시작해서 최하위 레이어로 향해 가면서 TDD를 진행합니다.  구체적으로는 시스템 외부에 대한 첫 번째 테스트를 작성하면서 시작합니다. 서버라면 WebMvc 또는 외부 API 테스트 일 수 있습니다.

 

Outside In 개발 방식의 가장 중요한 부분은 테스트를 통해 구현하고자 하는 객체의 인터페이스를 먼저 만들고 이와 협력 객체(ex, mocks, stubs)를 통해 개발을 이어가는 것입니다. 협력 객체에 대해서 예측하면서 테스트 할 대상과 협력객체들 사이의 상호작용 을 고려하면서 진행합니다. 만약, 테스트가 성공하면, Mock 객체에 대해 명세를 시작하고 이는 다음 테스트의 시작점이 됩니다.

 

2. Inside Out 

전통적인 방식의 개발방법론으로, 한번에 하나에만 집중할 수있도록 해주는 개발 방식입니다. 아래 그림에서는 도메인부터 테스트 코드를 작성하면서, 점차 바깥으로 향하는 테스트 코드를 작성하는 것을 의미합니다.

 

 

그림 1 Outside In

Inside-Out VS Outside-In

Inside-OutOutside-In

  Inside-Out Outside-In
정의 & 방식 개발자가 한번에 하나에만 집중할 수 있도록 해줌 일부 엔터티들이 초기엔 하드코딩될 수도 있지만 처음부터 시스템을 정의할 수 있는 경로를 제공
한번에 하나의 엔터티에 집중하는 것은 팀원들이 병렬로 개발할 수 있도록 해줌 상위 레벨의 인수테스트(High Level Acceptance Test)를 사용하므로 엔터티들의 내부 구현보다는 엔터티들의 상호작용에 집중하게 됨
테스트 더블이 필요치 않음. 엔터티들이 사전에 식별되므로 실제 구현체를 사용할 수 있음 상호작용이 필요한 엔터티들이 발견될 때 마다 테스트 더블로 치환되어 해당 엔터티들에 대한 상세가 미뤄질 수 있음
  개발자들은 새로운 단위 테스트를 통해서 테스트 더블에 대한 구현물을 제공할 때까지 루프를 수행
이슈 개별 엔터티들은 통합되어 함께 동작할 때까지는 가치가 없을 수 있음 설계의 상세 구현이 테스트에 존재. 설계 변경은 대개 테스트의 변경을 수반함. 이런 상황이 위험을 추가하거나 구현에 자신감을 줄 수 있음
개발 후반부에 연동을 하는 것은 리스크가 클 수 있음 개발자가 사전에 테스트 더블을 활용하여 어떻게 상호작용을 테스트할지 알아야 함
초기 초기에는 시스템 설계에 대한 완전한 이해는 필요치 않음. 시작할 때는 한개의 엔터티만 식별되면 됨 초기부터 시스템 전반의 완전한 흐름에 집중함으로써 시스템의 서로 다른 부분들이 어떻게 상호작용하는지에 대한 지식이 요구됨
적합성 기능의 수행 후 상태 값 검증을 통한 정합성 검증에 유리한 알고리즘 구현에 적합 협력 객체들의 상호작용 검증(인자, 호출 여부 등)을 통한 정합성 검증이 유리한 비즈니스 어플리케이션에 적합(탑다운으로 점진적으로 협력 객체의 인터페이스를 찾아나가는 방법: Mock Roles, not Objects - Iterative Interface Discovery)
개발자들이 프로그래밍 언어 초보인 경우 좋은 시작점임. 개발자는 한번에 하나의 엔터티에만 집중하면 됨. 개발을 진행해 나가면서 언어, 테스트 프레임워크 등에 대한 지식이 축적됨 개발자들은 전체 시스템을 만들면서 시작하고 리팩토링 기회가 발생할 때 작은 컴포넌트들로 분해한다. 과정은 보다 탐색적일 수 있고, 목표에 대한 일반적인 아이디어가 있지만 구현의 세부사항이 명확치 않을 때 이상적임
기 구축된 시스템에 추가 기능을 구현하는 경우, 꽤 상세한 설계가 있는 경우 Inside-Out 신규 기능 셋 추가, 무엇부터 시작해야 할지 모르는 경우 Outside-In

그럼 언제 Outside-In을 활용해야 하고, 언제 Inside-Out를 활용해야 하는가?

사실은 상향식, 하향식 둘 다 TDD의 프로세스를 효과적으로 설명해 줄 수 없다. ... 만약 어떤 방향성을 가질 필요가 있다면 '아는 것에서 모르는 것으로(known-to-unknown)' 방향이 유용할 것이다. 우리가 어느 정도의 지식과 경험을 가지고 시작한다는 점, 개발하는 중에 새로운 것을 배우게 될 것임을 예상한다는 점 등을 암시한다.
- Test-Driven Development, kent beck

두 방식을 둘다 Step1 미션을 해결하며 활용해보니...

 확실히 Inside-out 을 할 때 테스트 코드를 작성하는 과정에서 막히는 부분이 없었습니다. 이미 어떤 요구사항이 있고, 이 요구사항을 해결하기 위해서는 '이렇게 테스트 코드를 작성하면 되겠다. ' 라는 것이 명확해, 거침없는 테스트 코드를 작성할 수 있었습니다. 그렇게 어느정도 마무리를 하고 나서 이번에는 Outside In 접근법으로 개발을 시도해봤습니다.

 

인수테스트 코드를 작성하는 과정까지는 문제가 없었습니다만, Mock 객체를 사용하는 과정에서 막힘을 느낄 수 있었습니다. 어떤 객체를 사용해야 되지? 어떤 협력을 유도해야 되지? 라는 생각으로 이렇게 작성했다가 다시 지우고 이를 반복했습니다.

 

 그러나, 곧바로 어떻게 설계할 지를 고민이 끝났을 때는 InsideOut 와 같이 빠르게 개발을 이어갈 수 있었습니다. OutSide In 사고방식이 좀 더 협력객체에 초점을 맞쳐서 생각해보니, 조금 더 나은 코드를 작성할 수 있었던 것이 아니였을까 싶습니다.

 

Next-step에서 말하는 추천하는 흐름은...

- Top-Down으로 방향을 잡고, Bottom-Up으로 구현하기
- 인수 테스트 작성을 통해 요구사항과 기능 전반에 대한 이해를 선행
- 내부 구현에 대한 설계 흐름을 구상
- 설계가 끝나면 도메인부터 차근차근 TDD로 기능 구현
- 만약 도메인이 복잡하거나 설계가 어려울 경우 이해하고 있는 부분부터 기능 구현
- 인수 테스트의 요청을 처리하는 부분부터 진행할 수 있음

 

단위 테스트란?

단위 테스트는 특정 단위(테스트 대상)가 의도한대로 작동하는지 검증하는 작업입니다.

 

여기서 단위란 말의 의미는?

  • 단위에 대한 정의는 하는 사람마다 다 다를 수 있음
  • 소프트웨어 시스템의 작은 부분에 초점을 맞춘 저수준이라는 개념
  • 다른 종류의 테스트보다 훨씬 빠르고 작음
  • 단일 메서드에서 전체 클래스에 이르기 까지 다양함

 

단위 테스트를 조금 깊이 고찰하기 위해 인수 테스트와 비교해서 생각해보면 아래와 같습니다.

인수 테스트 vs 단위 테스트

인수 테스트 단위 테스트
인수테스트 작성으로 기능 구현을 시작 객체나 소수의 객체 집합을 격리해서 다룸
시스템이 전체적으로 잘 동작하는지 알려줌 클래스 설계를 돕고, 동작한다는 확신을 갖게 하는 점에서 중요
진척도 측정을 위한 테스트 회귀 테스트
어디서 시작하고 언제 멈출지 빠르게 동작하도록 하고 설계(Refactoring)

 

 

[미션 PR]

https://github.com/next-step/atdd-subway-service/pull/156

 

[Step1](retry) 인수 테스트 기반 리팩터링 by LenKIM · Pull Request #156 · next-step/atdd-subway-service

안녕하세요. 이슬님, 메세지 보내드렸는데, 다시 Pull Request 요청보내드립니다. 이번에는 도메인 테스트 부터 만들면서 Step1 단계에서 코드를 다시 리팩토링 해보았습니다. 잘 부탁드립니다!

github.com

https://github.com/next-step/atdd-subway-service/pull/171

 

[Step2] 경로 조회 기능 by LenKIM · Pull Request #171 · next-step/atdd-subway-service

안녕하세요. 이슬님. 2단계 미션에 대한 코드리뷰 부탁드립니다. 오늘 하루도 화이팅입니다! 🏋️‍♀️

github.com

https://github.com/next-step/atdd-subway-service/pull/196

 

[Step3] 인증을 통한 기능 구현 by LenKIM · Pull Request #196 · next-step/atdd-subway-service

안녕하세요. 이슬님🙇‍♂️ 3단계에서는 구현해야될 부분이 많아 보아야할 코드도 많을 듯합니다. 코드리뷰 잘 부탁드립니다. 감사합니다.

github.com

https://github.com/next-step/atdd-subway-service/pull/237

 

[Step4] 요금 조회 by LenKIM · Pull Request #237 · next-step/atdd-subway-service

안녕하세요! 이슬님, 벌써 마지막 단계이네요 🙇‍♂️ 그리고.. 6월의 마지막날도 왔습니다! 이번 단계에 구현해야될 내용들이 많아 시간이 걸렸습니다. 또한 PathService에서 최단경로를 조회할

github.com

[참고자료]

https://github.com/msbaek/memo/blob/master/insideout-outsidein-tdd.md

 

msbaek/memo

다양한 MD 메모. Contribute to msbaek/memo development by creating an account on GitHub.

github.com

https://www.youtube.com/watch?v=Cz_a2gQp63c

 

댓글