테스트 관리 도구의 비교 분석: CI/CD 파이프라인과의 실제 통합
Source: Dev.to
Introduction
테스트가 별도의 단계였던 시절은 지났습니다. 오늘날 DevOps 환경에서는 테스트가 지속적으로 이루어지며, 테스트 관리 도구도 이에 맞춰야 합니다. 올바른 도구는 단순히 테스트 케이스를 정리하는 것을 넘어, 원활한 CI/CD 연동, 실시간 피드백, 실행 가능한 인사이트를 제공해야 합니다.
여러 조직에서 테스트 파이프라인을 구현해 본 경험을 바탕으로, 실제 운영 환경에서 효과적인 방법을 공유합니다.
1. TestRail – The Specialist
Best for: Dedicated QA teams needing detailed reporting
TestRail는 한 가지에 특화되어 있습니다: 테스트 관리. 이 도구는 이슈 트래커나 프로젝트 관리 도구가 되려는 것이 아니라, QA를 위해 설계되었습니다.
Real GitHub Actions Integration
# .github/workflows/testrail-ci.yml
name: CI with TestRail Integration
on:
pull_request:
branches: [main]
push:
branches:
- 'releases/**'
- 'hotfix/**'
jobs:
test-and-report:
runs-on: ubuntu-latest-8-cores
timeout-minutes: 30
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
strategy:
matrix:
test-type: [api, ui, security]
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Test Environment
run: |
# Database migrations
npm run db:migrate
# Seed test data
npm run db:seed -- --environment test
- name: Run ${{ matrix.test-type }} Tests
id: run-tests
env:
TESTRAIL_ENABLED: true
TESTRAIL_RUN_NAME: "${{ github.event_name }} - ${{ github.sha }}"
NODE_ENV: test
run: |
case ${{ matrix.test-type }} in
api)
npm run test:api -- --reporter mocha-testrail-reporter
;;
ui)
npm run test:e2e -- --reporter cypress-testrail-reporter
;;
security)
npm run test:security -- --reporter testrail
;;
esac
# Capture exit code
echo "exit_code=$?" >> $GITHUB_OUTPUT
- name: Upload to TestRail
if: always() && env.TESTRAIL_ENABLED == 'true'
uses: testrail-community/upload-results-action@v1
with:
testrail-url: ${{ secrets.TESTRAIL_URL }}
username: ${{ secrets.TESTRAIL_USER }}
api-key: ${{ secrets.TESTRAIL_API_KEY }}
project-id: ${{ secrets.TESTRAIL_PROJECT_ID }}
suite-id: ${{ secrets.TESTRAIL_SUITE_ID }}
run-name: ${{ env.TESTRAIL_RUN_NAME }}
results-path: 'test-results/*.xml'
- name: Quality Gate Check
if: steps.run-tests.outputs.exit_code != 0
run: |
echo "❌ Tests failed - Blocking deployment"
# Create TestRail defect automatically
curl -X POST "${{ secrets.TESTRAIL_URL }}/index.php?/api/v2/add_result/$TEST_ID" \
-H "Content-Type: application/json" \
-u "${{ secrets.TESTRAIL_USER }}:${{ secrets.TESTRAIL_API_KEY }}" \
-d '{
"status_id": 5,
"comment": "Build failed in CI: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"defects": "CI-${{ github.run_id }}"
}'
exit 1
Why this works
- 유형별 병렬 테스트 실행
- 통합 테스트를 위한 데이터베이스 서비스
- 나쁜 빌드를 차단하는 품질 게이트
- TestRail에서 자동으로 결함 생성
2. Zephyr Scale
Best for: Teams already living in Atlassian’s ecosystem
Jira가 두 번째 집처럼 느껴진다면, Zephyr Scale(구 Zephyr Squad)은 자연스러운 확장처럼 보입니다.
Jenkins Pipeline with Smart Test Selection
// Jenkinsfile - Smart Testing Pipeline
def testResults = []
def qualityMetrics = [:]
pipeline {
agent {
kubernetes {
label 'test-agent'
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: test-runner
image: node:18-alpine
command: ['cat']
tty: true
resources:
requests:
memory: "2Gi"
cpu: "1000m"
'''
}
}
parameters {
choice(name: 'TEST_SCOPE',
choices: ['SMOKE', 'REGRESSION', 'FULL'],
description: 'Test scope to execute')
booleanParam(name: 'UPDATE_ZEPHYR',
defaultValue: true,
description: 'Update Zephyr with results')
}
environment {
ZEPHYR_BASE_URL = 'https://api.zephyrscale.smartbear.com/v2'
JIRA_PROJECT_KEY = 'QA'
GIT_COMMIT = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
}
stages {
stage('Test Analysis') {
steps {
script {
// Analyze code changes to determine affected tests
sh '''
git diff --name-only HEAD~1 HEAD | grep -E '\.(js|ts|java|py)$' > changed_files.txt
python scripts/test_impact_analyzer.py changed_files.txt
'''
// Read affected test cases
def impactedTests = readJSON file: 'impacted_tests.json'
qualityMetrics.impactedTestCount = impactedTests.size()
echo "📊 Running ${impactedTests.size()} impacted tests"
}
}
}
stage('Execute Tests') {
parallel {
stage('API Tests') {
steps {
script {
withCredentials([[
$class: 'StringBinding',
credentialsId: 'zephyr-access-token',
variable: 'ZEPHYR_TOKEN'
]]) {
sh '''
# Run tests with Zephyr integration
npx newman run collections/api_suite.json \
--reporters cli,zephyr \
--reporter-zephyr-token $ZEPHYR_TOKEN \
--reporter-zephyr-projectKey $JIRA_PROJECT_KEY \
--reporter-zephyr-testCycle "API Cycle ${BUILD_NUMBER}"
'''
}
}
}
}
stage('UI Tests') {
steps {
script {
// Dynamic test allocation based on scope
def testFilter = params.TEST_SCOPE == 'SMOKE' ?
'--grep @smoke' :
params.TEST_SCOPE == 'REGRESSION' ?
'--grep @regression' : ''
sh """
npx cypress run --headless \
--browser chrome \
${testFilter} \
--env updateZephyr=${params.UPDATE_ZEPHYR}
"""
}
}
}
}
}
stage('Zephyr Sync') {
when {
expression { params.UPDATE_ZEPHYR == true }
}
steps {
script {
// Sync all test results to Zephyr
sh '''
python scripts/zephyr_sync.py \
--build-number ${BUILD_NUMBER} \
--commit ${GIT_COMMIT} \
--results-dir test-results
'''
// Update test execution status in Jira
jiraUpdateIssue idOrKey: 'QA-123',
issue: [fields: [customfield_12345: 'E
'''
}
}
}
}
}