클린 코드 ‐ 단위 테스트[Clean Code 9] - thought-corner/Backend-PlayGround GitHub Wiki

TDD 법칙 3가지

  • TDD의 법칙 3가지는 다음과 같다.
    • 첫째 법칙 : 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
    • 둘째 법칙 : 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
    • 셋째 법칙 : 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
  • 하지만 실제 코드와 맞먹을 정도로 방대한 테스트 코드는 오히려 심각한 관리 문제를 유발하기도 한다. 실제로 코드를 작성하면서 나의 사수에게 들었던 피드백이기도 하다.

깨끗한 테스트 코드 유지하기

  • 테스트 코드가 지저분할수록 변경하기 어려워진다.
  • 테스트 코드가 복잡할수록 실제 코드를 짜는 시간보다 테스트 케이스를 추가하는 시간이 더 걸리기 마련이다.
  • 실제 코드를 변경해 기존 테스트 케이스가 실패하기 시작하면 지저분한 코드로 인해 실패하는 테스트 케이스를 점점 더 통과시키기 어려워진다. 그래서 테스트 코드가 계속해서 늘어나는 부담으로 돌아온다.
  • 테스트 코드는 실제 코드 못지 않게 중요하다. 테스트 코드는 사고와 설계의 주의가 필요하다. 실제 코드 못지 않게 깨끗하게 짜야한다.

테스트는 유연성, 유지보수성, 재사용성을 제공한다.

  • 테스트 코드를 깨끗하게 유지하지 않으면 결국 잃어버린다.
  • 그리고 테스트 케이스가 없으면 실제 코드를 유연하게 만드는 버팀목도 사라진다.
  • 코드에 유연성, 유지보수성, 재사용성을 제공하는 버팀목이 바로 단위 테스트이다.
  • 테스트 코드가 지저분하면 코드를 변경하는 능력이 떨어지며 코드 구조를 개선하는 능력도 떨어진다. 테스트 코드가 지저분할수록 실제 코드도 지저분해진다. 결국 테스트 코드를 잃어버리고 실제 코드가 망가진다.

깨끗한 테스트 코드

  • 깨끗한 테스트 코드를 만들려면 가독성이 가장 중요하다.
  • 테스트 코드에서는 최소의 표현으로 많은 것을 나타내야 한다.

테스트당 assert는 하나

  • JUnit으로 테스트 코드 작성 시 함수마다 assert문을 단 하나만 사용해야 한다.
  • given-when-then 관례를 사용하면 테스트 코드를 읽기가 쉬워진다.
@Test
fun `피어그룹 조회 시 null이 아니다`() {
    val group = peerGroupService.findById(1L)
    assertThat(group).isNotNull
}

@Test
fun `피어그룹 이름이 올바르다`() {
    val group = peerGroupService.findById(1L)
    assertThat(group.name).isEqualTo("코스피 대형주")
}

@Test
fun `피어그룹 상태가 활성이다`() {
    val group = peerGroupService.findById(1L)
    assertThat(group.status).isEqualTo("ACTIVE")
}

@Test
fun `피어그룹 성과가 양수이다`() {
    val group = peerGroupService.findById(1L)
    assertThat(group.performance).isGreaterThan(BigDecimal.ZERO)
}

테스트당 개념은 하나다

  • 잡다한 개념을 연속으로 테스트하는 긴 함수는 피한다.

FIRST 원칙

  • Fast : 테스트는 빨라야 한다. 테스트는 빨리 돌아야 한다는 말이다. 테스트가 느리면 자주 돌릴 엄두를 못 낸다. 자주 돌리지 않으면 초반에 문제를 찾아내 고치지 못한다. 코드를 마음껏 정리하지도 못한다. 결국 코드 품질이 저하되기 시작한다.
  • Independent : 각 테스트는 서로 의존적이면 안 된다. 한 테스트가 다음 테스트가 실행될 환경을 준비해선 안 된다. 각 테스트는 독립적으로 그리고 어떤 순서로 실행해도 괜찮아야 한다. 테스트가 서로에게 의존하면 하나가 실패할 때 나머지가 잇달아 실패하므로 원인을 진단하기 어렵고 후반 테스트가 찾아내야 할 결함이 숨겨진다.
  • Repeatable : 테스트는 어떤 환경에서도 반복 가능해야 한다. 실제 환경, QA 환경, 버스 타고 집으로 돌악는 길에 사용하는 노트북 환경에서도 실행할 수 있어야 한다. 테스트가 돌아가지 않는 환경이 하나라도 있다면 테스트가 실패한 이유를 둘러댈 변명이 생긴다. 환경이 지원되지 않기에 테스트를 수행하지 못하는 상황에 직면한다.
  • Self-Validating : 테스트는 부울 값으로 결과를 내야 한다. 성공 아니면 실패다. 통과 여부를 알고자 로그 파일을 읽게 만들어선 안 된다. 테스트가 스스로 성공과 실패를 가늠하지 않는다면 판단은 주관적이 되며 지루한 수작업 평가가 필요하다.
  • Timely : 테스트는 적시에 작성해야 한다. 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다. 실제 코드를 구현한 다음에 테스트 코드를 만들면 실제 코드가 테스트하기 어렵다는 사실을 발견할지도 모른다. 어떤 실제 코드는 테스트하기 너무 어렵다고 판명날지 모른다. 테스트가 불가능하도록 실제 코드를 설게할지도 모른다.