CI/CD의 자동화된 테스팅

CI/CD(지속적 통합 및 배포)의 목표는 개발 팀이 작동하는 소프트웨어를 사용자에게 빈번히 전달할 수 있도록 하여, 가치를 제공하고 제품이 실제로 어떻게 사용되는지 유용한 피드백을 얻는 데 있습니다. 많은 조직에서 경쟁력을 갖추기 위한 방법으로 DevOps 관행을 채택했습니다.

하지만 더 빨리 전달해야 한다는 비즈니스적 압박이 생산되는 제품의 품질을 저하해서는 안 됩니다. 결국 사용자는 혁신적인 기능의 출시를 요구할 때조차도 안정적으로 작동하는 소프트웨어를 기대합니다. 그렇기에 지속적 통합 및 전달 관행에 있어 최신 빌드에 대한 확신을 제공하는 안정적이고 철저한 자동화 테스트 프로세스가 핵심적입니다.

CI/CD 테스트를 자동화해야 하는 이유는 무엇인가요?

소프트웨어 테스트는 품질을 보장하는 데 필수적이며 오랫동안 소프트웨어 개발 방식의 일부를 형성해 왔습니다.

워터폴 컨텍스트에서 수동 테스트 또는 QA 테스트 단계는 코드 개발 및 통합 이후의 절차였으며, 그 목적은 사양에 따른 애플리케이션 작동 여부를 확인하는 것이었습니다.

이러한 선형적 접근 방식을 취할 경우 릴리스 프로세스가 지연되며 개발자는 구축한 결과물을 토대로 더 많은 개발이 이루어질 때까지 해당 결과물이 적절히 작동하는지 확인할 수 없습니다. 반면 CI/CD 자동화 프로세스를 활용하면 짧은 반복 주기의 애자일 접근 방식 통해 신속한 피드백을 제공하고 업데이트를 조금씩, 자주 릴리스할 수 있습니다. 이처럼 짧고 반복적인 주기의 핵심은 새로운 코드가 적절히 작동하는지, 다른 코드의 손상은 없는지 자동으로 검증하는 테스트입니다.

지속적 통합 과정에는 마스터 또는 트렁크로의 정기적인 코드 변경 사항 커밋, 적용 가능한 경우 빌드 트리거 및 매번 소프트웨어 보장 등이 포함됩니다. CI의 이점을 활용하려면 팀원들은 최소한 매일 이루어지는 변경 사항의 커밋을 목표로 삼아야 합니다. 하지만 아무리 소규모 팀이라 해도 이 정도 수준의 자동화 테스트를 수동으로 수행할 경우 QA 엔지니어에게 상당한 부담이 가해지며 매우 반복적인 작업이 수반될 것입니다. 그렇기에 자동화된 테스트가 필요합니다.

자동화 테스트는 반복 작업에 적합하며 수동 프로세스보다 더욱 일관성 있는 결과를 제공합니다. 그 이유는 사람이 동일한 단계를 반복적으로 수행하라는 요청을 받을 경우 세부 정보를 누락하거나 일관성 없이 검사를 수행할 위험이 필연적으로 초래되기 때문입니다.

자동화된 테스트는 동일 테스트를 수동으로 실행하는 것보다 빠를 뿐 아니라 동시 실행이 가능하므로 시간이 중요한 경우 (인프라만 허용한다면) 품질 보증을 확장할 수 있습니다. 자동화된 테스트 작성을 위해 초기 시간 투자가 필요하지만 팀원들이 변경 사항을 정기적으로 커밋하고 프로덕션에 훨씬 더 자주 릴리스하면 결과적으로 시간이 크게 단축됩니다.

자동화된 테스트를 통해 지루하고 반복적인 작업이 많이 제거되지만 QA 엔지니어링 팀의 일이 사라지는 것은 아닙니다. QA 팀은 관련 사례 정의 및 우선순위 지정 작업 외에도 종종 개발자와 협력하여 자동화된 테스트를 작성하는 데 관여합니다. 추후 다뤄질 내용이지만 자동화가 불가한 테스트에도 엔지니어가 필요합니다.

테스트는 CI/CD 프로세스의 어디쯤에서 수행할까요?

테스트는 파이프라인 전체의 여러 단계에서 수행된다는 것이 정답입니다.

지속적 통합 및 배포를 처음 접한다면 과도하게 느껴질 수 있지만 CI/CD 및 QA 자동화의 핵심은 팀에서 최대한 빨리 문제를 파악할 수 있게 지원하는 긴밀한 피드백 루프입니다.

문제가 발생한 직후 이슈를 해결하면 잘못된 기반 위에 더 많은 코드가 작성되지 않도록 방지하므로 수정이 훨씬 수월합니다. 또한 팀에서 다음 작업으로 이동하여 컨텍스트를 놓치기 전에 변경 사항을 적용하는 것이 더욱 효율적입니다.

많은 자동화된 빌드 테스트 도구가 CI/CD 도구와 통합을 지원하므로 테스트 데이터를 파이프라인에 공급하고 테스트를 단계적으로 수행한 후 각 단계의 결과를 받아볼 수 있습니다. 사용하는 CI 도구에 따라 이전 단계의 테스트 결과를 기반으로 다음 단계로의 빌드 진행 여부를 선택할 수도 있습니다.

자동화된 테스트를 통해 파이프라인을 최대한 활용하려면 일반적으로 가장 빠른 테스트가 먼저 실행되도록 빌드 테스트를 지시하는 것이 좋습니다. 이렇게 함으로써 긴 시간이 소요되며 더 많은 참여가 필요한 테스트를 실행하기 전에 초기 테스트 통과 여부를 확인할 수 있으므로, 피드백을 더 신속히 제공하고 테스트 환경을 보다 효율적으로 사용할 수 있습니다.

자동화된 테스트 생성 및 실행에 있어 우선순위를 지정하는 방식을 고려할 때 테스트 피라미드 측면에서 생각하면 도움이 됩니다.

테스트 피라미드 구축

테스트 피라미드를 활용하면 CI/CD 파이프라인에서 지속적 통합 테스트의 상대적 개수 및 수행 순서에 따라 우선순위를 지정하는 방법을 편리하게 개념화할 수 있습니다.

Mike Cohn이 처음으로 정의한 테스트 피라미드의 하단에는 유닛 테스트, 중간에는 서비스 테스트, 상단에는 UI 테스트가 표시됩니다.

이름은 다소 부정확할 수 있지만 그 전제는 합리적입니다. 빠르고 쉽게 실행 가능한 자동화된 유닛 테스트로 강력한 기반을 다지고, 작성하기 더욱 복잡하며 실행 시간도 더 오래 소요되는 테스트로 나아간 후, 마지막에 소수의 가장 복잡한 테스트로 완료하는 것입니다. 그렇다면 어떤 유형의 CI/CD 테스트를 고려해야 할까요? 옵션을 한번 살펴보겠습니다.

유닛 테스트

유닛 테스트는 테스트 피라미드의 기반을 적절히 형성합니다. 유닛 테스트는 가능한 최소 크기의 동작 단위를 처리하여 코드가 예상대로 작동하도록 설계됩니다. 팀에서 유닛 테스트를 작성하기로 결정했다면 일반적으로 개발자가 코드 작성과 마찬가지로 유닛 테스트 작성을 담당합니다. 이는 테스트 주도 개발(TDD: Test Driven Development)을 실천할 때 자동으로 수반되는 과정이지만 TDD가 유닛 테스트 작성의 필수 사항은 아닙니다.

기존 시스템에서 작업 중이고 유닛 테스트에 투자한 적이 없다면 처음부터 전체 코드베이스를 위한 유닛 테스트를 작성하는 일이 거의 불가능하게 느껴질 수 있습니다. 유닛 테스트에 광범위한 커버리지를 확보하는 편이 권장되지만 일단 보유한 부분부터 시작하여 점차 추가해나갈 수 있습니다. 현실적인 전략은 작성하는 코드마다 유닛 테스트를 추가하여, 새로 작성된 모든 코드를 커버하고 개발 중 상호작용하는 코드를 기반으로 기존 코드의 우선순위를 지정하는 것입니다.

통합 테스트

통합 테스트를 사용하면 소프트웨어의 여러 부분이 적절히 상호작용하는지 확인할 수 있습니다(예: 일부 애플리케이션 코드와 데이터베이스 간의 상호작용). 통합 테스트를 범위에 따라 세분화하면 유용할 수 있습니다. 범위가 좁은 통합 테스트의 경우 실제 모듈이 아닌 테스트 더블을 사용하여 다른 모듈과 상호작용을 테스트하는 반면, 범위가 넓은 통합 테스트는 실제 구성 요소 또는 서비스를 사용합니다.

프로젝트의 복잡성 및 관련된 내부 및 외부 서비스 개수에 따라 광범위한 통합 테스트보다 빠른 실행이 가능한 좁은 범위의 통합 테스트 계층을 작성하는 것이 적합할 수 있습니다(시스템의 다른 부분은 필요하지 않기 때문입니다). 이후 시스템 우선순위가 더 높은 영역을 대상으로 할 가능성이 있는 광범위한 통합 테스트를 수행합니다.

엔드투엔드 테스트

풀 스택 테스트라고도 부르는 엔드투엔드 테스트는 전체 애플리케이션을 조사합니다. GUI를 통해 엔드투엔드 테스트를 실행할 수 있지만 반드시 그럴 필요는 없습니다. API 호출로 시스템의 여러 부분을 실행할 수도 있습니다(통합 테스트를 활용한 API 테스트도 가능합니다). 테스팅 피라미드 방식에서 엔드투엔드 테스트는 실행 시간이 오래 소요될 뿐 아니라 취약하기 때문에 개수를 적게 유지할 것이 권장됩니다.

사용자 인터페이스 변경 시 테스트가 손상되어 빌드 테스트 결과 및 이를 업데이트하는 데 필요한 시간에 도움이 되지 않는 노이즈를 초래할 수 있습니다. 최상의 가치를 얻으려면 엔드투엔드 테스트를 신중하게 설계하고 하위 수준 빌드 테스트에서 이미 처리된 부분을 확인해야 합니다.

성능 테스트

테스팅 피라미드에서 성능 테스트에 대한 언급은 없지만 특히 안정성과 속도가 핵심 요구 사항인 제품의 경우 자동화된 테스트 도구에 성능 테스트 포함을 고려해보는 것이 좋습니다.

성능 테스트에는 실제 환경에서 소프트웨어의 작동 방식을 확인하도록 설계된 다양한 테스트 전략이 포함됩니다. 부하 테스트는 수요가 증가하는 경우 시스템의 작동 방식을 확인하며, 스트레스 테스트는 의도적으로 예상 사용량을 초과하고, 지속성 테스트인 소크(Soak) 테스트 지속적으로 높은 부하에서 성능을 측정합니다.

이러한 유형의 테스트를 수행하는 목표는 소프트웨어가 정의된 매개변수를 처리하는지 확인하는 것만이 아니며, 해당 매개변수를 초과할 경우 소프트웨어의 작동 방식을 테스트하는 데 있습니다. 물론 크래시 발생보다는 우아한 실패가 낫습니다.

테스트 환경

성능 테스트 및 엔드투엔드 테스트 모두 프로덕션과 매우 유사한 환경을 필요로 하며 빌드 테스트 데이터가 요구될 수도 있습니다. 자동화된 테스트 방식으로 테스트를 거친 소프트웨어에 대한 확신을 제공하기 위해서는, 매번 동일한 방식에 따라 빌드 테스트를 실행해야 합니다. 따라서 테스트 사이의 테스트 환경도 일관적으로 유지되어야 합니다(단, 변경 사항이 적용된 경우 프로덕션에 맞는 업데이트 필요).

환경을 수동으로 관리하는 데 시간이 많이 소요될 수 있으므로, 새로운 빌드마다 사전 프로덕션 환경의 생성 및 해체 단계를 자동화하는 것을 고려해볼 만합니다.

피드백을 활용한 개발

CI/CD 관행의 일부로 자동화된 테스트를 수행하는 목적은 방금 변경된 사항에 피드백을 신속히 받는 것입니다. 그렇기에 해당 피드백을 경청하고 그에 반응하는 것은 필수적입니다.

일반적으로 CI 서버는 자동화된 테스트 도구와 통합되므로 모든 결과를 한 곳에 표시할 수 있습니다. 개발 팀은 최신 결과를 표시하는 대시보드 또는 라디에이터 디스플레이를 Slack과 같은 커뮤니케이션 플랫폼의 자동 알림과 결합하여 최신 빌드 진행 상황을 전달하기도 합니다.

테스트 실패 시 코드베이스에서 어떤 영역이 테스트와 관련되었는지 이해하고 테스트에서 생성된 정보를(예: 스택 추적, 출력 값 또는 스크린샷) 확인하면 근본적 원인을 규명하는 프로세스 속도를 개선할 수 있습니다. 시간이 걸리더라도 테스트를 신중하게 설계하는 것이 좋습니다. 그렇게 해야 각각의 검사로 한 가지 사항을 확인하고 실패한 부분의 이해를 위해 상세한 라벨을 지정할 수 있습니다. 테스트 실패에 대한 추가 정보를 제공하는 지속적 통합 테스트 및 CI 도구도 빌드가 그린 상태로 조속히 돌아가는 데 유용합니다.

항상 그렇듯 도구와 방식은 일부 요인에 불과합니다. 좋은 CI/CD 자동화 방식이 자리 잡으려면 자동화된 CI/CD 테스트의 가치뿐 아니라 실패한 테스트에 신속하게 대응하여 소프트웨어를 배포 가능한 상태로 유지하는 것의 중요성을 인식하는 팀 문화가 필요합니다.

CI/CD 및 자동화 테스트로 수동 테스트는 더 이상 없는 것인가요?

CI/CD를 처음 접하는 사람들이 흔히 하는 오해는 자동화된 테스트가 수동 테스트 및 전문 QA 엔지니어의 필요성을 제거한다는 것입니다.

CI/CD 자동화는 QA 팀원에게 여유 시간을 제공하지만 테스터의 일이 사라지는 것은 아닙니다. 반복 작업에 시간을 소비하는 대신 엔지니어는 관련 케이스를 정의하고, 자동화된 테스트를 작성하고, 탐색 테스트를 통해 창의성과 독창성을 발휘하는 데 집중할 수 있습니다.

컴퓨터로 실행하도록 신중하게 스크립팅된 자동 빌드 테스트와 달리 탐색 테스트의 검토는 다소 여유롭게 진행됩니다. 탐색 테스트의 가치는 계획적이고 구조화된 테스트에서 누락된 부분을 발견하는 데 있습니다. 기본적으로 아직 고려하거나 테스트 사례를 작성하지 않은 문제를 찾아야 합니다. 탐색할 영역을 결정할 때 새로운 기능과 프로덕션에서 문제가 발생할 경우 가장 큰 피해를 초래할 시스템 영역을 모두 고려하세요.

탐색 테스트가 점차 수동적이고 반복적인 테스트로 변질되어선 안 됩니다. 탐색 테스트의 취지는 매번 동일한 테스트 세트를 수행하지 않는 것입니다. 탐색 테스트 중 문제를 발견해 이를 수정하는 경우 시간을 할애해 자동화된 테스트를 작성합니다. 이런 방식을 취하면 해당 문제가 다시 발생할 경우 포착되어 프로세스에서 훨씬 조속히 처리됩니다. 테스터의 시간을 효율적으로 사용하려면 자동화된 테스트를 모두 통과한 후에만 수동 테스트를 실시해야 합니다.

테스트 자동화를 위한 지속적 개선

자동화된 테스트는 모든 CI/CD 파이프라인에서 핵심 역할을 담당합니다.

자동화된 테스트를 작성하는 데 시간과 노력이 투입되지만, 신속한 피드백과 코드 배포 가능성에 대한 가시성이라는 이점이 있기에 결과적으로 자동화된 테스트는 시간을 훨씬 단축시키는 것입니다. 하지만 테스트 도구의 구축은 일회성으로 처리하고 잊어버리는 일이 아닙니다.

자동화된 빌드 테스트는 코드의 다른 부분 만큼 애플리케이션에서 많은 부분을 구성할 것이므로 관련성 있고 유용한 테스트를 유지하기 위한 관리가 필요합니다. 따라서 코드에 적용하는 지속적 개선은 테스트에도 적용됩니다.

지속적으로 새로운 기능에 대한 자동화된 테스트 커버리지를 구축하고 탐색 테스트 결과를 제공함으로써 테스트 도구를 효과적이고 효율적으로 유지할 수 있습니다. 또한 테스트 성과를 살펴보고, 더욱 신속한 피드백을 위해 작업 단계를 재정렬하거나 세분화할 가치가 있는지 확인하는 데 시간을 할애하는 것이 좋습니다.

CI 도구는 파이프라인 최적화에 도움이 되는 다양한 측정 기준을 제공하는 데 반해, 불안정한 테스트 지표는 잘못된 확신이나 우려를 줄 수 있는 신뢰도 낮은 테스트를 표시합니다. 측정 기준이 자동화된 테스트 프로세스를 개선하는 데 유용할 수 있지만 테스트 커버리지 자체를 목표로 생각하게 되는 사고의 함정에 주의해야 합니다. 실제 목표는 정기적으로 작동하는 소프트웨어를 사용자에게 제공하는 것입니다. 자동화는 빠르고 신뢰할 수 있는 피드백을 제공하여 이러한 목표를 달성하므로, 소프트웨어를 프로덕션에 배포할 때 확신을 가질 수 있습니다.