단위 테스트 (Unit Testing) 책 리뷰 ( 작성 중 )

Mugeon Kim·2024년 3월 24일
0

테스트 코드

목록 보기
2/2
post-thumbnail

서론


  • 이번에 회사에서 책 지원으로 단위 테스트 책을 구매하였습니다. 관련 링크 책을 읽고 단순히 작성한 테스트 코드에서의 문제점을 알 수 있었고 지금 근무하고 있는 현업에서 어떻게 적절하게 도입할 수 있을까? 생각할 수 있는 좋은 기회가 되었습니다.

본론


1. 테스트의 목표

  • 테스트의 목표는 크게 안전망, 지속적인 성장이 가능한 소프트웨어를 구축이 있다. 처음에 좋은 구조로 설계가 되어져 있어도 코드베이스에 변경이 생길 때마다 엔트로피 즉 무질서, 복합이 증가하여 시스템을 흐트러진다.

  • 테스트가 이러한 시스템에 안전망의 역활을 하며 회귀에 대한 보험을 제공하는 도구로 사용한다. 이를 위한 성공적인 테스트 스위트 (test suite)를 가지기 위해서는 개발 주기에 테스트가 포함이 되어 있어야 하고 코드 베이스에서 가장 중요한 부분을 테스트하며 최소한의 테스트로 최대한의 가치를 가져가야한다.

즉. 최소한의 테스트로 최대한의 가치를 얻어야 한다는 뜻은 결국에는 테스트도 유지보수에 비용이 든다는 것을 의미한다. 그러므로 우리는 테스트 커버리지를 무조건 1번째로 생각하면 안된다. ( 테스트를 작성하는 것 자체가 목표가 되면 안된다. )

2. 단위 테스트

2-1. 단위 테스트 정의

  • 단위 테스트 (Unit Test)는 하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 테스트이다. 즉. 하나의 기능 또는 메서드 단위의 테스트를 의미하며 테스트를 통하여 어떤 기능이 실행되면 어떤 결과가 나온다를 알 수 있다.

2-2. First 원칙과 테스트 분파(고전파, 런던파)

  • 테스트에 대해서 학습하면 First 원칙에 대해서 보게된다. 좋은 테스트의 조건으로는 다음과 같다.
  1. [F]irst : 빠른
  2. [I]solated : 고립된
  3. [R]epeatable : 반복 가능한
  4. [S]elf-vaildating : 스스로 검증 가능한
  5. [T]imely : 적시의
  • 단위 테스트에는 일반적으로 2가지 학파가(고전파, 런던파) 있다. 쉽게 생각해서 실제 코드를 통한 테스트, 목 테스트로 나눌 수 있는데 위에 테스트 원칙에서 두 학파는 무엇이 단위를 의미하는지에 대한 관점과 의존성 처리에 대한 처리 방식이 다르다.

2-3. 런던파

  1. 테스트 단위를 클래스 수준으로 간주합니다.
  2. 불변 의존성을 제외한 모든 의존성을 테스트 대역(test double)으로 대체합니다.
  3. 각 테스트 단위를 완전히 분리하여 독립적으로 실행할 수 있도록 합니다.
  4. 모의 객체(mock)를 광범위하게 사용하여 의존성을 대체합니다.

런던파의 장점으로는 더 나은 격리성, 높은 테스트 용이성, 실패 원인을 빠르게 식별할 수 있다는 점 등이 있습니다. 하지만 과도한 모의 객체 사용으로 인해 테스트 코드의 복잡성이 증가할 수 있다는 단점이 있습니다.

2-4. 고전파

  1. 테스트 단위를 메서드 수준으로 간주합니다.
  2. 실제 의존성을 사용하며, 필요한 경우에만 테스트 대역을 사용합니다.
  3. 테스트 간의 상호작용과 통합을 허용합니다.
  4. 실제 객체를 사용하여 테스트합니다.

고전파의 장점으로는 실제 코드와 유사한 환경에서 테스트를 수행하므로 테스트의 신뢰성이 높다는 점이 있습니다. 하지만 의존성으로 인해 테스트 실행 속도가 느려질 수 있고, 테스트 간의 결합도가 높아질 수 있다는 단점이 있습니다.

위에 First원칙과 장단점으로 보면 런던파가 매력적으로 다가오지만 개인적으로 생각하기에 지속적인 성장을 하는 소프트웨어를 만들기 위해서는 런던파 관점의 테스트를 조심해야한다.

단기적으로는 런던파 테스트가 빠르고 격리성이 보장하는 장점을 가지고 있지만 서비스가 커지고 시간이 지나면 과도한 목 객체 사용으로 테스트 코드의 복잡도가 증가하고 실제 코드와 괴리가 생긴다.
또한 실제 코드의 변경 사항을 반영하지 못할 수 있기 때문에 테스트의 신뢰성이 떨어질 수 있다.
밑에서 설명하겠지만 각 Layer에 맞는 테스트를 작성하며 Test Double을 최소화 하여 지속적으로 리펙토링이 가능한 테스트 코드를 가지고 가야한다.


2-5. 좋은 테스트의 4대요소

  • 단위 테스트에서 말하는 좋은 테스트는 4대 요소가 있다.
  1. 회귀 방지 (Protection against regressions)
  2. 리팩터링 내성 (Resistance to refactoring)
  3. 빠른 피드백 (Fast feedback)
  4. 유지보수 용이성 (Maintainability)

2-5-1. 회귀 방지

  • 테스트가 얼마나 버그(회귀)의 존재를 잘 나타내는지에 대한 척도이다. 즉. 소프트웨어 버그를 방지한다는 것을 의미한다.

2-5-2. 리팩토링 내성

  • 개인적으로 책을 보면서 가장 중요한 부분이라고 생각을 하였다.

  • 리팩토링을 하였을 때 테스트가 실패하면 안된다. 즉. 정상적으로 작동하는데 테스트가 실패하면 안된다를 의미한다. 리팩토링을 하였을 때 거짓 양성이 일어난다면 우리는 나중에 실패하는 테스트를 깊이있게 보려고 하지 않을 것이다.

  • 즉. 테스트에 대한 신뢰성이 깨지며 지속적으로 성장하는 소프트웨어를 만들 수 없다.

리팩토링 내성이 있는 소프트웨어가 지속적으로 성장이 가능한 소프트웨어인데 이것을 만들기 위해서는 테스트가 실제 객체의 세부 구현에 의존하지 않도록 해야한다. 즉 변경되지 않는 부분인 추상화된 인터페이스에 대한 결과를 검증하게 해야된다.

2-5-3. 빠른 피드백

  • 테스트가 얼마나 빨리 실행되는지에 대한 척도이다. 테스트를 빠르게 실행이 중요한 이유는 전체 팀원들이 테스트를 통해 정보 또는 오류 또는 피드백을 받을 수 있는 시간을 단축을 시켜준다. 즉. 리소스를 줄일 수 있다.

2-5-4. 유지 보수성

  • 말 그대로 유지 보수에 필요한 비용을 의미한다. 여기서 2가지 요소로 나눌 수 있다.
  1. 테스트 이해 난이도, 테스트가 작을수록 읽기 쉽다.
  2. 테스트 실행 난이도, 외부 의존성이 적을수록 쉽게 운영할 수 있다.

2-5-5. 테스트 가치

위에 4가지 (회귀방지, 리팩토링 내성, 빠른 피드백, 유지 보수)의 특성을 곱한 값이다.
따라서 4자기 특징에서 1개라도 0이면 테스트의 가치는 0이 된다. 우리는 위에 4가지 특징에서 리팩터링 내성, 유지 보수성을 가져가고 회귀 방지, 빠른 피드백을 두고 고민을 해야된다.

2-5-6. 블랙, 화이트박스 테스트

  • 처음에 테스트를 진행하면서 private 테스트에 대해서 고민을 가지게 되었다. 테스트를 알아보니 Java Reflection API를 통해서 메서드를 호출하거나 Utils를 따로 분리하여 테스트를 진행할 수 있다.

  • 하지만 private 테스트를 작성하는 것은 고민을 해보아야 한다. private을 먼저 생각해보면 외부에 접근을 제어하여 내부의 응집도를 높힌다. 그러나 클라이언트에서 내부 메서드를 알고 있으면 결국에는 결합도가 높아진다. 이는 유지보수에 대한 비용을 증가시키며 또한 메서드 이름, 파라미터가 변경할 때 실패하게 된다. 하지만 가장 큰 문제는 Reflection을 통해서 가져오기 때문에 컴파일 에러를 유발하지 못하므로 Private 메서드의 테스트는 지양하며 팀에서 필요한 테스트라고 한다면 같이 고민하는게 좋을것이다.

테스트를 작성할 때 블랙박스 테스트를 사용하고 테스트를 분석할 때는 화이트박스 방법을 사용하라.

올바른 단위 테스트 작성방법

가치 있는 테스트를 작성하는 방법

통합 테스트를 작성해야 하는 이유

테스트에 목을 적용할 때 고려할 사항

데이터베이스 테스트

단위 테스트 안티패턴

시간 처리하기

참고


https://dzone.com/articles/7-popular-unit-test-naming
https://www.yes24.com/Product/Goods/104084175

profile
빠르게 실패하고 자세하게 학습하기

0개의 댓글