여러분의 코드 중 테스트되지 않는 부분은 무엇인가요? 아마도 여러분이 통제할 수 없는 복잡한 상황을 다루는 코드일 겁니다. 스레드, 명령어 실행, git, 네트워킹, 혹은 UI 같은 것들 말이죠.
앱은 복잡할 때 가장 흥미롭습니다. 동시에 가장 위험하기도 하죠. 그렇기 때문에 테스트하기 어려운 코드야말로 꼼꼼하게 테스트가 필요한 코드입니다. 하지만 현실에서는 그렇게 되지 않는 경우가 많습니다.
대신 그런 코드를 건드릴 때마다 조심스럽게 살짝만 손을 댑니다. 수동 테스트를 몇 번 해보고, 풀 리퀘스트를 보낼 때는 테스트가 없다는 사실을 동료들이 눈치채지 못하길 바랄 뿐입니다.
하지만 이렇게 해서는 상황이 나아지지 않습니다. 다음번에도, 그다음에도 똑같은 문제와 버그, 스트레스를 마주하게 됩니다. 어떻게 하면 이런 까다로운 테스트들을 믿고 맡길 수 있는 대상으로 만들 수 있을까요?
마인드셋 전환하기
이런 테스트들이 가장 답답한 점은 무엇일까요? 작성하는 데 걸리는 시간이 생각했던 것의 열 배는 된다는 점입니다. 테스트가 절약해주는 시간과 테스트 작성에 드는 시간을 비교해보면 도저히 가성비가 맞지 않아 보입니다.
하지만 문제는 이 테스트 하나만이 아닙니다. 앞으로 작성할 모든 테스트에 관한 이야기입니다.
테스트가 잘 갖춰진 코드들 대부분에는 든든한 지원군이 있습니다. test/models 디렉터리의 코드만 있는 게 아니라, 훌륭하게 테스트된 코드에는 fake와 mock이 있고, 잘 정돈된 테스트 픽스처 세트가 있으며, 테스트 전용 설정 옵션까지 마련되어 있습니다.
이 모든 것을 작성하고 한데 모으는 데는 시간이 듭니다.
하지만 일단 갖추고 나면 정말 기분이 좋습니다. 테스트를 하나씩 쏟아낼 수 있고, 코드에 대한 확신과 함께 투자한 만큼 빠르게 나아갈 수 있다는 자신감이 생깁니다.
이전에 해둔 작업을 믿고 의지할 수 있게 되는 거죠.
따라서 이것은 단순히 복잡한 코드의 버그를 예방하는 것만이 아닙니다. 앞으로의 코드를 조금씩 더 테스트하기 쉽게 만드는 과정이기도 합니다.
우선은 통합 테스트로 작성하기
하지만 때로는 가치를 이해하는 문제가 아니라, 말 그대로 막히는 경우도 있습니다. 작고 빠른 유닛 테스트를 어떻게 작성해야 할지 도무지 감이 잡히지 않는 거죠.
실제로 git을 실행하지 않으면서 배포 도구가 올바른 git 명령어를 실행하는지 어떻게 확인할 수 있을까요? 원격 서버에 올바른 헤더를 보내고 있는지 어떻게 확신할 수 있을까요?
시간을 충분히 들이면 테스트가 의지할 수 있는 품질 좋은 fake를 만들 수 있습니다.
하지만 그것조차 부담스럽게 느껴질 때 시도해볼 수 있는 다른 방법이 있습니다. 테스트를 "코드 테스트하기"와 "mock 작성하기"라는 두 단계로 분리하는 것입니다.
그냥 실제 서버를 호출하세요. 실제 명령어를 실행하세요. 왜냐고요?
- 시작하기가 훨씬 쉽습니다. 어차피 그 명령어들을 수동으로 테스트하고 있지 않나요? 콘솔에서 실행하거나 브라우저에서 시도해보고 있겠죠? 그걸 그대로 테스트에 옮겨 적으면 됩니다.
- 나중에 mock이나 fake를 작성할 때 이 테스트들을 활용해 mock이 제대로 작동하는지 검증할 수 있습니다. 실제 환경에서 보이는 동작과 fake에서 보이는 동작이 같다면, fake가 제대로 만들어졌다고 볼 수 있습니다!
다만 이런 테스트들을 영원히 유지하고 싶지는 않을 겁니다:
- 통합 테스트가 가진 모든 문제점을 그대로 안고 있습니다. 느릴 수 있고, 인터넷 연결이 필요할 수 있으며, 앱이 실제로 신경 쓰지 않는 동작에 의존하기 때문에 깨지기 쉬울 수 있습니다.
- 실제 환경에서는 일부 사항을 테스트할 수 없을 수 있습니다. 예를 들어 상대편 서버를 통제할 수 없다면 특정 에러 코드를 강제로 발생시키려면 어떻게 해야 할까요?
- 의존하는 서버에게 차단당하면 테스트(그리고 앱!)가 깨질 수 있습니다. 실제로 겪었던 일인데, 꽤 큰 문제였습니다.
따라서 실제 환경 기반의 통합 테스트로 작성하는 것은 영구적인 해결책도, 장기적인 해결책도 아닙니다. 하지만 이런 단점에도 불구하고 여전히 도움이 됩니다. 그리고 나중에 교체한 후에도 통합 테스트를 별도의 스위트에 남겨둘 수 있습니다. 그러면 언제든 내 코드를 가정이 아닌 현실과 비교해볼 수 있습니다.
첫 번째 테스트가 만드는 변화
어떤 코드는 그저 테스트하기 어렵습니다. 신뢰할 수 있는 테스트를 빠르게 작성하는 데 필요한 인프라를 갖추는 데 시간이 걸리고, 많은 경우 그럴 가치가 없어 보이기도 합니다.
하지만 그 하나의 테스트만 생각하지 않고, 앞으로의 모든 테스트를 쉽게 만드는 가치에 초점을 맞추면 복잡한 코드 테스트가 훨씬 동기부여가 됩니다. 그리고 첫 번째 테스트가 완성되고 나면, 나머지 테스트들은 마법처럼 훨씬 쉽게 작성됩니다.
하지만 그것만으로 충분하지 않을 때도 있습니다. 실제 환경에서 코드가 작동하는지 확인하는 방법은 알지만, 정작 테스트로 만드는 방법을 모르겠다면 어떻게 할까요?
그럴 때는 테스트를 순수하고 고립된 상태로 유지해야 하는 대상으로 보지 마세요. 대신 이미 수동으로 하고 있는 작업을 자동화하는 수단으로 바라보세요.
완벽하지 않고, 가능한 한 빨리 교체해야 합니다. 하지만 그런 테스트들이 복잡한 코드를 빠르게 작성하고 변경하는 데 필요한 자신감을 줄 수 있습니다.