TDD 테스트 주도 개발 : 천천히 시작하기

hyomin·2020년 3월 3일
9
post-thumbnail
post-custom-banner

시작하며

이 글은 저의 공부 내용을 바탕으로 하기에,
TDD를 시작하고자 하는 분께 작은 이론적 가이드가 될 수는 있지만,
실질적인 테스트코드의 예시 등은 다루고 있지 않습니다.
더 실력있는 분들의 좋은 글들을 참고해 주시기 바랍니다.

🚀 개요

얼마전 회사에서 TDD워크숍이 있었습니다.

CTO님이 사내 개발 문화로써 TDD를 도입할 수 있는 팀은 점진적으로 도입하면 좋겠다는 취지였던 것 같습니다.

결과 아주 유익했고, 저의 코딩스타일에 좋은 영향을 끼쳤습니다.

이 글은 TDD 워크숍을 진행하는 동안 익혔던 내용들에 대한 저의 메모를 바탕으로 정리한 글입니다.

🚀 이론

"동작하는 깨끗한 코드"는 TDD의 골이자 가치관이다. - Kent Beck

"Clean code that works!"

TDD란 본질적으로는 코드 작성법입니다.
그러나 이를 한 번에 적을 수는 없습니다.

한 번에 적을 수 없다면, 두 번, 세 번에 해치운다.

  1. 움직이는 코드
  2. 깨끗한 코드

1번을 먼저 해결합니다. 2번은 시간을 필요로 합니다.
1번과 2번을 섬세하게 작게 쪼개어 반복합니다.

어떻게 하는가

🚀 구현할 기능 개발에 필요한 TODO 리스트를 작성한다.

🚀 아래 과정을 진행한다.

  1. 다음 목표를 생각한다. (과도한 시간을 투자하지 않는다.)
  2. 그 목표를 달성(실패)시키는 테스트 코드를 작성한다.
  3. 그 테스트를 실행하여 실패시킨다. (RED)
  4. 목적을 달성시키는 코드를 적는다. (깨끗함은 포기한다.)
  5. 2에서 적은 테스트를 성공시킨다. (GREEN)
  6. 테스트를 통과하는 상태에서 리팩토링한다.
  7. 다시 1번으로 가서, 1~6번을 반복한다.

🚀 TODO리스트에서 먼저 테스트할 우선순위는?

  1. 구현이 쉬운 것
  2. 중요해보이는 것

TDD를 "방해하는" 마음가짐

타락 : " 움직이니까 됐어. 다음에 하자 " 라며 그만두는 것
조급함 : " 스케줄에 쫒기니까 다음에 하자 " 라며 포기하는 것
두려움 : " 움직이는데 건드리니까 움직이지 않게 됐어 " 라는 공포

개발환경은 변하는데 아무것도 하지 않으면 움직이지 않게 됩니다.

🚀움직이지 않게 된 것을 빠르게 대처하면 됩니다. 어떻게? UnitTest로 !

탈출조건을 세웁니다.

  1. 시간
    5분, 10분만 작성한다는 룰을 정하는 등으로 대처합니다.
  2. 갯수
    TODO리스트에서 작성할 논리적인 목표를 줄여나갑니다.

🚀 작성

테스트 함수명은 가능하다면 한글로 작성합니다.

왜냐하면 테스트코드는 곧 명세서입니다.

  • 테스트를 실행시켰을 때, 제품이 어떻게 동작하는지를 한 눈에 파악할 수 있는 것이 좋습니다.
  • 새로운 사람이 팀에 합류했을 때, 테스트를 실행시키는 것만으로 대략적인 동작을 파악할 수 있다면 최고입니다.

예) public function test주소를행정구역으로나누기() {}

하지만, 회사에 외국인 개발자가 많을 경우에는 적합하지 않을 수 있기 때문에
영어로 작성해도 좋습니다!

요소

하나의 테스트 메소드는 아래와 같이 구성되어 있습니다.

// 준비
// 실행
// 검증
// 뒤처리(X)

이를 포페이스 또는 쓰리페이스라고 합니다.
또는 given, when, then
또는 Arrange, Act, Assert 의 3A라고도 합니다.

보통 위에서부터 작성하지만, 테스트 코드는 밑에서부터 작성합니다.

// 예 
// arrange
params = []; // (2)
expected = 1;
// act
actual = testMethod(params);  // (3)
// assert
assertEquals(expected, actual); // (1)

왜? Goal 부터 적습니다. 무엇을 하고 싶은지를 명확하게!

움직일거라 생각했지만, 동작하지 않고 멈출 때 개발자는 마음이 아픕니다.

따라서 "예상대로" 실패시켜보는 것이 중요합니다. (실패를 예상했기에 마음에 상처받지 않습니다!)

🚀 그 외 질문 및 답변

Q. 테스트코드는 어떻게 테스트하나요?

테스트코드의 테스트는 프로덕션에서 하자.

의도적으로 실수를 넣어서 실패를 의도한다.
의도적으로 프로덕션에 실패를 넣어서 테스트코드를 확인합니다.

Q. 2번 이상 실행해야 하는 테스트의 경우는 어떻게 하나요?

  1. assert 를 늘리기
  2. method 를 늘리기

assert 를 늘리면 ...

  • 함수명과 그 의도가 달라집니다.
  • assert 실패하면 다음 assert 는 실행되지 않고 그대로 종료됩니다.
    따라서 다음 assert 의 상황을 알 수 없게 됩니다.

하나의 메소드에 assertion 이 하나면, debug 에 강해지고, doc 에 강해집니다!

Q. 테스트 코드에 로그를 넣어도 되나요?

로그는 별로 좋지 않습니다.
실행되는 테스트 함수명으로 그 목적을 명확히 알 수 있어야 합니다.

Q. 테스트 메소드간의 의존관계가 있는 경우 어떻게 하나요?

Unit테스트는 실행순서를 보장하지 않습니다. 따러서 Unit 테스트에서 제공하는 직렬 실행 옵션을 메소드 단위로 붙여주면서 통제할 수는 있지만, 테스트가 방대해집니다.

테스트 간의 의존관계는 없애는 것이 제일 좋습니다.

🚀 TDD 스킬

- 문제를 명확히 분할하기 (어렵다)

- 보폭 정하기

아래의 3가지 보폭이 있으며, 이 중 한 가지를 선택합니다. (이 글 참고)

  • 테스트 -> 가짜구현 -> 삼각측량 -> 명백한 구현
  • 테스트 -> 가짜구현 -> 명백한 구현
  • 테스트 -> 명백한 구현

🚀 테스트코드의 유지보수

테스트코드의 유지보수가 대량으로 발생하지 않도록 하는 것이 중요합니다.

"구조화" 와 "리팩토링"

테스트코드를 구조화하기 위한 방법

  1. 메소드명을 길게 만든다. (과거)
    ex) test_ClassA_methodA, test_ClassA_methodB,...
  2. Class를 만든다.
    ex) ⓐNested 를 사용하거나, class 안에 class 를 넣고
    ⓐdisplayName("") 를 사용

테스트코드를 리팩토링 하는 방법

1) 유지보수 비용을 최소화하기 위해 필요없는 것은 지워버려야 합니다.

불안에 근거하여 테스트코드를 남기는 것은 지양해야 하며
본인만 알고 있는 코드는 지웁니다.

그렇게 하지 않을 경우, 테스트 코드는 방대해지며,
새로 팀에 합류한 사람이 전혀 이해할 수 없는 구조가 됩니다.

2) tree만 보고도 알 수 있게 작성해야합니다.

즉, 테스트코드란

(1) 유지보수를 최소화하며
(2) 품질을 보증할 수 있는 상태(테스트 케이스를 늘리기)

두 번째 테스트 케이스는 "근거가 있어야" 합니다.

"불안하니까 다른 케이스를 해보자?"
무작정 케이스를 늘리는 것은 지양해야 합니다.

케이스를 추가해야 하는 근거가 분명할 때, 케이스를 추가합니다.

🚀 마치며

이번 워크숍을 통해 테스트주도개발에 대해 이론적 배경을 공부할 수 있었고,
2명씩 짝을 지어 페어프로그래밍 도 실시해서 몸소 익힐 수 있었습니다.
다른 팀의 발표도 들으면서 문제를 어떻게 해결해 갔는지도 공유하고 배울 수 있어서 좋았습니다.

사실 워크숍을 한 지는 4개월 정도 지났고,
그 동안 저희 팀은 제품에 새로운 기능을 추가할 때는 점진적으로 테스트코드를 작성해왔습니다.

기존 코드에 대해서는 테스트 코드를 작성할 수 있는 부분만 작성하고
의존관계가 높은 함수들에 대해서는 뒤로 미루는 등의 선택적으로 도입해왔습니다.

테스트코드를 작성하면서 제가 느낀점은 결론부터 말씀드리면 "아주 행복하다!" 라는 것입니다.

- 마음 편한 리팩토링!

새로운 기능을 추가할 때, 테스트코드가 통과하기만 하면 되므로, 리팩토링을 정말 마음 편히 할 수 있었습니다!

- 전체 개발 기간 단축!

이전에는 테스트를 할 때 직접 값을 입력하거나, 값을 직접 넘겨주는 방식으로 테스트를 했었는데, 지금은 테스트 코드를 실행하기만 하면 되니 전체적인 개발 시간이 단축됨을 확실하게 느낍니다!

- 설계의 중요성을 인식!

기존 코드에 대한 테스트코드를 작성하기 어려운 이유가
의존관계가 깊었다는 것이었는데, 새로운 개발을 착수할 때 이 부분을 고려하면서 개발하는 등 의존관계를 의식하게 되었습니다.

TDD 도입을 고민하시는 분들이 계시다면, 새로 개발하는 기능부터라도 조금씩 도입한다면 회사의 경험도 쌓이면서 코드도 점점 나아지지 않을까 생각합니다!

참고

post-custom-banner

4개의 댓글

comment-user-thumbnail
2020년 3월 24일

글 잘 읽었습니다!!

1개의 답글
comment-user-thumbnail
2020년 3월 24일

좋은 글 감사합니다.

1개의 답글