테스트를 작성하세요. 너무 많이는 말고요. 통합 테스트 위주로요.

원문: Kent C.Dodds, "Write tests. Not too many. Mostly integration."
이 블로그 게시물은 강연으로도 제작되었고 아래에서 시청할 수 있습니다.
얼마 전 Guillermo Rauch(Socket.io의 창시자이자 Zeit.co(최근 출시되는 멋진 제품들의 배후에 있는 회사)의 창립자)가 심오한 트윗을 남겼습니다.
"테스트를 작성하세요. 너무 많이는 말고요. 통합 테스트 위주로요."
짧지만 깊이 있는 내용이니 자세히 들여다보겠습니다.
테스트를 작성하세요
대부분의 프로젝트는 자동화된 테스트가 필요합니다. 당신의 시간이 소중하다면요. 로컬 환경에서 테스트를 통해 버그를 찾는 것이 새벽 2시에 전화를 받고 급히 수정하는 것보다 훨씬 낫습니다. 저는 테스트 작성에 시간을 투자할 때 많은 경우 시간을 절약할 수 있었습니다. 구축하고 있는 기능을 구현하는 데 시간이 더 걸릴 수도 있고 덜 걸릴 수도 있지만 확실히 기능을 유지 보수하는 시간을 절약할 수 있습니다.
테스트를 작성할 때 고려해야 할 점은 프로젝트에 버그가 없다는 신뢰를 테스트가 얼마나 가져다줄 수 있는지입니다. TypeScript나 ESLint 같은 정적 타이핑과 린팅 도구로도 상당한 신뢰를 얻을 수 있으니 이러한 도구를 사용하고 있지 않다면 한 번 살펴보는 것을 강력히 제안합니다. 하지만 강타입 언어를 사용하더라도 테스트는 필요합니다. 타이핑과 린팅은 비즈니스 로직에 버그가 없다는 것을 보장하지 않습니다. 따라서 좋은 테스트 묶음(test suite)으로 여전히 신뢰를 높일 수 있습니다.
너무 많이는 말고요
애플리케이션의 100% 코드 커버리지를 요구하는 관리자와 팀에 관하여 들었습니다. 이는 정말 좋지 않은 생각입니다. 문제는 커버리지가 70%를 훨씬 넘어서면 테스트로 얻는 이익이 감소한다는 점입니다 (제가 만들어 낸 숫자이며… 과학적인 증거는 없습니다). 왜 그럴까요? 항상 100%를 위해 노력하다 보면 실제로 테스트할 필요가 없는 부분을 테스트하느라 시간을 낭비하게 됩니다. 아무런 로직이 없는 것들이죠(따라서 모든 버그는 ESLint와 Flow에서 잡을 수 있습니다). 이런 식으로 테스트를 유지 보수하면 실제로 여러분과 팀의 업무 속도가 상당히 저하됩니다.
또한 단지 테스트 환경에서 재현하기 어려운 코드 한 줄을 확실하게 만들려고 구현 세부 사항을 테스트하는 경우도 있습니다. 구현 세부 사항을 테스트하는 것은 애플리케이션이 제대로 작동한다는 신뢰를 주지 못하고 리팩터링 속도가 저하되므로 이런 상황은 정말 피하고 싶을 것입니다. 코드를 리팩터링할 때 테스트를 변경하는 경우는 드물어야 합니다.
제 오픈 소스 프로젝트 중 거의 모든 프로젝트가 100% 코드 커버리지를 달성한다는 사실을 언급해야겠습니다. 제 오픈 소스 프로젝트 대부분이 다양한 상황 (코드 손상은 프로젝트를 사용하는 많은 곳에서 심각한 문제를 일으킬 수 있습니다)에서 재사용할 수 있는 소규모 라이브러리나 도구이며 어쨌든 이러한 소규모 프로젝트에서 100% 코드 커버리지 달성은 비교적 수월하기 때문입니다.
통합 테스트 위주로요
테스트에는 다양한 유형이 있습니다 (Fluent Conf에서 제가 5분간 진행한 강연인 "What we can learn about testing from the wheel"을 확인하세요). 테스트 유형마다 장단점은 존재합니다. 보통 자동화된 테스트를 이야기할 때 가장 일반적인 세 가지 형태는 단위 테스트, 통합 테스트, E2E 테스트입니다.
아래는 제가 프론트엔드 마스터즈 워크숍에 사용한 슬라이드입니다. - "Testing JavaScript Applications"

이 테스트 피라미드는 Martin Fowler의 블로그와 Google 테스트 블로그에서 가져와 조합한 것입니다.
사진에 표시된 것처럼 피라미드는 아래부터 위로 단위, 통합, E2E 테스트를 나타냅니다. 피라미드의 위로 올라갈수록 테스트 작성과 실행 속도가 느려지고 실행과 유지에 (시간 및 자원 측면에서) 더 큰 비용이 듭니다. 이러한 요인들은 단위 테스트에 더 많은 시간을 할애해야 한다는 것을 의미합니다.
하지만 사진에 나타나지 않은 한 가지는 피라미드의 위로 올라갈수록 각 형태의 테스트 신뢰 지수가 증가한다는 점입니다. 투자 대비 더 큰 효과를 얻을 수 있습니다. 그러므로 E2E 테스트가 단위 테스트보다 더 느리고 비용은 많이 들지만 애플리케이션이 의도대로 작동한다는 신뢰를 훨씬 가져다줍니다.
앞서 얘기했듯이 우리의 도구는 Martin의 테스트 피라미드 개념에서의 가정을 뛰어넘었습니다. 이것이 바로 제가 "테스트 트로피"를 만든 이유입니다. 🏆
아래는 통합 테스트의 중요성을 나타내는 재밌는 예시입니다.
프롭 e가 제공되지 않으면 실제로 컴포넌트 <B />가 망가지는 상황에서 컴포넌트 <A />가 컴포넌트 <B />를 프롭 c와 d로 렌더링하는지 여부는 중요하지 않습니다. 따라서 이러한 요소들이 개별적으로 작동하는지 확인하는 일부 단위 테스트가 나쁜 것은 아니지만 함께 잘 작동하는 것 또한 확인하지 않으면 아무 소용없습니다. 그리고 함께 제대로 작동하는지 테스트하면 따로 테스트할 필요가 없는 경우가 많다는 것을 알게 될 것입니다.
통합 테스트는 신뢰성과 속도·비용 사이의 균형을 잘 유지합니다. 그렇기 때문에 대부분 (전부가 아님을 명심하세요)의 노력을 통합 테스트에 투자하는 것이 바람직합니다.
이와 관련한 내용을 더 알고 싶다면 Testing Implementation Details를 읽어보세요. 테스트 유형의 차이를 더 알고 싶다면 Static vs Unit vs Integration vs E2E Testing for Frontend Apps를 읽어보세요.
통합 테스트를 더 많이 작성하는 방법
통합 테스트와 단위 테스트 사이의 경계는 다소 모호합니다. 그럼에도 통합 테스트를 더 많이 작성하기 위한 가장 좋은 방법은 모킹을 많이 하지 않는 것이라 생각합니다. 무언가를 모킹하면 테스트 대상과 모킹된 대상 사이의 통합에서 모든 신뢰가 사라집니다. 때론 어쩔 수 없이 모킹이 필요한 상황을 이해합니다(누군가는 동의하지 않지만요). 모든 테스트에서 실제로 이메일을 보내거나 신용카드를 청구하고 싶지는 않겠지만 대부분의 경우 모킹을 하지 않아도 되고 그러는 편이 더욱 좋을 것입니다.
리액트를 사용한다면 하지 말아야 할 사항에 shallow rendering도 포함됩니다. 이와 관련하여 더 알고 싶다면 Why I Never Use Shallow Rendering을 읽어보세요.
결론
소프트웨어 테스트가 시간 낭비라고 주장할 사람은 아무도 없다고 생각합니다. 가장 큰 과제는 무엇을 테스트할지 알고, 구현 세부 사항을 테스트하는 잘못된 신뢰가 아닌 진정한 신뢰를 주는 방식의 테스트 방법을 아는 것입니다.
이 글이 도움이 되었기를 바라며 애플리케이션을 전달하는 데 신뢰를 갖기 위한 여러분의 목표에 행운이 함께 하기를 기원합니다!




