자동화된 테스트는 지속적 통합 및 배포의 핵심 요소 중 하나입니다. 연속 테스트를 DevOps 파이프라인에 구축하면 소프트웨어의 품질을 크게 개선할 수 있습니다.
지속적 통합 및 배포(CI/CD)의 주요 목적은 제품 및 서비스에 관한 피드백을 정기적으로 받기 위해 점진적인 변경 사항을 자주 전달하는 것입니다. 그러나 빠르게 자주 전달한다고 하더라도 제품의 품질이 떨어져서는 안 됩니다. 결국 사용자는 혁신적인 기능의 출시를 요구하면서도 소프트웨어가 안정적으로 작동하기를 기대하기 때문입니다.
따라서 최신 빌드에 대한 신뢰감을 줄 수 있는 안정적이고 엄격한 자동화된 테스트 프로세스가 CI/CD 실행에 필수적입니다.
테스트는 소프트웨어의 품질을 보장하는 데 필수적이며 소프트웨어 개발에서 중요한 역할을 해 왔습니다.
폭포수 컨텍스트에서 수동 테스트 혹은 QA 단계는 코드가 배포되고 통합된 후에 진행됩니다.
💡참고 사항: 흔히 폭포수 모델이라고도 하는 폭포수 전략은 선형적이며 순차적인 소프트웨어 개발 및 프로젝트 관리 방법입니다. 소프트웨어 개발에 사용된 초기 모델 중 하나이며 여러 단계로 구성된 구조화되고 체계적인 흐름이 특징입니다.
이 방식의 단점은 코드를 작성하고 오랜 시간이 흐르기 전까지는 코드가 의도대로 동작하는지 확인할 수 없다는 점입니다. 그때쯤이면 이미 많은 코드가 추가로 빌드되어 문제를 고치기가 더욱 어려워지고, 이 때문에 새로운 기능의 전달과 버그 수정이 늦어집니다.
반면에 애자일 방식에서는 짧고 반복적인 개발 주기를 취하여, 릴리스를 자주하고 사용자들로부터 피드백을 받아 이 정보를 기반으로 다음에 무엇을 빌드할지 결정할 수 있습니다. CI/CD는 코드 작성과 릴리스 사이의 단계를 자동화하여 이러한 작업 방식을 지원하여 전체 프로세스의 신뢰성과 효율성을 재고합니다.
이러한 DevOps 방법론을 성공적으로 활용하려면, 정기적으로(하루에도 여러 번이 적절) 코드를 커밋, 빌드 및 테스트해야 합니다. 그러나 작은 팀이라 하더라도 수동 테스트 일체를 하루에 한 번 이상 수행하는 것은 현실적이지 않습니다. 모든 CI/CD 파이프라인에서 자동화된 테스트가 중요한 이유가 바로 이 때문입니다.
자동화된 테스트를 작성하는 데에는 처음에 시간이 많이 필요하긴 하지만, 머지않아 투자된 시간 이상의 효과를 내며 이는 변경 사항을 많이 커밋하고 배포할수록 커집니다. 자동화된 테스트에 투자했을 때의 주요 장점은 다음과 같습니다.
테스트 자동화를 통해 지루하고 반복적인 작업이 많이 제거되지만 QA 엔지니어링 팀의 일이 사라지는 것은 아닙니다. QA 팀은 관련 사례 정의 및 우선 순위 지정 작업 외에도 종종 개발자와 협력하여 자동화된 테스트를 작성하는 데 관여합니다. 추후 다뤄질 내용이지만 테스트에서 자동화가 불가한 부분에도 QA 엔지니어가 필요합니다.
자동화된 테스트는 파이프라인 전체에 걸쳐 여러 단계에서 수행됩니다.
CI/CD와 QA 자동화에서는 팀이 문제를 최대한 빨리 찾을 수 있도록 도와주는 짧은 피드백 루프가 핵심입니다.
문제가 발생하자마자 이를 해결하면 나쁜 토대에 추가로 코드가 작성되는 것을 막을 수 있으므로 작업이 더 간편해집니다. 또한, 다음 작업으로 넘어가 다 잊어버리기 전에 변경하는 것이 팀에도 효율적입니다.
많은 자동화된 빌드 테스트 도구가 CI/CD 도구와 통합을 지원하므로 테스트 데이터를 파이프라인에 공급하고 테스트를 단계적으로 수행한 후 각 단계의 결과를 받아볼 수 있습니다. 사용하는 CI 도구에 따라 이전 단계의 테스트 결과를 기반으로 다음 단계로의 빌드 진행 여부를 선택할 수도 있습니다.
자동화된 테스트를 통해 파이프라인을 최대한 활용하려면 일반적으로 가장 빠른 테스트가 먼저 실행되도록 빌드 테스트를 지시하는 것이 좋습니다. 이렇게 함으로써 긴 시간이 소요되며 더 많은 참여가 필요한 테스트를 실행하기 전에 초기 테스트 통과 여부를 확인할 수 있으므로, 피드백을 더 신속히 제공하고 테스트 환경을 보다 효율적으로 사용할 수 있습니다.
자동화된 테스트 생성 및 실행에 있어 우선순위를 지정하는 방식을 고려할 때 테스트 피라미드 측면에서 생각하면 도움이 됩니다.
테스트 피라미드는 CI/CD 파이프라인에서 자동화된 테스트의 상대적 개수 및 수행 순서에 따라 우선 순위를 지정하는 방법입니다.
Mike Cohn이 처음으로 정의한 테스트 피라미드는 하단에는 유닛 테스트, 중간에는 서비스 테스트, 상단에는 UI 테스트가 표시됩니다.
테스트 피라미드는 다음의 단계로 구성됩니다.
그렇다면 어떤 유형의 CI/CD 테스트를 고려해야 할까요? 옵션을 한번 살펴보겠습니다.
유닛 테스트는 테스트 피라미드의 기반을 적절히 형성합니다. 유닛 테스트는 가능한 최소 크기의 동작 단위를 처리하여 코드가 예상대로 작동하도록 설계됩니다. 예를 들어 날씨 앱을 만드는 경우 섭씨에서 화씨로 값으로 변환하는 것이 기능의 일부일 수 있습니다. 유닛 테스트를 사용하여 온도 변환 함수가 값의 범위 내에서 예상된 결과를 반환하는지 확인할 수 있습니다. 관련 코드를 변경할 때마다 이러한 유닛 테스트를 사용하면 매번 앱을 빌드하고 실행하지 않고도 이러한 특정 부분이 의도대로 동작하는지 확인할 수 있습니다.
팀에서 유닛 테스트를 작성하기로 결정했다면 일반적으로 개발자가 관련 코드 작성과 마찬가지로 유닛 테스트 추가를 담당합니다. 이 프로세스는 테스트 주도 개발(TDD)에서 매우 강조되기는 하지만(아래에 후술) 유닛 테스트를 작성할 때 TTD가 필수적인 것은 아닙니다.
또 다른 방법은 각 개발 작업의 완료 상태를 정의하는 유닛 테스트를 만들고, 코드 검토를 수행할 때 또는 코드 커버리지 보고서를 사용하여 이러한 테스트가 실행되는지 확인하는 것입니다.
기존 시스템에서 작업 중이고 유닛 테스트에 투자한 적이 없다면 처음부터 전체 코드 베이스를 위한 유닛 테스트를 작성하는 일이 거의 불가능한 것처럼 느껴질 수 있습니다. 유닛 테스트에 광범위한 커버리지를 적용하는 편이 좋지만 일단 가능한 부분부터 시작하여 점차 추가해 나갈 수 있습니다.
코드에 충분한 유닛 테스트 커버리지가 없는 경우, 다루는 모든 코드에 유닛 테스트를 추가하여 팀과 함께 유닛 테스트 커버리지를 만드는 것을 고려해 보세요. 이러한 전략으로 모든 코드가 포함되고 자주 사용하는 코드를 바탕으로 기존의 코드에 우선 순위를 지정할 수 있습니다.
통합 테스트를 진행하여 애플리케이션 코드와 데이터베이스 혹은 API 호출과 같은 소프트웨어의 여러 부분이 상호작용할 때 예상대로 잘 동작하는지 확인할 수 있습니다.
통합 테스트를 범위에 따라 세분화하면 유용할 수 있습니다. 좁은 범위의 테스트 방식에서는 실제 모듈이 아니라 테스트 더블을 사용하여 다른 모듈과의 상호작용을 테스트합니다. 넓은 통합 테스트에서는 실제 구성 요소나 서비스를 사용합니다. 날씨 앱의 예시로 돌아가자면, 광범위한 통합 테스트에서는 API로 기상 예보 데이터를 가져올 수 있고, 좁은 테스트에서는 목업 데이터를 사용할 수 있습니다.
프로젝트의 복잡도나 관여된 내외부 서비스의 개수에 따라 좁은 범위의 통합 테스트로 시작하는 게 좋을 수 있습니다. 이러한 테스트는 시스템의 다른 부분이 가용하지 않아도 가능하기 때문에 광범위한 통합 테스트보다 더 빠르게 실행됩니다(피드백도 빠르게 제공됩니다).
좁은 범위의 테스트가 성공적으로 완료된 경우 광범위한 통합 테스트를 실행할 수 있습니다. 이러한 테스트는 실행에 많은 시간이 걸리고 유지하는 데 많은 공수가 들기 때문에 제품이나 서비스에서 우선 순위가 높은 영역으로 이러한 테스트를 제한하는 게 좋습니다.
풀 스택 테스트라고도 부르는 엔드투엔드 테스트는 전체 애플리케이션을 조사합니다. 이러한 테스트는 사용자가 계정을 생성할 수 있는지 혹은 거래를 완료할 수 있는지 여부와 같은 비즈니스 사용 사례를 검증하는 데 주로 사용됩니다.
GUI로 자동화된 엔드투엔드 테스트를 실행할 수 있지만 반드시 그럴 필요는 없습니다. API를 호출하여 시스템의 여러 부분을 실행할 수도 있습니다(통합 테스트를 활용한 API 검사도 가능합니다).
테스팅 피라미드 방식에서 엔드투엔드 테스트는 실행 시간이 오래 소요될 뿐 아니라 취약하기 때문에 개수를 적게 유지할 것이 권장됩니다. 사용자 인터페이스 변경 시 테스트가 손상되어 빌드 테스트 결과에 도움이 되지 않는 노이즈를 초래하고 이를 업데이트하는 데 추가적인 시간이 소요될 수 있습니다. 따라서 최상의 가치를 얻으려면 엔드투엔드 테스트를 신중하게 설계하고 하위 수준 테스트에서 이미 처리된 부분을 확인해야 합니다.
행동 주도 개발(BDD)은 개발자, 테스터 및 비즈니스 이해 관계자 간의 의사소통 문제를 해결하는 협력적인 소프트웨어 개발 방식입니다. 이 방식은 소프트웨어의 구현이 아니라 동작을 강조하여 TDD를 확장합니다.
BDD는 통합 테스트와 엔드투엔드 테스트 개발 모두에서 유용한 전략입니다. 주요 사항은 다음과 같습니다.
테스트 피라미드는 성능 테스트에 대해서는 언급하지 않지만, 특히 안정성과 속도가 핵심 요구 사항인 제품의 경우 고려해 보는 것이 좋습니다.
성능 테스트에는 실제 환경에서 소프트웨어의 작동 방식을 확인하도록 설계된 다양한 테스트 전략이 포함됩니다.
이러한 유형의 테스트를 수행하는 목표는 소프트웨어가 정의된 매개변수를 처리하는지 확인하는 것뿐만 아니라, 해당 매개변수를 초과할 경우 소프트웨어가 어떻게 작동하는지 테스트하는 데 있습니다. 이때 충돌이 발생하는 것보다는 깔끔하게 실패하는 것이 낫습니다.
성능 테스트 및 엔드투엔드 테스트 모두 프로덕션과 매우 유사한 환경이 필요하며 빌드 테스트 데이터가 요구될 수도 있습니다. 자동화된 테스트 방식의 신뢰성을 확보하려면 테스트가 매번 동일하게 실행되는 것이 중요합니다. 그러려면 실행할 때마다 테스트 환경이 일관적이어야 하며 프로덕션 환경에 변경 사항이 배포되면 테스트 환경도 일치하게 업데이트되어야 합니다.
이러한 환경을 수동으로 관리하려면 시간이 많이 소요됩니다. 프로덕션 전 단계의 환경을 새로운 빌드가 나올 때마다 생성하고 분해하는 과정을 자동화하면 시간도 절약되고 자동화 테스트 방식의 일관성과 신뢰성을 확보할 수 있습니다.
테스트 주도 개발(TDD)은 익스트림 프로그래밍(XP)에서 파생된 개발 방식입니다. TDD에서 첫 단계는 추가하고 싶은 기능에 필요한 테스트 사례의 목록을 작성하는 것입니다. 그런 다음 한 번에 하나의 테스트 사례를 진행하고, (실패할) 테스트를 작성하고, 테스트를 통과하도록 코드를 작성합니다. 마지막으로 다음 테스트 사례로 넘어가기 전에 요구 사항에 따라 코드를 리팩터링합니다. 이러한 프로세스는 '레드, 그린, 리팩터링' 혹은 '되게 하거나, 고치거나'로 요약될 수 있습니다.
TDD의 주요 장점 중 하나는 코드를 새로 작성할 때마다 강제로 자동화된 테스트를 추가하도록 만든다는 점입니다. 이는 테스트 커버리지가 항상 증가한다는 의미이며, 코드를 변경할 때마다 빠르고 정기적으로 피드백을 받을 수 있습니다. 테스트 주도 개발의 다른 장점에는 다음이 포함됩니다.
TDD는 자동화된 테스트 커버리지를 만들어 CI/CD 프로세스를 지원하는 효과적인 방법입니다. 그러나 TDD가 효율적인 DevOps 전략의 필수 사항은 아니며, TDD를 사용하지 않고도 높은 수준의 자동화된 테스트 커버리지를 유지할 수 있습니다.
CI/CD 방식의 일부로 자동화 테스트를 실행하는 목적은 방금 작성된 변경 사항에 관한 피드백을 빠르게 받는 것입니다. 피드백을 듣고 이에 응답하는 것은 이 프로세스에서 필수적입니다. 다음의 모범 사례를 참조하면 자동화 테스트 방식을 최대한 활용할 수 있습니다.
항상 그렇듯 도구와 방식은 일부 요인에 불과합니다. 좋은 CI/CD 자동화 방식이 자리 잡으려면 자동화된 CI/CD 테스트의 가치뿐 아니라 실패한 테스트에 신속하게 대응하여 소프트웨어를 배포 가능한 상태로 유지하는 것의 중요성을 인식하는 팀 문화가 필요합니다.
대부분의 팀에서 자동화된 테스트의 시작점은 수동으로 트리거할 수 있거나 간단한 지속적 통합 파이프라인의 일부로 트리거할 수 있는 유닛 테스트 모음입니다. DevOps 문화가 성숙하면, 통합 테스트, 엔드투엔드 테스트, 보안 테스트 및 성능 테스트 등을 추가하며 테스트 피라미드를 구축할 수 있습니다.
연속 테스트란 CI/CD 파이프라인의 일부로 자동화된 테스트를 광범위하게 수행하는 방법을 말합니다. 연속 테스트에서는 코드에 변경 사항이 있을 때마다 자동화된 테스트가 자동으로 실행되므로 버그가 최대한 빠르게 발견됩니다.
연속 테스트의 초기 단계에는 변경 사항이 커밋되기도 전에 IDE에서 실행되는 테스트가 포함될 수 있습니다. 연속 테스트에서는 일반적으로 파이프라인의 일부로 자동으로 새로고침되는 테스트 환경이 마지막 단계의 테스트에 필요합니다.
완전히 자동화된 연속 테스트 프로세스는 릴리스 속도를 높이면서 코드의 신뢰도를 극대화합니다. 소프트웨어에 엄격한 테스트 방식을 적용하면 버그가 발생할 위험을 크게 줄일 수 있습니다. 이러한 프로세스를 자동으로 지속 실행하면 업무 효율이 증대될 뿐만 아니라 긴급한 수정을 빠르고 확신을 갖고 배포할 수 있습니다.
연속 테스트는 구현하는 데 시간이 다소 걸리지만, CI/CD 파이프라인의 다른 부분들을 자동화하면서 테스트 커버리지를 올려가며 점진적으로 달성할 수 있는 목표입니다.
CI/CD를 처음 접하는 사람들이 흔히 하는 오해는 자동화된 테스트가 수동 테스트 및 전문 QA 엔지니어의 필요성을 없앤다는 것입니다.
CI/CD 자동화는 QA 팀원에게 여유 시간을 제공하지만 테스터의 일이 사라지는 것은 아닙니다. 반복 작업에 시간을 소비하는 대신 엔지니어는 테스트 사례를 정의하고, 자동화된 테스트를 작성하고, 탐색 테스트를 통해 창의성과 독창성을 발휘하는 데 집중할 수 있습니다.
컴퓨터로 실행하도록 신중하게 스크립팅된 자동 빌드 테스트와 달리 탐색 테스트의 검토는 다소 여유롭게 진행됩니다. 탐색 테스트의 가치는 계획적이고 구조화된 테스트 방식에서 누락될 수 있는 부분을 발견하는 데 있습니다. 기본적으로 아직 고려하거나 테스트 사례를 작성하지 않은 문제를 찾아야 합니다.
탐색할 영역을 결정할 때 새로운 기능과 프로덕션에서 문제가 발생할 경우 가장 큰 피해를 초래할 시스템 영역을 모두 고려하세요. 테스터의 시간을 효율적으로 사용하려면 자동화된 테스트를 모두 통과한 후에만 수동 테스트를 실시해야 합니다.
탐색적인 테스트가 수동적인 반복 테스트로 이어져서는 안됩니다. 똑같은 검사를 매번 수행하는 것이 목표가 아닙니다. 탐색적인 테스트에서 문제가 발견되면, 문제를 해결하고 하나 이상의 자동화 테스트도 작성해야 합니다. 이렇게 하면 문제가 재발하더라도 프로세스에서 더 일찍 찾을 수 있습니다.
테스트 도구의 구축은 한 번 작업하고 잊어버리는 일이 아닙니다. 자동화된 테스트는 적합성과 유용성을 유지하기 위해 관리되어야 합니다. 지속적으로 코드를 개선하듯이 테스트도 지속적으로 개선되어야 합니다.
지속적으로 새로운 기능에 대한 자동화된 테스트를 추가하고 탐색 테스트 결과를 제공함으로써 테스트 도구를 효과적이고 효율적으로 유지할 수 있습니다. 또한 테스트 성과를 살펴보고, 더욱 신속하게 피드백을 전달하기 위해 프로세스 단계를 재정렬하거나 세분화할 가치가 있는지 확인하는 데 시간을 할애하는 것이 좋습니다.
CI 도구는 파이프라인 최적화에 도움이 되는 다양한 메트릭을 제공할 수 있는 한편, 불안정한 테스트 지표는 신뢰도 낮은 테스트를 표시하여 잘못된 확신이나 우려를 낳을 수 있습니다.
메트릭은 자동화된 테스트 프로세스를 개선하는 데 유용할 수 있지만 테스트 커버리지 자체를 목표로 생각하게 되는 함정에 주의해야 합니다. 실제 목표는 정기적으로 작동하는 소프트웨어를 사용자에게 제공하는 것입니다. 자동화는 빠르고 신뢰할 수 있는 피드백을 제공하여 이러한 목표를 달성하므로, 소프트웨어를 프로덕션에 배포할 때 확신을 가질 수 있습니다.
테스트 자동화는 모든 CI/CD 파이프라인에서 핵심 역할을 담당합니다. 테스트를 자동으로 실행하면 코드가 변경될 때 신뢰할 수 있는 피드백을 빠르게 받을 수 있습니다. 버그를 빠르게 찾을수록 해결하기가 쉬워지기 때문에 결과적으로 개발의 효율성이 높아집니다.
자동화된 테스트의 소요 시간에 따라 우선 순위를 정하는 것이 좋습니다. 유닛 테스트는 피드백을 가장 빠르게 주기 때문에 먼저 실행되어야 하며, 그 다음으로 통합 테스트 및 엔드투엔드 테스트를 수행하세요. 자동화된 테스트가 전혀 없다면 유닛 테스트부터 시작하는 게 가장 좋습니다. 테스트 주도 개발(TDD)은 검증된 개발 방식으로 유닛 테스트 커버리지를 높이고 유지하는 데 도움이 됩니다.
DevOps 문화가 성숙해지면, 연속 테스트로 전환해도 좋습니다. 이러한 전환의 일부에는 테스트 환경 생성 및 관리 자동화가 포함됩니다. 높은 단계의 자동화된 테스트를 작성할 때는 위험이 가장 큰 영역을 우선하는 것이 좋습니다. 여기에는 로드, 스트레스 혹은 소크(Soak) 테스트와 같은 자동화된 성능 테스트가 필요할 수 있습니다. 수동 탐색 테스트는 테스트 커버리지에 틈이 있는지 확인하고 CI/CD 프로세스를 지속적으로 개선할 때 좋은 방법입니다.
TeamCity는 사용자가 CI/CD 프로세스를 최대한 활용할 수 있도록 테스트 프레임워크를 광범위하게 지원하고 다양한 테스트 자동화 기능을 제공합니다.
효율적인 테스트 자동화에는 속도와 신뢰도가 핵심이며, TeamCity는 두 가지 모두를 위해 최적화되었습니다. 문제의 근원을 빠르게 파악할 수 있도록 상세한 테스트 보고서를 제공할 뿐만 아니라 TeamCity는 유효한 실패만 플래그 처리되도록 하기 위해 불안정한 테스트를 자동으로 강조 표시합니다. 지능적인 테스트 재정렬과 병렬화로 결과를 더욱 빠르게 낼 수 있으며, 원격 실행 기능을 사용하면 커밋하기 전에 피드백을 받을 수 있습니다.
TeamCity는 이슈 트래커, IDE, Slack 및 다른 플랫폼과 통합되기 때문에 어디에서 작업하든 테스트가 실패하면 알림을 받을 수 있습니다. 마지막으로, 가상 머신과 Docker 컨테이너가 지원되므로 테스트 환경 관리를 자동화하고 CI/CD 프로세스에 연속 테스트를 구현할 수 있습니다.