설마 아직도 테스트 코드를 작성 안 하시나요?
가끔 욕을 먹고 싶을 때가 있을 수도 있죠. 가끔 지탄을 받고 꾸중을 들음으로써 자극을 받고 정신을 차리고 싶을 수도 있습니다. 아니면 혹은 그냥 아무 이유 없이 갑자기 한심한 눈초리를 받고 싶을 때가 있을 수도 있겠죠. 그럴 땐 주변에 있는 훌륭한 개발자를 잡아놓고 “저희는 테스트 코드 안 짜요.” 라고 한 마디 건네 보세요. 아주 쉽게 원하는 것을 얻으실 수 있을 것입니다.
테스트는 중요합니다.
테스트의 장점은 너무 많습니다. 제품의 안정성을 높이고, 기능 추가 및 수정으로 인한 부작용(Side-effect)를 줄일 수 있으며, 불안감 없이 코드 작성을 할 수 있도록 도와주고, 결과적으로 생산성을 매우 높여 줍니다. 디버깅을 쉽게 해주고, 개발 과정에서 반복적인 작업들을 하지 않도록 도와주며, 더 깔끔하고 재사용성이 좋은 코드 작성을 가능하게 해줍니다.
근데 왜 아직도 많은 프로젝트들이 테스트 코드를 작성 안 할까요?
테스트 구성을 하지 않는 변명들
- 실시간으로 코드가 바로 반영(e.g livereload, entr) 된다.
- curl 등의 도구를 활용해서 쉽게 구현을 테스트하고 있다.
- 테스트 작성하는데 시간이 너무 많이 든다.
- UI가 포함된 클라이언트는 테스트 작성이 어렵다.
- QA 프로세스 혹은 QA 팀이 있다.
- 해봤는데 의미 없더라.
마지막 이유가 설득하기 가장 어렵습니다. 자신의 경험이 정답이고, 아무리 남들이 좋다고 해도 내가 해봤는데 잘 안됐으면 그건 안 되는 것이라는 생각을 가지셨다면 여기까지만 읽으셔도 됩니다. 변화가 두려운 사람들은 어디에나 있기 마련이죠. 혹시 팀 혹은 회사의 중요한 의사 결정권자가 “해봤는데 별거 없더라” 군이라면, 심심한 위로의 말씀 드립니다. 팀에 Rise of the Expert Beginner 글을 투척하는 것을 추천 드립니다. 번역본도 있습니다.
변명 부수기
실시간 코드 반영
테스트 코드는 방금 작성한 코드를 당장 테스트 하기 위해서만 작성되는 것이 아닙니다. 잘 구성된 테스트 코드는 이후 다른 변경 사항으로 인해 발생 가능한 결함을 찾아내는 역할을 합니다.
수동 테스트 도구
방금 구성한 코드가 잘 동작하는지 테스트 하기 위해서 테스트 환경을 손으로 직접 만드는 것은 아무리 쉽게 한다고 해도 작성된 테스트 코드를 실행하는 것 보다 쉽지 않습니다.
테스트 작성 시간
프로그래밍을 막 배우기 시작하던 때를 생각해보세요. 버블 소트 하나 작성하는데 얼마나 오래 걸렸는지 기억 나시나요?
클라이언트 테스트
MVP, MVVM 등 클라이언트 쪽에서 많이 회자되는 아키텍쳐가 중요하게 생각하는 것 중 하나는 뷰와 로직의 분리이며, 이를 통해 얻고자 하는 것 중 절대 빠질 수 없는 것이 바로 테스트 가능성(Testability) 입니다. 네, UI 테스트가 쉽지는 않죠. 하지만 불가능하지도 않습니다. 정말 많이 양보해서 UI 테스트를 미룬다고 하더라도, 비지니스 로직의 테스트까지 포기하지 마세요. UI 없이 비지니스 로직만 테스트할 수 없는 상태라면 코드 구조를 다시 한번 고민해보세요.
QA 프로세스
코드를 새로 하나 작성할 때마다 1시간씩 쓸 수는 없습니다. QA 만으로 모든 테스트 목적을 커버할 수 없습니다.
해봤는데 의미 없더라
어라, 아직 여기 계신가요?
테스트 기본 원칙
테스트 구성에는 단위 테스트(Unit test), 통합 테스트(Integration test), 승인 테스트(Acceptance test) 등 다양한 테스트가 존재합니다. 각 테스트의 목적과 상황에 맞게 테스트를 구성하는 것도 중요하지만, 테스트의 원칙을 지키는 것이 우선되어야 합니다.
일곱 테스트 원칙 (Seven Testing Principles)
소프트웨어 테스팅 분야에서 40여년 간 제안되고 발전해 온 일곱 개의 기본 원칙 입니다. 이 글에서는 항목과 간단한 설명만 첨부합니다. 각 항목에 대한 자세한 내용은 Sevent Testing Principles 문서에서 확인할 수 있습니다.
- 테스팅은 결함의 존재를 보여주는 것이다.
- 완벽한 테스트는 불가능하다.
- 테스트 구성은 가능한 빠른 시기에 시작한다.
- 결함은 군집되어 있다.
- 살충제 역설(Pesticide Paradox) — 비슷한 테스트가 반복되면 새로운 결함을 발견할 수 없다.
- 테스팅은 정황에 의존적이다.
- 오류 부재의 오해 — 사용되지 않는 시스템이나 사용자의 기대에 부응하지 않는 기능의 결함을 찾고 수정하는 것은 의미가 없다.
F.I.R.S.T 단위 테스트 원칙
단위 테스트는 가장 작은 단위의 테스트이며, 모든 테스트의 시작점 입니다. 단위 테스트만 구성되어도 굉장히 많은 문제를 해결할 수 있으며, 코드 품질이 훨씬 좋아질 수 있습니다. F.I.R.S.T 원칙은 다음 각 항목의 앞 글자를 딴 원칙입니다.
- Fast — 유닛 테스트는 빨라야 한다.
- Isolated — 다른 테스트에 종속적인 테스트는 절대로 작성하지 않는다.
- Repeatable — 테스트는 실행할 때마다 같은 결과를 만들어야 한다.
- Self-validating — 테스트는 스스로 결과물이 옳은지 그른지 판단할 수 있어야 한다. 특정 상태를 수동으로 미리 만들어야 동작하는 테스트 등은 작성하지 않는다.
- Timely — 유닛 테스트는 프로덕션 코드가 테스트를 성공하기 직전에 구성되어야 한다. 테스트 주도 개발(TDD) 방법론에 적합한 원칙이지만 실제로 적용되지 않는 경우도 있다.
TL;DR
테스트는 안정적이고 빠른 — 네 맞습니다 빠른 — 개발을 위해 반드시 구성되어야 합니다. 테스트 기본 원칙에 맞춰 잘 구성된 테스트는 기능 개발 효율 및 안정성을 높여줄 뿐만 아니라, 코드 품질을 좋게 만드는데 큰 역할을 합니다.
언어와 프레임워크가 달라도 테스트의 기본 원칙은 동일합니다. 이후 타래 글에서는 몇 개의 언어를 기준으로 실제 테스트 구성 사례를 소개하겠습니다.