본문 바로가기

개발서적

테스트 주도 개발 - 켄트 백

 채용 공고에 TDD가 많이 있어서, 도대체 TDD는 무엇인지 항상 궁금하였다. 궁금증을 해소하기 위해 이 책을 읽게 되었다. 내가 느낀 TDD의 핵심은 "한 번에 한 가지 일에만 집중한다"였다.

 

예제 소스코드 : https://github.com/JMsuper/java-TDD-pratice

 

GitHub - JMsuper/java-TDD-pratice: TDD-kend beck practice repository

TDD-kend beck practice repository. Contribute to JMsuper/java-TDD-pratice development by creating an account on GitHub.

github.com

 

 


 

 

 

 TDD의 궁극적인 목적은 작동하는 깔끔한 코드(clean code that works)를 만드는 것이다.

→ 책을 읽으면서 내가 느낀 TDD의 목적은, 한 번에 한 가지 일에만 집중한다 였다.

 

 TDD는 두가지 규칙만을 따른다.

첫째, 오직 자동화된 테스트가 실패할 경우에만 새로운 코드를 작성한다.

둘째, 중복을 제거한다.

 

 두 가지 규칙에 의해 TDD 프로그래밍 순서는 다음 절차를 따른다.

  1. 빨강 - 실패하는 작은 테스트를 작성한다. 처음에는 컴파일조차 되지 않을 수 있다.
  2. 초록 - 빨리 테스트가 통과하게끔 만든다. 이를 위해 어떤 죄악(ex. 코드 복붙, 상수 사용)을 저질러도 좋다.
  3. 리팩토링 - 일단 테스트를 통과하게만 하는 와중에 생겨난 모든 중복을 제거한다.

 TDD를 통해서 무엇은 얻을 수 있을까? 프로그래밍을 하는 중 겪게되는 두려움을 관리할 수 있다. 여기서 두려움이란, 너무 어려운 문제여서 시작 단계인 지금은 어떻게 마무리될 지 모르는 미래에 대한 합리적인 두려움이다. 이 두려움은 아래의 일의 원인을 제공하곤 한다.

  • 두려움은 여러분을 망설이게 만든다.
  • 두려움은 여러분이 커뮤니케이션을 덜 하게 만든다.
  • 두려움은 여러분이 피드백 받는 것을 피하도록 만든다.
  • 두려움은 여러분을 까다롭게 만든다.

 빨강, 초록, 리팩토링으로 나눈 TDD 프로그래밍의 순서를 구체화하여 5가지 단계로 다시 정리한다.

  1. 재빨리 테스트를 추가한다.
  2. 모든 테스트를 실행하고 새로 추가한 것이 실패하는지 확인한다.
  3. 코드를 조금 바꾼다.
  4. 모든 테스트를 실행하고 전부 성공하는지 확인한다.
  5. 리펙토링을 통해 중복을 제거한다.

 

 

 

1부. 화폐 예제

저자(켄트 백)는 TDD를 할 때 할일 목록을 작성한다고 한다. 앞으로 어떤일을 해야 하는지 알려주고, 지금 하는 일에 집중할 수 있도록 도와주며, 언제 일이 다 끝나는지 알려줄 수 있게끔 도와준다고 한다. 작성 방법은 아래와 같다.

  • 진행 중인 일(굵은 글씨)
  • 완료된 일(가운데 줄)
  • 테스트 생각나는 것들을 지속적으로 추가

 

 테스트를 작성할 때는 오퍼레이션(or 객체의 메소드)의 완벽한 인터페이스에 대해 상상해보는 것이 좋다. 오퍼레이션(or 인터페이승)이 외부에서 어떤 식으로 보일지에 대한 이야기를 테스트 코드에 적는다.

 

 TDD의 빨간 막대를 보기위해 스텁 구현을 활용한다. 메서드의 서명부와 (반환값이 있을 경우) 반환 명령만 적는 식으로 해서, 이 메서드를 호출하는 코드가 컴파일 될 수 있도록 껍데기만 만들어두는 것이다. 이게 어떤 의미가 있는가 싶겠지만, 단순한 실패도 일종의 진척이다. 실패에 대한 구체적인 척도를 갖게 된 것이다. 막연히 실패했다는 사실만 아는 것보다는 나아진 것이다.

 

 테스트 코드 작성 중 주의해야할 점이 있다. 테스트 자체에 결함이 존재한다면, 테스트는 코드가 정확히 작동한다는 것을 보장해주지 않는다. 따라서 코드의 결함을 잡아내지 못한다. 만약 이러한 결함을 나중에 발견한다면 이에 대해 교훈을 얻고 앞으로 나아가면 된다.

 

 TDD의 단계 중 빨강, 초록, 리팩토링이 모두 같은 속도로 이뤄지는 것은 아니다. 빨강, 초록 단계에서는 빠르게 수행하는 것이 핵심이다. 이후의 시간은 리팩토링에 활용하도록 한다. 만약 리팩토링 하면서 실수를 했는데도 불구하고 테스트가 통과한다면, 기 작성된 테스트 코드에 결함이 있음을 의미한다. 이때는 리팩토링을 멈추고, 충분한 테스트 코드를 작성한 이후 다시 진행해야 한다.

 

 할일 목록이 모두 완료된 이후에는 무엇을 해야할까? 이때는 그때까지 설계한 것을 검토하기에 적절한 시기이다. “어떤 테스트들이 추가로 더 필요할까?”라는 질문에 답해야 하며, 자주 활용되는 핵심 로직과 변경이 잦은 로직이 서로 분리되어 설계되었는지 확인해야 한다. 그래야 이후 수정할 때 안심할 수 있다.

 

 

 

2부. XUnit 예제

→ 이 챕터 정리는 토비님의 “XUnit 테스팅 프레임워크 TDD로 만들기” 라이브 코딩을 수행하는 것으로 갈무리함.

참고자료

- 유튜브 라이브 코딩 : https://www.youtube.com/watch?v=tdKFZcZSJmg

- 토비님 xUnit 관련 블로그 포스팅 : https://toby.epril.com/5

- 소스코드 : https://github.com/JMsuper/java-xUnit-impl

 

JMsuper/java-xUnit-impl

JUnit implementation with Java. Contribute to JMsuper/java-xUnit-impl development by creating an account on GitHub.

github.com

 

 

 

 

3부. 테스트 주도 개발의 패턴

 어떻게 테스트할 것인가에 대하여 자세히 이야기하기 전에 우선 기본적인 전략에 관한 질문에 답해야 한다.

 

테스트 한다는 것은 무엇을 뜻하는가?

 명사로서의 테스트의 의미는 “승인 또는 거부에 도달하는 과정”이다. 작성한 소프트웨어에 대해 자동화된 테스트를 만드는 것이다. 사람은 스트레스를 많이 받으면 테스트를 점점 더 뜸하게 하기 마련이다. 테스트를 뜸하게 하면 에러는 더 많아질 것이다. 에러가 많아지면 더 많은 스트레스를 받게 된다. 이 악순환의 고리에서 벗어나기 위해 ‘자동화된 테스트’를 활용할 수 있다. 자동화된 테스트가 있다면 스트레스를 받기 시작할 때 테스트를 실행할 것이다. 테스트는 두려움을 지루함으로 바꿔주는 효험이 있다.

 

 각각의 테스트는 다른 테스트와 완전히 독립적이어야 한다. 즉 문제가 하나면 테스트도 하나만 실패해야 하고, 문제가 둘이면 테스트도 두 개만 실패해야 한다.

 

테스트할 로직을 어떻게 고를 것인가?

 테스트를 작성할 때 단언(assert)은 언제쯤 쓸까? 단언을 제일 먼저 쓰고 시작하라.

  • 시스템을 개발할 때 무슨 일부터 하는가? 완료된 시스템이 어떨 거라고 알려주는 이야기부터 작성한다.
  • 특정 기능을 개발할 때 무슨 일부터 하는가? 기능이 완료되면 통과할 수 있는 테스트부터 작성한다.
  • 테스트를 개발할 때 무슨 일부터 하는가? 완료될 때 통과해야 할 단언부터 작성한다.

 단언을 작성할 때 우리는 다음과 같은 질문을 하게된다.

  • 테스트하고자 하는 기능이 어디에 속하는 것일까? 기존의 메서드를 수정해야 하나, 기존의 클래스에 새로운 메서드를 추가해야 하나, 아니면 이름이 같은 메서드를 새 장소에? 또는 새 클래스에?
  • 메서드 이름을 뭐라고 해야 하나?
  • 올바른 결과를 어떤 식으로 검사할 것인가?
  • 이 테스트가 제안하는 또 다른 테스트에는 뭐가 있을까?

 

테스트할 데이터를 어떻게 고를 것인가?

 테스트할 때 어떤 데이터를 사용해야 하는가? 테스트를 읽을 때 쉽고 따라가기 좋을 만한 데이터를 사용해야 한다. 테스트 작성에도 청중이 존재한다. 단지 데이터 값을 산발하기 위해 데이터 값을 산발해서는 안된다. 데이터 간에 차이가 있다면 그 속에 어떤 의미가 있어야 한다.

 

 데이터의 의도를 어떻게 표현할 것인가? 테스트 자체에 예상되는 값과 실제 값을 포함하고 이 둘 사이의 관계를 드러내기 위해 노력해야 한다. 명백한 데이터가 주는 또 다른 이점은 프로그래밍이 쉬워진다는 것이다. 단언 부분에 일단 수식을 써놓으면 다음으로 무엇을 해야 할지 쉽게 알게 된다.

 

💡 TIP : 혼자서 프로그래밍할 때는, 마지막 테스트가 꺠진 상태로 끝마치는 것이 좋다. 나중에 다시 코딩하기 위해 돌아왔을 때, 어느 작업부터 시작할 것인지 명백히 알 수 있다.