9장. 단위 테스트

공부하는 감자·2024년 2월 12일
0

클린코드

목록 보기
9/18

이 글에서 분류한 기준은 책의 내용을 바탕으로 주관적인 견해로 재정리해본 것입니다.

TDD

TDD 법칙 세 가지

  1. 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
  2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
  3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.

방대한 테스트 코드의 문제

  • 위 법칙을 모두 지키면서 테스트 케이스를 만들면 실제 코드와 맞먹을 정도로 방대한 테스트 코드가 나온다.
  • 이러한 방대한 코드는 심각한 관리 문제를 유발하기도 한다.

깨끗한 테스트 코드

지저분한 테스트 코드

핵심: 테스트 코드는 실제 코드 못지 않게 중요하므로 깨끗하게 짜야 한다.

실제 코드가 진화하면 테스트 코드도 변해야 한다. 그런데, 테스트 코드가 지저분할 수록 변경하기 어려워 진다.

  • 테스트 코드가 복잡할 수록 실제 코드를 짜는 시간보다 테스트 코드를 추가하는 시간이 더 걸린다.
  • 실제 코드를 변경해 기존 테스트 케이스가 실패했을 경우, 지저분한 코드로 인해 실패하는 케이스를 점점 더 통과시키기 어려워진다.
  • 새 버전을 출시할 대마다 팀이 테스트 케이스를 유지하고 보수하는 비용도 늘어난다.

결국 테스트 슈트를 폐기하지 않으면 안 되는 상황에 처한다.

  • 테스트 슈트가 없으면 결합율이 높아지기 시작한다.
  • 결함 수가 많아지면 개발자는 변경을 주저하고, 더 이상 코드를 정리하지 않는다.
  • 코드가 망가지기 시작한다.

깨끗한 테스트 코드

테스트가 제공하는 것

테스트 케이스가 있으면 변경이 두렵지 않다.

테스트는 아래 세 가지를 제공한다.

  • 유연성
  • 유지보수성
  • 재사용성

단위 테스트

실제 코드를 점검하는 자동화된 단위 테스트 슈트는 설계와 아키텍처를 최대한 깨끗하게 보존하는 열쇠다.

그러나 테스트 코드를 깨끗하게 유지하지 않으면 결국은 잃어버린다. 이 세 가지를 제공하는 버팀목이 바로 단위 테스트이다.

  • 테스트 케이스가 없다면 모든 변경이 잠정적인 버그다.
  • 반대로 말해, 테스트 케이스가 있다면 아키텍처가 부실한 코드나 설계가 모호하고 엉망인 코드라도 우려 없이 변경할 수 있다.

깨끗한 테스트의 핵심은 가독성

  • 테스트 코드에서 가독성을 높이려면 명료성, 단순성, 풍부한 표현력이 필요하다.
  • 테스트 코드는 최소의 표현으로 많은 것을 나타내야 한다.
  • BUILD-OPERATE-CHECK 패턴을 사용하여 잡다하고 세세한 코드를 거의 다 없애고 진짜 필요한 자료 유형만 함수만 사용하도록 한다.
    • BUILD: 첫 부분은 테스트 자료를 만든다.
    • OPERATE: 두 번째 부분은 테스트 자료를 조작한다.
    • CHECK: 세 번째 부분은 조작한 결과가 올바른지 확인한다.

도메인에 특화된 언어 (DSL)

  • 흔히 쓰는 시스템 조작 API를 사용하는 대신, API 위에 함수와 유틸리티를 구현한다.
    • 이렇게 구현한 함수와 유틸리티는 테스트 코드에서 사용하는 특수 API가 된다.
    • 즉, 테스트를 구현하는 당사자와 나중에 테스트를 읽어볼 독자를 도와주는 테스트 언어다.
  • 이러한 테스트 API는 처음부터 설계된 API가 아니다.
    • 잡다하고 세세한 사항으로 범벅된 코드를 계속 리팩터링하다가 진화된 API다.

이중표준

  • 테스트 API 코드에 적용하는 표준은 실제 코드에 적용하는 표준과 확실히 다르다.
  • 실제 환경과 테스트 환경은 요구사항이 판이하게 다르다.
    • 테스트 코드는 실제 코드만큼 효율적일 필요가 없다.
  • 이중 표준의 본질은 실제 환경에서 절대로 안 되지만 테스트 환경에서는 전혀 문제없는 방식이 있다는 것이다.
    • 대개 메모리나 CPU 효율과 관련 있는 경우
    • 코드의 깨끗함과는 철저히 무관하다.

테스트 코드와 assert 개수

테스트당 assert 하나

  • JUnit으로 테스트 코드를 짤 때는 함수마다 assert 문을 단 하나만 사용해야 한다.
    • 결론이 하나라서 코드를 이해하기 쉽고 빠르다.
  • 테스트 코드를 분리하면 중복되는 코드가 많아지는데, 이 경우엔 Template Method 패턴을 사용하여 중복을 제거할 수 있다.
    • given-when-then 관례를 사용한다면 given과 when부분은 부모 클래스에 두고 then 부분은 자식 클래스에 둔다.
    • 혹은 완전히 독자적인 테스트 클래스를 만들어 @Before 함수에 given/when 부분을 넣고 @test 함수에 then 부분을 넣어도 된다.
  • 하지만 어떠한 방식을 사용하더라도 배보다 배꼽이 더 크다.

테스트 당 assert 여러 개

  • 이것저것 감안해 보면 결국 assert 문을 여럿 사용하는 편이 좋다.
  • ‘단일 assert 문’이라는 규칙을 지키되, 불가피한 경우 여러 assert 문을 넣는 것이 낫다.
    • 그래도 assert 개수는 최대한 줄여야 좋다는 것을 기억하자.

테스트당 개념 하나

  • 테스트 함수마다 한 개념만 테스트하라.
  • 이렇게 표현하면 장황한 테스트 코드 속에 감춰진 일반적인 규칙이 보인다.

결론

  • 테스트 함수 하나는 개념 하나만 테스트 하라
  • 개념 당 assert 문 수를 최소로 줄여라

F.I.R.S.T

깨끗한 테스트는 다음 다섯 가지 규칙을 따른다.

  • 빠르게 Fast
    • 테스트는 빨리 돌아야 한다.
    • 테스트는 자주 돌려야 하므로, 빨라야 한다.
  • 독립적으로 Independent
    • 각 테스트는 서로 의존하면 안 된다.
    • 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안 된다.
    • 테스트가 서로에게 의존하면 하나가 실패할 때 나머지도 잇달아 실패하므로 원인을 진단하기 어려워진다.
  • 반복가능하게 Repeatable
    • 테스트는 어떤 환경에서도 반복 가능해야 한다.
    • 실제 환경, QA 환경, 네트워크에 연결되지 않은 노트북 환경 등
  • 자가검증하는 Self-Validating
    • 테스트는 bool 값으로 결과를 내야 한다. (성공 아니면 실패)
    • 통과 여부를 알려고 로그 파일을 읽게 만들거나, 텍스트 파일 두 개를 수작업으로 비교하게 만들어서는 안 된다.
  • 적시에 Timely
    • 테스트는 적시에 작성해야 한다.
    • 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다.

Reference

참고 서적

📔 Clean Code

profile
책을 읽거나 강의를 들으며 공부한 내용을 정리합니다. 가끔 개발하는데 있었던 이슈도 올립니다.

0개의 댓글