본문 바로가기

책/테스트 주도 개발 시작하기(완)

3장 테스트 코드 작성 순서

테스트 코드 작성 순서

  • 2장에서 테스트 코드를 작성한 순서는 다음과 같다.
    • 쉬운 경우에서 어려운 경우로 진행
    • 예외적인 경우에서 정상인 경우로 진행
  • 반대로 어려운 경우를 먼저 시작하거나 정상 상황을 먼저 시작하면 구현 과정이 원활하게 진행되지 않기도 한다.

초반에 복잡한 테스트부터 시작하면 안 되는 이유

  • 초반부터 다양한 조합을 검사하는 복잡한 상황을 테스트로 추가하면 해당 테스트를 통과시키기 위해 한 번에 구현해야 할 코드가 많아진다.
  • 이 테스트를 통과시키려면 어떻게 해야 할까? 가장 빨리 통과시킬 수 있는 방법은 입력 값이 "abcDef12"이면 STRONG을 리턴하는 코드를 추가하는 것이다.
  • 한번에 많은 코드를 만들다 보면 나도 모르게 버그를 만들고 나중에 버그를 잡기 위해 많은 시간을 허비하게 된다.

구현하기 쉬운 테스트부터 시작하기

  • 구현하기 쉬운 것이 선택 기준이 된다.
  • 보통 수 분내에 구현을 완료하고 통과시킬 수 있는 테스트를 선택해야 한다.

예외 상황을 먼저 테스트해야 하는 이유

  • 다양한 예외 상황은 복잡한 if-else 블록을 동반할 떄가 많다. 예외 상황을 전혀 고려하지 않은 코드에 예외 상황을 반영하려면 코드의 구조를 뒤집거나 코드 중간에 예외 상황을 처리하기 위해 조건문을 중복해서 추가하는 일이 벌어진다.
  • 초반예 예외 상황을 테스트하면 이런 가능성이 줄어든다.

완급 조절

  • 처음 TDD로 구현할 때 어려운 것 중 하나는 한 번에 얼마만크의 코드를 작성할 것인가이다.
  • TDD 연습 단계
    1. 정해진 값을 리턴
    2. 값 비교를 이용해서 정해진 값을 리턴
    3. 다양한 테스트를 추가하면서 구현을 일반화
  • 이런 연습 과정은 아중에 만들어야 할 코드가 잘 떠오르지 않을 때 점진적으로 구현을 진행할 수 있는 밑거름이 된다.

지속적인 리팩토링

  • 매번 리팩토링을 진행해야 하는 것은 아니지만 적당한 후보가 보이면 리팩토링을 진행한다.
  • 지속적으로 리팩토링을 진행하면 코드 가독성이 높아진다.
  • 코드 변경의 어려움을 줄여주어 향후 유지보수에 도움이 된다.

테스트 대상 코드의 리팩토링 시점

  • 테스트 대상 코드에서 상수를 변수로 바꾸거나 변수 이름을 변경하는 것과 같은 작은 리팩토링은 발견하면 바로 실행한다.
  • 메소드 추출과 같이 메서드의 구조에 영향을 주는 리패토링은 큰 틀에서 구현 흐름이 눈에 들어오기 시작한 뒤에 진행한다.
  • 구현 초기에는 아직 구현의 전반적인 흐름을 모르기 때문에 메서드 추출과 같은 리팩토링을 진행하면 코드 구조를 잘못 잡을 가능성이 있다.

테스트 작성 순서 연습

  • 먼저 테스트 클래스 이름을 정하자.

쉬운 것부터 테스트

  • 테스트를 추가할 때에는 다음 두 가지를 고려애햐 한다
    • 구현하기 쉬운 것부터 먼저 테스트
    • 예외 상황을 먼저 테스트
  • 내용을 예측할 수 있는 짧은 이름을 사용하는 것도 방법이다.

예를 추가하면서 구현을 일반화

  • 1만 원을 납부하는 예를 하나 더 추가한다.
  • 이제 구현을 고민할 차례다. 이 테스트를 통과시키기 위해 한 번 더 상수를 사용할까? 아니면 바로 구현을 일반화할까? 이 예는 비교적 단순하므로 바로 구현을 일반화해도 된다.

코드 정리: 중복 제거

  • 리팩토링할 시간이다.
  • 발생하지도 않았는데 미리 단정 지어 코드를 수정할 필요는 없다.
  • 테스트 코드의 중복 제거는 고민이 필요하다.
  • 테스트 코드의 구현 중복을 기계적으로 제거하면 자칫 테스트 메서드가 검증하고 싶은 내용을 알아보기 힘들 수 있다.
  • 일단 중복 제거를 해보고 테스트 코드가 여전히 자신을 설명하고 있는지 확인해보자

예외 상황 처리

  • 쉬운 구현을 하나 했으니 이제 예외 상황을 찾아보자

다음 테스트 선책: 다시 예외 상황

  • 그다음으로 쉽거나 예외적인 것을 선택하면 된다.
  • 이전 테스트가 1개월 요금 지불을 기준으로 하므로 1개월 요금 지불에 대한 예외 상황을 마무리하고 2개월 이상 요금 지불을 테스트하는 것이 좋을 것 같다.

다음 테스트를 추가하기 전에 리팩토링

  • 파라미터 개수는 적을수록 코드 가독성과 유지보수에 유리하므로 메서드의 파라미터 개수가 세 개 이상이면 객체로 바꿔 한 개로 줄이는 것을 고려해야 한다.

예외 상황 테스트 진행 계속

  • 상수를 이용해서 테스트를 통과시켰으니 새로운 테스트 사례를 추가해서 구현을 일반화할 차례다.
  • 테스트가 실패했으니 통과시켜보자, 테스트를 통과할 만큼만 구현을 일반화해보자

코드 정리: 상수를 변수로

  • 상수 1을 [리스트 3.17]과 같이 변수로 바꾸자
  • 코드를 리팩토링한 뒤에는 테스트를 다시 실행해서 깨지는 테스트가 없는지 확인한다.

다음 테스트 선택: 쉬운 테스트

  • 새로 추가한 테스트도 통과한다. 이제 몇 가지 사례를 더 추가해 나가면서 구현에 문제가 없다는 것을 확인할 차례다.

예외 상황 테스트 추가

코드 정리

  • 코드가 복잡한 이유 중 하나는 날짜 관련 계싼 코드가 중복해서 존재하기 때문이다.
  • 코드를 수정했으면 테스트를 실행해서 깨지는 테스트가 없는지 확인한다.
  • 역시 코드를 수정한 뒤에는 테스트를 실행해서 문제가 없는지 확인하다.

다음 테스트: 10개월 요금을 납부하면 1년 제공

  • 지금까지 했던 것처럼 먼저 할 일은 테스트 코드를 추가하는 것이다.
  • 이 테스트를 통과시켜보자, 쉬운 방법은 지불한 금액이 10만 원인지 여부를 비교하는 것이다.

테스트할 목록 정리하기

  • TDD를 시작할 때 테스트할 목록을 미리 정리하면 좋다.
  • 어떤 테스트가 구현이 쉬울지 상상해본다. 또는 어떤 ㅔㅌ스트가 예외적인지 상상해본다.
  • 시간을 조금 들여서 구현의 난이도나 구조를 검토하면 다음 테스트를 선택할 때 도움이 된다.
  • 테스트 과정에서 새로운 테스트 사례를 발견하면 그 사례를 목록에 추가해서 놓치지 않도록 해야 한다.
  • 처음부터 모든 사례를 정리하려면 시간도 오래 걸릴뿐더러 쉽지도 않다.
  • 테스트 목록을 적었다고 해서 테스트를 한 번에 다 작성하면 안 된다.
  • 다루는 범위가 작고 개발 주기도 짧으므로 개발 집중력도 높아진다.
  • 범위가 큰 리팩토링은 시간이 오래 걸리므로 TDD 흐름을 깨기 쉽다. 이때는 리팩토링을 진행하지 말고 테스트를 통과시키는 데 집중한다.
  • 리팩토링 범위가 크면 리팩토링에 실패할 수도 있다. 그러니 범위가 큰 리팩토링을 진행하기 전에는 코드를 커밋하는 것을 잊지 말자.

시작이 안 될 때는 단언부터 고민

  • 테스트 코드를 작성하다 보면 시작이 잘 안 될 때가 있다. 이럴 땐 검증하는 코드부터 작성하기 시작하면 도움이 된다.

구현이 막히면

  • 쉬운 테스트, 예외적인 테스트
  • 완급조절

' > 테스트 주도 개발 시작하기(완)' 카테고리의 다른 글

8장 테스트 가능한 설계  (3) 2024.07.23
7장 대역  (1) 2024.07.17
6장 테스트 코드의 구성  (0) 2024.07.17
4장 TDD, 기능 명세, 설계  (0) 2024.07.09
2장 TDD 시작  (0) 2024.07.03