애플리케이션을 개발할 때 중요하지만 잘 지켜지지 않는 것이 테스트코드를 작성하는 것이다.
테스트 코드를 왜 작성해야할까? 여러가지 이유가 있겠지만 결국 유지보수 비용의 절감. 즉, 개발시간의 단축이다.
테스트코드가 없는 상태에서 애플리케이션의 크기가 거대해지면 기능 추가나 변경이 어려워진다. 이 상태에서 여러 개발자의 손을 타게되면 코드는 더욱더 복잡해지고 리팩토링은 커녕 자연스레 모두가 수정하기 꺼려하는 코드가 된다. (테스트 코드 부재로 여러 케이스별를 검증시에 시간이 오래걸리므로)
이렇게 코드는 방치되고 해당 기능을 수정할 때 유지보수 비용은 폭발적으로 늘어난다. 만약 테스트 코드를 작성한다면 위 상황을 모두 방지할 수 있다.
자동화된 테스트? 회귀 테스트?
크고 복잡한 시스템에서는 수정한 코드가 어디에 어떤 영향을 줄지 완벽하게 분석하기란 불가능
에 가깝다.
테스트 코드가 없는 조직에서는 코드를 수정한 후 일부 기능을 수동 테스트
로 진행하게 된다.
- 안하는 것보다는 낫겠지만, 다양한 경우의 수를 확인하기 어렵다.
- 현실적으로 모든 경우의 테스트를 할 수 없기에 버그를 놓칠 가능성이 있다.
만약, 자동으로 실행할 수 있는 테스트 코드가 있다면 수정한 코드가 발생시키는 문제를 빨리 찾을 수 있다. 이렇게 되면 QA 담당자를 기다릴 필요도 없고 개발자가 직접 실행하면 된다.
테스트 커버리지가 높다고 하여 모든 문제를 없앨 수 없지만 테스트를 통과한 코드는 문제가 없다는 것을 확신
할 수 있게 된다. 테스트 검증하는 범위가 넓을수록 코드를 수정하는데 자신감이 생긴다.
- 커버리지를 지나치게 높이기 위해 가치 없는 코드를 만드느라 시간을 낭비하지 말자. 70~80% 수준이면 적당한다.
회귀 테스트(regression test)
는 수정한 기능 외에 다른 기능에 영향이 없는지 검증하는 테스트를 말한다. 자동화된 테스트가 있으면 회귀 테스트를 쉽게 진행할 수 있으며, QA 담당자는 신규 기능 및 몇 가지 필수 기능만 점검하면 된다. (자동화된 테스트가 많은 범위를 검증하기 때문)
테스트 주도 개발(TDD)?
TDD는 테스트 코드를 먼저 만드는 개발 방식이다. 구현할 대상에 대한 테스트 코드를 먼저 만들고 테스트 코드르 통과시킬 만큼 구현을 진행한다.
TDD를 하면 기능을 설계하는 데 도움을 준다. 또한 테스트 코드를 작성하면 업무에 대한 이해도 함께 높일 수 있다. 테스트를 만들려면 테스트할 대상의 기능을 실행할 수 있어야하기에 아래와 같은 것들을 미리 정해야 하는데, 이는 기능을 설계하는 과정과 동일하다.
- 클래스 이름
- 메서드 이름
- 메서드 파라미터 타입
- 리턴 타입, 익셉션 타입(예외)
- 의존 대상과 역할
테스트 주도 개발과 생산성
테스트 코드를 작성하는 자체가 생산성이 떨어지는 일이라 생각할 수 있지만 전체적인 개발 관점에서 보면 테스트가 주는 이점은 상당히 크다. 하여 테스트 코드 작성 능력을 키워둬야 한다.개발 시간 = 코드를 작성하는 시간 + 테스트하는 시간 + 디버깅하는 시간
개발을 완료할 때까지 개발, 테스트, 디버깅을 반복한다. 개발 시간을 줄이려면 코딩하는 시간뿐 아니라 테스트, 디버깅 시간을 줄여야하는데 TDD를 적용하면 이 시간들을 줄일 수 있다.
개발자는 코드를 수정하면 테스트를 해야 한다.
만약, 테스트 코드 없이 수동으로 기능을 검증해야 한다면?
- 코드를 수정해서 확인하고 잘못된 곳이 발견되면 또 코드를 수정해서 확인하는 과정을 반복적으로 해야하는데 수동으로 테스트를 한다면 다양한 예외 상황을 만들기 어렵다.
- 수동으로 테스트하려면 일단 필요한 코드를 모두 만들어야 한다. (컨트롤러, 서비스, 데이터베이스 연동 등) 한 번에 다 만들어야 하니 시간이 오래 걸리며, 실행 과정에서 에러가 발생하면 확인할 코드가 많아지므로 시간이 오래 걸리고 흐름 또한 깨지기 쉽다.
- 수동으로 테스트할 때는 대부분 데이터베이스와 외부 API를 직접 연동한다. 따라서 원하는 상황을 만들려면 데이터베이스에 테스트 데이터를 구성해야하거나 외부 API를 사용할 때는 API 제공 부서에 연락해서 도움을 요청해야 한다. 이런 번거로운 과정은 개발 시간을 증가시키고 개발 자체를 힘겹게 만든다.
테스트 코드를 자동화 한다면? TDD를 사용한다면?
- 테스트 코드를 만들기 때문에 반복되는 테스트 시간을 줄여준다. 처음에는 개발시간이 늘어나는 것처럼 느껴지지만 시간이 갈수록 테스트 시간을 줄여줘 오히려 개발 시간이 줄어드는 것을 경험할 수 있다.
- 코드를 작성하는 시점과 테스트 시점 간의 차이가 벌어질수록 문제가 발생했을 때 원인을 찾는데 더 긴 시간이 걸린다. (코드를 다시 읽고 분석해야 하기 때문) TDD는 기능을 구현하자마자 테스트를 실행하기때문에 테스트 직전에 코드를 작성했기 때문에 실해 원인을 빨리 찾을 수 있다. (디버깅 시간 축소)
- TDD는 리팩터링을 포함하기에 코드 구조와 가독성을 개선하는 작업을 함께 진행한다. 이는 미래의 디버깅 시간과 코딩 시간을 줄여주는 것을 의미한다.
- TDD를 진행하면 전체가 아닌 일부 코드만 검증 할 수 있다. 하여 컨트롤러-서비스-모델-리포지터리처럼 범위를 좁혀서 한 번에 하나만 집중할 수 있도록 도와준다.
테스트 가능성
모든 코드를 TDD로 진행할 필요는 없다. 개발을 먼저 하고 테스트 코드를 작성해도 되고 TDD처럼 테스트 코드를 먼저 작성해도 된다. 궁극적으로는 테스트 가능성(testability)
를 높이는데 목표를 두어야 한다.
코드를 만들 때 테스트 가능성을 염두에 두면 개발 생산성과 설계 품질을 높일 수 있다. 테스트 가능성을 염두해 두지 않고 작성된 코드는 나중에 테스트 코드를 추가하는데도 쉽지 않으며 코드의 변경을 동반하며 테스트 코드를 작성해야해서 수정사항에 따른 검증이 쉽지 않다. (코드의 변경을 동반하지 않고 통합 테스트를 만든 후 리팩토링하는 방법이 있지만 쉽지 않다.)
- 리팩토링이 동반되지 않으면 배보다 배꼽이 큰 테스트 코드를 만들게 될 것이다.
테스트 가능성을 염두해둔다면 구현을 분리해서 코드를 작성하게 되는데, 외부 연동 구현같은 경우 손쉽게 mocking 처리할 수 있게 된다.(외부 환경에 대한 의존이 줄어 전체가 아닌 일부만 빠르게 테스트할 수 있다.)
=> 테스트 가능성을 높이는 과정에서 자연스럽게 역할이 분리되게 되어있다.
참고) 육각형 개발자
'프로그래밍 노트 > TEST를 해보자' 카테고리의 다른 글
[Fixture Monkey] 테스트 생산성 향상을 위해 Fixture Monkey를 찍먹해보자 (0) | 2024.10.02 |
---|---|
[Kotest] 몇 가지 팁 (0) | 2023.10.26 |
[Kotest] kotest, mockk 사용하기 (0) | 2023.10.25 |
[Mockito] @Spy, @SpyMock 스터빙(stubbing)(thenReturn vs doReturn) (0) | 2020.12.14 |
[Mockito] Mockito이용하여 테스트하기(@Mock, @Spy, @InjectMocks, @MockBean, @SpyBean) (1) | 2020.12.14 |