[Clean Code] Chapter 9. 단위 테스트

joyful·2024년 9월 1일
0

개발서적

목록 보기
9/9

들어가기 앞서

이 글은 개발자 필독서인 클린 코드를 읽으며 습득한 내용을 정리한 글입니다. 모든 출처는 해당 저서에 있습니다.


1. TDD 법칙 세 가지

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



2. 클린 테스트 코드 유지하기

  • 테스트 코드는 실제 코드 못지 않게 중요하다.
  • 테스트 코드는 사고·설계·주의가 필요하다.
  • 테스트 코드는 실제코드 못지않게 깨끗하게 작성해야 한다.
  • 지저분한 테스트 코드를 작성하는 것은 테스트를 하지 않는 것보다 더 해로울 수 있다.
    • 실제 코드가 변경될 때 테스트 코드도 함께 수정되어야 하는데, 테스트 코드가 지저분할수록 이러한 수정이 어려워진다.
    • 테스트 코드가 복잡할수록 실제 코드 작성보다 테스트 케이스를 추가하는 데 더 많은 시간이 소요될 수 있다.
    • 코드 변경으로 인해 기존 테스트 케이스가 실패하기 시작하면, 테스트를 통과시키기가 점점 더 어려워진다.
    • 새 버전을 출시할 때마다 테스트 케이스를 유지보수하는 비용이 증가하게 된다.
  • 그럼에도 불구하고 테스트 수트는 필수적이다.
    • 테스트 수트는 개발자가 수정한 코드가 정상적으로 동작하는지 확인할 수 있는 중요한 도구다.
    • 시스템의 일부를 변경할 때 다른 부분이 안전하게 유지되고 있는지 검증할 수 있는 방법을 제공한다.
  • 테스트 코드를 깨끗하게 작성해야 하는 이유
    • 테스트 코드가 엉망으로 작성되면 결함이 많아지고, 개발자는 코드 변경이 오히려 해롭다고 느껴 더 이상 코드를 정리하지 않게 된다. 그 결과, 전체 코드 품질이 낮아진다.

2.1 테스트는 코드에 특성을 부여한다

  • 단위 테스트는 코드에 유연성, 유지보수성, 재사용성을 제공한다.
  • 실제 코드를 점검하는 자동화된 단위 테스트 수트는 설계와 아키텍처를 깨끗하게 보존하는 열쇠다.
    • 테스트 커버리지가 높을수록 안심하고 아키텍처와 설계를 개선할 수 있다.
    • 테스트 코드가 지저분하면 코드 변경 및 구조 개선 능력이 저하된다.



3. 클린 테스트 코드

  • 테스트 코드의 가독성을 향상하기 위해서는 명료성, 단순성, 풍부한 표현력이 필요하다.
    • 진짜로 필요한 자료 유형과 함수를 사용하여 코드의 수행 기능을 이해하는 속도를 향상시키도록 한다.
    • BUILD-OPERATE-CHECK 패턴을 사용한다.

      💡 BUILD-OPERATER-CHECK 패턴
      테스트 자료 작성 - 테스트 자료 조작 - 조작한 결과가 올바른지 확인


3.1 해당 분야에 밀접한 테스트 언어

  • API 위에 구현한 함수와 유틸리티는 테스트 코드에서 사용하는 특수 API이며, 테스트 코드를 작성하고 읽기 쉬워진다.
  • 본인의 코드를 좀더 간결하고 표현력이 풍부한 코드로 리팩토링하도록 한다.

3.2 이중 표준

  • 테스트 API 코드에 적용하는 표준은 실제 코드에 적용하는 표준과 다르다.
  • 단순하고, 간결하고, 표현력이 풍부해야 하지만, 실제 코드만큼 효율적일 필요는 없다.
  • 이는, 실제 환경과 테스트 환경은 요구사항이 다르기 때문이다.
    • 테스트 환경은 자원이 제한적일 가능성이 낮기 때문에, 실제 환경과는 다르게 문제없는 방식이 존재한다.



4. 테스트 당 assert 하나

  • assert문이 하나인 함수는 결론이 하나라 코드를 이해하기 쉽고 빠르다.
    • 예시 - 테스트를 두 개로 분리하여 각각 assert를 수행

      public void testGetPageHierarchyAsXml() throws Exception {
      	givenPages("PageOne", "PageOne.ChildOne", "PageTwo");
          whenRequestIsIssued("root", "type:pages");
          thenResponseShouldBeXML();
      }
      
      public void testGetPageHierarchyHasRightTags() throws Exception {
      	givenPages("PageOne", "PageOne.ChildOne", "PageTwo");
          whenRequestIsIssued("root", "type:pages");
          thenResponseShouldContain(
          	"<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>"
          );
      }
      • given-when-then 관례를 사용하여 테스트 코드의 가독성이 향상되었다.
  • 테스트를 분리하다보면 중복되는 코드가 발생하게 된다. 이를 해결하는 방법은 다음과 같다.
    1. 템플릿 메소드(TEMPLATE METHOD) 패턴
      • given/when 부분을 부모 클래스에 두고, then 부분을 자식 클래스에 둔다.
    2. 독자적인 테스트 클래스 생성
      • @Before 함수에 given/when 부분을 넣고, @Test 함수에 then 부분을 넣는다.
  • 위의 방법들은 과도할 수 있으며, 차라리 assert 문을 여러 개 사용하는 것이 더 효율적이다.
  • 다만, assert 문의 개수는 가능한 한 최소화하는 것이 좋다.

4.1 테스트 당 개념 하나

  • 테스트 함수 하나는 개념 하나만 테스트하도록 한다.
  • 잡다한 개념을 연속으로 테스트하는 긴 함수는 피한다.
    • 여러 개념을 함수 하나에 넣으면, 독자가 각 절이 위치한 이유와 테스트하는 개념을 모두 이해해야 한다.



5. F.I.R.S.T.

클린 테스트는 다음 다섯 가지 규칙을 따른다.

✅ 빠르게(Fast)

테스트는 빨리 돌아야 한다.

  • 테스트가 느리면 테스트를 자주 실행하지 않게 되어, 초기 단계에서 문제를 발견하고 수정할 기회를 놓치게 된다.
  • 이로 인해 코드 정리에 소홀해지고, 결국 코드 품질이 저하된다.

✅ 독립적으로(Independent)

각 테스트는 서로 의존하면 안 된다.

  • 각 테스트는 독립적으로, 순서 무관하게 실행해도 괜찮아야 한다.
  • 테스트가 서로에게 의존하면, 하나의 테스트가 실패할 때 다른 테스트도 연달아 실패하여 원인을 진단하기 어려워진다.
  • 이로 인해 후반 테스트에서 발견해야 할 결함이 숨겨질 수 있다.

✅ 반복 가능하게(Repeatable)

테스트는 어떤 환경에서도 반복이 가능해야 한다.

  • 테스트가 실행되지 않는 환경이 하나라도 존재하면, 테스트 실패에 대한 변명의 여지가 생긴다.
  • 환경이 지원되지 않아 테스트를 수행할 수 없는 상황에 직면할 수도 있다.

✅ 자가 검증하는(Self-Validating)

테스트는 bool 값으로 결과를 내야 한다.

  • 테스트가 스스로 성공과 실패를 판단하지 못하면, 평가가 주관적으로 변하고 수작업 검토가 필요하게 된다.

✅ 적시에(Timely)

테스트는 적시에 작성해야 한다.

  • 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다.
  • 테스트 코드를 먼저 작성해야 하는 이유
    • 실제 코드를 먼저 구현한 후 테스트 코드를 작성하면 테스트하기 어려운 코드가 될 수 있다.
    • 이로 인해 테스트를 고려한 코드 설계가 어려워질 수 있다.



6. 결론

  • 테스트 코드는 실제 코드만큼이나 프로젝트의 품질에 영향을 미친다.
    • 테스트 코드는 실제 코드의 유연성, 유지보수성, 재사용성을 보존하고 강화한다.
  • 따라서, 테스트 코드는 지속적으로 깨끗하게 관리하도록 한다.
    • 표현력을 높이고 간결하게 정리한다.
    • 테스트 API를 구현해 해당 분야 언어를 만든다.
  • 이는 테스트 코드 작성 난도를 낮출 뿐만 아니라, 테스트 코드에 따른 실제 코드의 품질에도 영향을 미친다.

📖 참고

  • 로버트 C. 마틴, 『Clean Code 클린 코드 애자일 소프트웨어 장인 정신』, 박재호·이해영 옮김, 케이앤피북스(2010), p183-198.
profile
기쁘게 코딩하고 싶은 백엔드 개발자

0개의 댓글