본문 바로가기

단위 테스트

[단위 테스트] 좋은 단위 테스트의 4대 요소 자세히 살펴보기

1) 좋은 단위 테스트의 4가지 특성

 

- 좋은 단위 테스트에는 다음 네 가지 특성이 있다

 

(1) 회귀 방지

(2) 리팩터링 내성

(3) 빠른 피드백

(4) 유지 보수성

 

- 이 네 가지 특성이 기본이다.

  이 특성으로 어떤 자동화된 테스트(단위 테스트, 통합 테스트, E2E 테스트 등)도 분석할 수 있다.

  여기서는 처음 두 특성을 정의하고, 그 다음 두 특성 간의 본질적인 연관성을 설명한다. 

 

 

(1) 첫 번째 요소: 회귀 방지

- 좋은 단위 테스트의 첫 번째 특성인 회귀 방지이다.  

  회귀는 소프트웨어 버그다. 코드를 수정한 후 기능이 의도한 대로 작동하지 않는 경우다.

 

- 코드베이스가 커질수록 잠재적인 버그에 더 많이 노출된다. 

  그렇기 때문에 회귀에 대해 효과적인 보호를 개발하는 것이 중요하다.

 

- 이러한 보호가 없다면 프로젝트가 오랫동안 성장할 수 없으며 

  점점 더 많은 버그가 쌓일 것이다 .

 

- 회귀 방지 지표에 대한 테스트 점수가 얼마나 잘 나오는지 평가하려면 

  다음 사항을 고려해야 한다. 

 

(1) 테스트 중에 실행되는 코드의 양

(2) 코드 복잡도

(3) 코드의 도메인 유의성

 

- 일반적으로 실행되는 코드가 많을수록 테스트에서 회귀가 나타날 가능성이 높다.

  코드의 양뿐만 아니라 복잡도와 도메인 유의성도 중요하다.

 

- 복잡한 비즈니스 로직을 나타내는 코드가 보일러플레이트 코드보다 훨씬 더 중요하다.

  비즈니스에 중요한 기능에서 발생한 버그가 가장 큰 피해를 입히기 때문이다. 

 

- 반면에 단순한 코드를 테스트하는 것은 가치가 거의 없다.

  이러한 코드는 짧고, 비즈니스 로직을 많이 담고 있지도 않다.

  단순한 코드의 예로 다음과 같이 속성 한 줄이 있다.

public class User
{
	public string Name { get; set;}
}

   

- 게다가 여러분의 코드 외에 작성하지 않은 코드(ex) 라이브러리, 프레임워크 등)도 중요하다. 

  이 코드는 작성한 코드만큼이나 소프트웨어 작동에 영향을 미친다.

 

- 최상의 보호를 위해서는 테스트가 해당 라이브러리, 프레임워크, 외부 시스템을 테스트 범주에 포함시켜서

  소프트웨어가 이러한 의존성에 대해 검증이 올바른지 확인한다. 

 

 

(2) 두 번째 요소: 리팩터링 내성

- 좋은 단위 테스트의 두 번째 특성은 리팩터링 내성이다.

  이는 테스트를 '빨간색'(실패)으로 바꾸지 않고 

  기본 애플리케이션 코드를 리팩터링할 수 있는지에 대한 척도다.

 

- 이러한 상황을 상상해보자.

  새로운 기능을 개발했으며, 모든 기능에 잘 작동한다.

  여기에 리팩터링을 조금 하고 저기를 조금 고치면 모든 것이 전보다 훨씬 좋아 보인다.

  

- 단 하나, 테스트가 실패하고 있다는 것만 빼면 말이다.

  리팩터링으로 정확히 무엇이 고장 났는지를 자세히 살펴봤지만,

  알고 보니 아무것도 고장 나지 않았다.

 

(2-1) 거짓 양성(False Positive)

 

- 이러한 상황을 거짓 양성이라고 한다.

  실제로 기능이 의도한 대로 작동하지만 테스트는 실패를 나타내는 결과다.

 

-  이러한 거짓 양성을 일반적으로 코드를 리팩터링할 때,

   즉 구현을 수정하지만 식별할 수 있는 동작은 유지할 때 발생한다.

   따라서 좋은 단위 테스트의 한 가지 특성으로 이름 붙이자면 '리팩터링 내성'이라 할 수 있다. 

 

(2-2) 단위 테스트의 목표

 

- 단위 테스트의 목표는 프로젝트 성장을 지속 가능하게 하는 것이다.

  단위 테스트에는 두 가지 장점이 있다. 

 

(1) 기존 기능이 고장 났을 때, 테스트가 조기 경고를 제공한다.

     이러한 조기 경고 덕분에 결함이 있는 코드가 운영 환경에 배포되기 훨씬 전에 

     문제를 해결할 수 있다. 

     

(2) 코드 변경이 회귀로 이어지지 않을 것이라고 확신하게 된다.

     이러한 확신이 없으면 리팩터링을 하는데 주저하게 되고

     코드베이스가 나빠질 가능성이 훨씬 높아진다. 

 

 

(2-3) 거짓 양성의 문제점

 

- 거짓 양성을 이 두 가지 이점을 모두 방해한다.

(1) 테스트가 타당한 이유 없이 실패하면, 코드 문제에 대응하는 능력과 의지가 희석된다.

     이내 타당한 실패도 무시하기 시작해 기능이 고장 나도 운영 환경에 들어가게 된다. 

 

(2) 반면에 거짓 양성이 빈번하면 테스트 스위트에 대한 신뢰가 서서히 떨어지며,

     더 이상 믿을 만한 안전망으로 인식하지 않는다.

     이렇게 신뢰가 부족해지면 리팩터링이 줄어든다.

     회귀를 피하려고 코드 변경을 최소한으로 하기 때문이다. 

 

 

(2-4) 결론

 

- 이 이야기는 깨지기 쉬운 테스트가 있는 프로젝트 대다수에 해당하는 전형적인 사례다.

  처음에는 개발자들이 테스트 실패를 액면 그대로 받아들이고 이에 따라 처리한다.

  결국 실제 버그가 운영 환경에 릴리스되는 순간이 온다. 

 

- 하지만 리팩터링을 중단해서 이러한 상황을 피하고 싶지는 않다.

  올바른 대응은 테스트 스위트를 다시 살펴보고 안정성을 높이기 시작하는 것이다. 

 

 

(3) 세 번째 요소:  빠른 피드백

- 빠른 피드백은 단위 테스트의 필수 속성이다. 

  테스트 속도가 빠를수록 테스트 스위트에서 더 많은 테스트를 수행할 수 있고

  더 자주 실행할 수 있다.

  

- 테스트가 빠르게 실행되면 코드에 결함이 생기자마자

   버그에 대해 경고하기 시작할 정도로 피드백 루프를 대폭 줄여서,

   버그를 수정하는 비용을 거의 0까지 줄일 수 있다.

 

- 반면에 느린 테스트는 피드백을 느리게 하고,

  잠재적으로 버그를 뒤늦게 눈에 띄게 해서 버그 수정 비용이 증가한다.

 

 

(4) 네 번째 요소: 유지 보수성

- 마지막으로, 좋은 단위 테스트의 네 번째 특성인 유지 보수성 지표는 

  유지비를 평가한다.

   이 지표는 다음 두 가지 주요 요소로 구성된다. 

 

(1) 테스트가 얼마나 이해하기 어려운가?

- 이 구성 요소는 테스트의 크기와 관련이 있다.

  테스트는 코드 라인이 적을수록 더 읽기 쉽다.

  작은 테스트는 필요할 때 변경하는 것도 쉽다.

 

- 테스트 코드의 품질은 제품 코드만큼 중요하다.

  테스트를 작성할 때 절차를 생략하지 말라. 

  테스트 코드를 일급 시민으로 취급하라. 

 

 

(2) 테스트가 얼마나 실행하기 어려운가?

- 테스트가 프로세스 외부 종속성으로 작동하면,

  데이터베이스 서버를 재부팅하고 네트워크 연결 문제를 해결하는 등 

  의존성을 상시 운영하는 데 시간을 들여야 한다. 

 

 

 

   

 

 

 

'단위 테스트' 카테고리의 다른 글

[단위 테스트] 목과 테스트 취약성  (0) 2024.07.17