목차
- 계기
- 단위 테스트에 적용하는 원칙
- 경험
계기
최근 ChatApplication 프로젝트를 3주간 진행하면서 초기에 생각한 것과 다르게 프로젝트를 변경해야하는 일이 굉장히 자주 발생했습니다. Redis가 사실은 서버 한개에 대해서만 연결하는 모듈이었다든가, Manager 모듈이 단순히 raw한 연결 자체를 넘겨주는 등의 형태로 구현돼있단 사실들을 리뷰하는 단계에서 발견한 일 등 다 기억은 안 나지만 수도 없이 많았습니다.
그래서 이런 코드들을 다 리팩토링하는 과정에서 처음에 했던 걱정은 ‘근데 이거 고치고 나서 동작하는지는 어떻게 확인하지?’였습니다. 다행히도 굉장히 운이 좋았던 것은 제가 그때까지 작성한 모든 코드들에 대한 unittest들이 있었기 때문에 이들에 대한 테스트를 통과시키는 것만으로도 굉장히 많은 것들을 확인할 수 있었습니다.
즉, 테스트가 생산성과 속도를 높여주는 도구라는 것을 깨닫는 경험을 했습니다.
그런데 이와 별개로 ci/cd를 위해 github actions를 작성하고 테스트하는 과정에서 제가 작성한 테스트들을 이 시스템에 통합하는 것이 불가능하단 사실들을 알았습니다. 그리고 현재 제가 작성하고 있는 테스트들이 충분히 좋지 못한 테스트라는 생각을 하게 됐습니다.
그렇다면 앞으로는 지금보다 더 좋은 테스트들을 작성하면 규모가 크고 오래 진행하는 프로젝트를 진행할 때 많은 도움을 받을 수 있겠다는 생각하게 됐습니다. 마침 요 근래 읽고 있는 책인 ‘구글 엔지니어는 이렇게 일한다’의 테스트 항목들에서 이에 대한 이야기들을 많이 하고 있고, 도움이 된다고 생각되어 이에 대한 정리 및 고민을 해보려고 합니다.
단위 테스트에 적용하는 원칙
- 유지보수하기 쉬워야 한다.
- 깨지기 쉬운 테스트가 되지 않아야 한다
- 버그가 없고 검증 대상 코드와 관련 없는 변경으로 인해 테스트가 실패하면 안된다.
- 공개 API를 이용해 테스트한다.
- 상호작용이 아니라 상태를 테스트하자.
- 상태: 기능 호출 이후에 시스템이 어떻게 변하는지
- 상호작용: 기능 호출 이후에 시스템이 어떤 행동을 하는지
- 명확한 테스트 작성하기
- 완전한 테스트를 작성하자.
- 결과에 도달하기까지의 논리를 읽는 이가 이해하는 데 필요한 모든 정보가 본문에 있다.
- 간결한 테스트를 작성하자.
- 관련 없는 정보를 제거하자.
- 메서드가 아니라 행위를 테스트하자.
- given, when, then을 사용하자.
- given: 시스템의 설정
- when: 시스템이 수행할 작업
- then: 결과 검증
- given, when, then을 사용하자.
- 테스트에 논리를 넣지 말자.
- 실패 메시지를 명확하게 작성하자.
- 완전한 테스트를 작성하자.
- DAMP: Descriptive And Meaningful Phrases
- 단순하고 명료하게 테스트를 작성하자.
- 테스트에서 다소의 중복은 괜찮다.
경험
위와 같은 원칙들을 적용하면서 테스트를 작성하기 시작하다보니, 이전보다 나은 테스트를 작성할 수 있게 된 장점은 있었습니다. 그렇지만 동시에 어려움을 겪은 부분도 있습니다.
제시한 원칙 중에 특히 공개 API를 이용해 테스트한다는 원칙 같은 경우 현재 CI/CD의 방법으로 git actions를 사용하는 중이고, 어플리케이션이 서버에 올라가있지 않은 상태여서 문제가 됐습니다. 로컬에서 서버를 띄우고 api에 요청을 보내는 것은 가능하지만, 이를 github actions에서 하는 것은 불가능했기 때문입니다.
현재는 이 부분에서 테스트를 mock_tests, open_api_tests 두 가지로 분리하고 기존의 작성한 테스트들을 open_api_tests로 옮긴 뒤 검증하지 않게 해 두었습니다. 그리고 mock_tests를 추가로 작성하려고 생각 중입니다.
오래 유지될 서비스의 측면에서 open_api_tests들을 결국 사용해야겠습니다만, 개발 비용 측면에서 이런 부분에 대한 고민이 들어가면 원칙은 항상 지키기 어렵다는 생각을 하게 됩니다.