개발을 하며 수도 없이 들어왔던 테스트 코드의 중요성,
과연 어떻게 시작해야 할까?
라는 의문점과 함께 시작한 프로덕션 환경에서의 테스트 커버리지 0%에서 98%로 만들기까지의 경험을 공유해보려고 합니다.
테스트? 그거 어떻게 작성하는건데?
테스트 코드를 작성해보았던 경험이 없었기에 맨 처음에는 도저히 어떻게 작성 해야하는지에 대해 감이 잡히지 않았습니다.
여러 자료들을 보며 공부하면서 처음 만들었던 테스트케이스는 특정한 API의 엔드포인트에 테스트 client를 통해 요청을 보내고 정상적인 응답(status 200)이 오는지에 대해서 검증하는 것이었습니다.
코드의 모든 부분에 대해서 작성을 하지는 않았고 새롭게 추가 기능을 개발할 경우에 테스트를 함께 작성하고는 했습니다. 하지만, 단순히 응답만을 assert 하는 것은 테스트 초보인 저에게도 무언가 굉장히 모자르다는 느낌을 지울수가 없었습니다.
본격적으로 테스트코드를 작성하다
시간이 남을때마다 테스트코드를 작성하는 것에 투자했던 시기입니다. 서비스내의 주요 API에 대해서는 모두 테스트케이스를 작성하는 것을 목표로 삼았습니다.
우선, 지난번의 테스트 케이스를 업그레이드 할 필요성을 느끼게 되었습니다. 응답만을 assert하는 코드에 그치지 않고 해당 API가 서비스의 비즈니스 로직을 정상적으로 수행하는지를 테스트 해보는 코드를 작성하였습니다.
- 비즈니스 로직을 테스트 하기 위한 Django 모델, json data들을 생성합니다.
(모델 생성에는 model-mommy를 이용하였습니다.) - 적절한 데이터 또는 invalid한 데이터와 함께 GET, POST, PATCH, DELETE등의 메소드와 함께 API를 호출합니다.
- 각 상황에 대해 정상적으로 처리가 되는지를 검증하거나 반환되는 데이터를 보고 정상적으로 수행되었는지에 대해 검증합니다.
처음에는 테스트 케이스를 1개 작성하는 것도 버거웠지만, 반복적으로 하다보니 점차 쉬워졌던 것 같습니다. 하지만, 특정한 상황을 고려해야 할 때 작성하는 테스트는 생각보다 어려웠습니다. 실제로 해당 로직이 돌아가는 플로우에 대한 것을 모두 알아야만 가능한 일이었기 때문입니다.
모킹? 외부 API와의 테스트는 어떻게 해결하지??
테스트는 외부 API의 결과에 의존성이 있으면 안됩니다. 외부 API와 상호작용하는 부분이 있다면 테스트는 느려질 것이고, 외부 API의 서버가 불안정하다면 테스트의 결과가 매번 바뀌게 될 것입니다.
이러한 부분은 모킹, 패칭을 통해 해결하였습니다. Mock이란 가짜 객체를 의미하는 것으로 외부 API에 대한 응답을 가짜로 만들고, 이 응답을 테스트의 결과로 활용하는 것입니다.
처참했던 첫 테스트 커버리지 측정
조금 오랜 시간이 지난후 대부분의 주요 API의 테스트가 모두 작성되었다고 판단하여 커버리지를 측정해보았습니다.
측정 후에는 적지 않은 충격을 받았는데, 여태 작성했던 것의 커버리지는 겨우 64%에 불과했습니다.
꽤나 오랜시간을 고민하며 만들었던 테스트인데 막상 커버리지를 측정해보니 결과는 처참했습니다. 커버리지를 낮추는 요인의 대부분은 분기처리에 따라 응답이 다른 경우들이었습니다. 즉, 고려하지 못한 케이스들이 많았던 것입니다.
하지만, 여태 테스트를 꾸준히 작성하면서 생겼던 노하우들로 인하여 생각보다 수월하게 특정 상황에 대한 테스트를 작성할 수 있었습니다. 보완을 거듭하면서 테스트 커버리지는 98%까지 올릴 수 있었습니다.
테스트 커버리지는 매번 측정하여 어느 부분을 놓치고 있는지 미리 파악하여 보완하는 것이 좋다고 생각합니다
너무 거대해진 테스트코드
테스트를 계속해서 작성하다보니 점점 느려져만 갔습니다. 처음에는 5초 이내로 수행되었던 테스트들이 10초, 20초 늘어가면서 약 40초의 수행시간을 가지게 되었습니다. 점점 늘어만가는 수행시간은 추후의 테스트 코드를 작성하는 것에 있어서 걸림돌이 될 것이라고 판단하였기에 이를 개선해보기로 하였습니다.
우선, 현재 작성되어있는 테스트는 서로 의존성을 가지고 있는 부분들이 다소 존재하였습니다.
예를들면, 테스트의 실행 순서에 따라 다른 결과가 나오는 상황이었습니다. 테스트끼리는 서로 의존성이 있어서는 안되기 때문에 따로 분리를 하여 의존성을 제거하였습니다.
또한, 테스트의 이름을 길고 명확하게 명시해주었습니다. (ex: test_update -> test_update_user_info)
분리 작업을 진행하면서 테스트의 속도 저하가 일어나는 부분에 대한 개선도 함께 진행하였습니다. 불필요하게 모델을 생성하거나, 과하고 의미없는 테스트 케이스를 정리해나갔습니다.
어느정도의 정리를 마치니 약 15초를 줄여 테스트의 속도를 20초대 까지로 끌어올릴 수 있었습니다.
여기까지 테스트 코드를 작성하면서 겪었던 경험들을 정리해보았습니다. 다음 포스팅에서는 테스트를 작성함으로써 실제로 얻었던 장점과 단점들에 대해서 공유해보려고 합니다.
읽어주셔서 감사합니다 :)
'SW개발 > 개발이야기' 카테고리의 다른 글
좋은 변수명을 짓는 것에 대하여 (feat. 읽기 좋은 코드가 좋은 코드다) (0) | 2022.02.22 |
---|---|
[테스트]테스트 커버리지 0%에서 98%까지의 경험기 2 (0) | 2021.12.06 |
[Django]검색 퍼포먼스를 향상하기까지의 과정 2 (2) | 2021.09.29 |
[Django]검색 퍼포먼스를 향상하기까지의 과정 (0) | 2021.09.27 |
[Django]Openstack Swift 401 Authentication failed 삽질기 (0) | 2021.07.29 |