TIL 2021/4/7 : TDD란 무엇인가

Ji_min·2021년 4월 6일
0

Today I Learnt

목록 보기
2/3

도서관 서비스 프로젝트를 하면서 유닛 테스트라는 걸 알게 되었는데, 뭐지 하고 찾아보다가 TDD, 테스트 주도 개발이라는 개발 방법론이라는 개념을 접하게 되었다. 요즘 굉장히 핫한 개념이라는데 아무리 관련 글을 찾아봐도 도대체 이게 뭐라는 소리인지 이해가 되지 않았다.

그러던 차에 굉장히 흥미로운 영상을 보게 되었다. 2018년 OKKYCON 녹화 영상이었는데, 테알못 신입은 어떻게 테스트를 시작했을까?라는 제목의 TDD 관련 발표였다. 그런데 연사로 나온 분이 테스트로 개발하기 시작한 기간은 5개월에 불과하고 TDD로 개발한 시간은 그보다 짧다고 해서 놀랐다. 나는 TDD로 개발 5개월 하고 저런 곳에 나가서 마이크를 잡고 내 경험에 대해서 말하라고 하면 못할 것 같은데... 연사분이 대단하면서도 또 한편으로는 TDD가 도대체 무엇인지 더 알고 싶어졌다. 어 그럼 나도 한 번 해볼까? 하는 생각이 들었다.

우선은 영상을 보고 몇 가지 내용을 정리해보았다.


TDD vs Test Last

TDD는 테스트 코드부터 만들고 실제 사용할 코드를 만드는 것이고, Test Last는 기존의 코드를 가지고 테스트 코드를 만들어서 테스트 해보는 것이다.

  • TDD : 구현되지 않은 코드에서 시작 (red → green → refactor)
  • Test Last : 구현된 코드에서 시작하기 때문에 보통은 green에서 시작하는데, 코드에 버그가 있을 경우 red에서 시작하기도 함(red or green → refactor → red or green)

기존 코드에 테스트 적용하기

  • 테스트하기 쉬운 함수를 찾아서 적용하기
    • 순수함수
    • 외부의존성이 적은 함수
    • 인풋과 아웃풋이 명확한 함수
    • utility, helper 함수
  • 주의할 점 : 프로덕션 코드는 건드리지 않는다.
  • 리팩토링 시 테스트 코드는 수정하지 않는다.
  • 테스트하기 어려운 코드에서 테스트하기 쉬운 코드만 분리한다.
    • 중요한 비즈니스 로직을 가지고 있는 부분
    • 현재 버그가 발견된 부분
    • 결합은 낮고 로직은 복잡한 부분
  • 전체 흐름 : 테스트하기 어려운 코드에서 테스트하기 쉬운 코드를 분리해서 테스트를 추가한 다음 리팩토링하는 걸 반복

새로운 코드는 TDD로 개발하기

  • 실패하는 코드를 짜서 성공하게 만든다.
  • 테스트의 좋은 점
    • 오류를 잡아주기 때문에 오류가 있는 채로 배포하는 것을 막아줌
    • 리팩토링을 안정적으로 할 수 있다
    • 스펙 문서의 기능을 수행 → 본인이 만든 함수의 세부사항을 모두 기록해둘 수 있다. (패러미터가 뭐고 반환값이 뭔지 스펙만 보고 이해 가능)
    • 디자인 개선 효과
    • 학습 동기 부여 → 내가 얼마나 못하는지 깨닫게 해주기 때문
  • TDD 원칙 : 테스트가 성공할 만큼의 최소한의 코드만 만든다.

처음에는 무슨 소리인지 하나도 이해가 안되었다. red? green? 실패하는 코드를 짜서 성공하게 만든다? 테스트가 성공할 만큼의 최소한의 코드만 만든다?

색깔은 둘째치고 실패하는 코드를 짜서 성공하게 만들라는 게 무슨 소리인지 도무지 이해가 되지 않았다. 테스트 하는 이유가 성공시키려고 하는 건데 애초에 실패하는 코드를 짠다? 왜 그런 비효율적인 일을 굳이 하는 걸까 하는 생각이 들었다.

그래서 TDD에 대한 설명을 더 찾아보다가, 정말 좋은 영상을 발견했다. TDD in Python with pytest라는 제목의 영상이었는데 파이썬에서 pytest로 TDD하는 방법을 정말 쉽게 가르쳐준다. 이 영상을 보고 앞서 이해가 되지 않았던 부분들이 전부 이해가 되었다. 영상을 보고 처음으로 테스트 코드를 작성해보면서 내가 이해한 TDD란 무엇인지를 한 번 적어보고자 한다.

1. TDD는 無에서 시작한다.

저게 뭔가 싶었던 red와 green은 알고보니 단순했다. 테스트 실패와 성공을 의미한다. 테스트가 실패하면 빨간색, 성공하면 초록색으로 메세지가 뜨기 때문이다. TDD는 red에서 시작한다는 말은 곧 실패에서 시작한다는 뜻이다.

어떻게 테스트가 실패에서 시작할 수 있을까? 방법은 두 가지가 있다. 첫 번째는 오류가 있는 코드를 짜는 것이고, 두 번째는 코드를 짜지 않는 것이다. 나는 전자로 알아들었기 때문에 이해가 되지 않았었다. 그런데 그게 아니라 후자를 이야기하는 거였다. 테스트를 실패하려면 코드를 짜지 않으면 된다. 그러면 반드시 실패하기 때문이다. 좀 더 쉽게 말해보겠다. 테스트를 하려면 테스트 하려는 모듈을 가져와야 한다. 그런데 그 모듈이 없는 모듈이라면? 테스트는 실패한다. 그러면 오류 메세지가 모듈이 없다고 말한다. 그럼 그제서야 가서 모듈을 만들고 다시 돌아와서 테스트 하는 것이다. TDD는 여기서 시작한다. 실패하는 코드를 만들고, 실패하면 오류 메세지를 보고 지시에 따라 성공하는 코드로 바꿔가는 것이다.

그러면 아무것도 없는 거를 가지고 왜 테스트를 하는건가? 하는 의문이 들 수도 있는데, 그게 바로 TDD의 본질이라고 생각한다. 아무것도 없는 것에서 테스트 코드부터 짜서 점점 실제 코드를 완성해가는 것이다. 그래서 테스트 주도 개발이다.

2. TDD는 적당히 하는 것이다.

앞서 TDD의 원칙은 테스트가 성공할 정도로만 코드를 작성하는 것이라고 했다. 이게 무슨 말일까? 예를 들어 보겠다.

두 수를 더한 결과를 반환하는 addition이라는 함수가 있고, 아래와 같이 테스트 코드를 만들었다.

from cal import addition

def test_addition():
    assert addition(1, 4) == 5

그럼 다음의 return에 무엇이 들어가야 할까?

def addition(a, b):
    return ?

a+b라고 생각하겠지만 틀렸다. 정답은 5이다.
아니 무슨 개소리야? 하고 생각할 수도 있다. 나도 처음 보고는 그냥 a+b를 하면 되지 굳이 한 번 더 실패하고 다시 코드를 수정하는 수고를 거치는 게 뭐하는 짓인가 싶었다. 그런데 TDD 방법론에서는 오히려 a+b가 과하다고 한다. 5를 리턴해서 테스트가 통과되었으면 거기서 족하다. TDD에서의 최우선 고려사항은 코드가 예쁘고 재사용 가능한 게 아니다. 일단 테스트를 통과시키는 게 목적이고 그 목적이 달성되었다면 어떤 이유에서든 오케이인 것이다. 리팩토링은 그 다음이다. 그럼 5를 리턴하는 엉망인 코드는 언제 고치는가? 여러가지 케이스들을 가지고 더 테스트할 때 고치면 된다. 우선 지금은 일단 테스트가 통과되었고 문제될 게 없으니 키보드에서 손을 떼도 된다는 게 TDD에서 말하는 적당히이다.

3. TDD는 실패하는 것이다.

TDD를 왜 할까? 설명만 들어서는 너무 비효율적이고 시간만 잡아먹는 행위같은데, 왜 굳이 실패하는 코드를 짜서 거기서부터 시작하는 걸까? 물론 여러 이점이 있으니까 사람들이 전부 TDD 이야기를 하는 것일 거다. 예를 들어, TDD는 뭘 해야될지 다 알려주기 때문에 코드를 짜는 시간이 절약된다. 메세지의 지시만 따라가면 되니까 처음부터 다 짜놓고 실패하면 다시 뭐가 틀렸는지 찾아가는 것보다 시간적으로 더 유리하다고 할 수도 있다.

그런데 내가 생각하는 TDD를 하는 이유는 실패에 익숙해지기 위해서이다. 개발자들은 코드를 짜면서 수많은 실패와 오류 메세지들을 마주하기 때문에, 이미 그런 것들에는 익숙해질만큼 익숙해졌다고 생각할 수도 있다. 그러나 생각해보면, 코드를 실행하는 그 순간은 언제나 긴장되고 에러가 뜰까봐 조마조마해 한다.

실패가 두려운 이유는 성공하기를 바라기 때문이다. 그러나 TDD에서는 실패가 당연하다. 실패하지 않으면 오히려 잘못된 것이다. 어차피 개발은 실패의 연속이다. 평상시에는 당연히 실패하고, 성공할 때만 성공하면 된다.


그 동안 찾아보고 따라해본 내용을 가지고 주절대기는 했지만 여전히 개발할 때 적용해보기는 어려운 것 같다. 계속 연습해보면서 익혀가야겠다. 테스트 코드 짜는 거 재미있다!

참고

profile
Curious Libertine

0개의 댓글