본문 바로가기
IT & Tech 정보

AI 기반 테스트 우선순위화 끝판왕 가이드: ML 모델로 Test Suite 최적화

by 지식과 지혜의 나무 2025. 5. 26.
반응형




1. 서론: 왜 AI로 테스트 순서를 짜야 하나

대규모 모놀리틱→마이크로서비스 전환, CI/CD 파이프라인 고도화, 코드베이스 성장으로 수천 수만 개 테스트를 매번 실행하기엔 시간과 자원이 치명적으로 낭비됩니다. 실리콘밸리·심천 톱티어 기업들은 테스트 실패 확률·코드 변경 영향도를 기반으로 우선순위를 동적으로 재조정해, 최소한의 테스트로 최대한의 결함 탐지를 실현합니다.
• 피드백 루프 단축: 변경된 코드와 연관된 테스트만 빠르게 실행
• 리소스 최적화: 불필요한 테스트 격리·스킵 → 빌드 자원 절감
• AI 예측 정확도: 과거 테스트 히스토리·커버리지·코드 복잡도 데이터 학습

이제 머신러닝 모델을 활용해 Test Suite를 동적 스코어링·샘플링하는 ‘끝판왕’ 전략을 설계해 보겠습니다.



2. 아키텍처 개요

코드 레포 ──▶ 변경 감지 ──▶ 피처 엔지니어링 ──▶ ML 모델 예측 ──▶
└────────────▶ Test Prioritizer ──▶ CI 파이프라인 (Parallel/Matrix)

1. 변경 감지: Git diff로 수정된 파일·라인 추출
2. 피처 엔지니어링:
• 코드 변경 메트릭: 변경 라인 수, 변경 파일 수, churn rate
• 테스트 메타: 과거 실패율, 실행시간, 커버리지 비율
• 코드 테스트 맵: 어떤 테스트가 어떤 코드 라인을 커버하는지
3. ML 모델 학습:
• 분류/순위 예측 모델(Gradient Boosting, LightGBM, XGBoost)
• Label: 테스트 실패 여부(0/1) 또는 실행 우선순위(0~1 스코어)
4. Test Prioritizer: 스코어 기준 상위 N개 혹은 스코어 합 ≥ T 실행
5. CI 파이프라인: 선택된 테스트만 병렬 실행 → 결과 집계



3. 데이터 수집 & 피처 엔지니어링

3.1 Git 변경 감지

git diff --name-only origin/main...HEAD > changed_files.txt
git diff --unified=0 origin/main...HEAD | grep -E "^\+[^+]" > changed_lines.diff

• changed_files.txt: 변경된 파일 리스트
• changed_lines.diff: 변경된 라인 번호(정규표현식)

3.2 커버리지 매핑
• Coverage Report: lcov, Jacoco, coverage.py 등으로 라인 단위 커버리지 JSON 생성
• 파싱 스크립트 (Python 예시):

import json
from collections import defaultdict

with open('coverage/coverage-summary.json') as f:
    cov = json.load(f)

# 파일 → 라인 커버리지 맵
file_line_map = defaultdict(set)
for file_path, data in cov['files'].items():
    for line_no, count in data['lines'].items():
        if count > 0:
            file_line_map[file_path].add(int(line_no))

3.3 테스트 메타 수집
• 과거 빌드 로그(CI DB)에서:
• test_name, duration, status(pass/fail), timestamp
• 스크립트 예:

SELECT test_name,
       AVG(duration) AS avg_time,
       SUM(CASE WHEN status='FAIL' THEN 1 ELSE 0 END) / COUNT(*) AS fail_rate
FROM ci_test_history
WHERE repo='my-app'
GROUP BY test_name;

3.4 피처 집계
• Feature Vector per test per commit:

- test_name: test_user_api
  changed_files_count: 3
  changed_lines_count: 7
  overlap_cover_lines: 5
  avg_duration: 12.3
  fail_rate: 0.08
  last_run_days_ago: 2


• Overlap = len(changed_lines ∩ coverage_lines)



4. ML 모델 설계 & 학습

4.1 모델 선택
• LightGBM (Ranker / Classifier)
• XGBoost with rank:pairwise
• Neural Network (optional): Feed-Forward + Tabular Embedding

4.2 학습 파이프라인 (Python)

import pandas as pd
from lightgbm import LGBMRanker
from sklearn.model_selection import GroupKFold

# 데이터 로드
df = pd.read_csv('features.csv')
X = df.drop(['test_name','score','group'], axis=1)
y = df['score']  # 1: 실패 확률 높음 우선
groups = df['group']  # commit id as group

# Ranker 사용 예
model = LGBMRanker(
    objective='lambdarank',
    metric='ndcg',
    n_estimators=100,
    learning_rate=0.1
)

# GroupKFold로 커밋 단위 CV
gkf = GroupKFold(n_splits=5)
for train_idx, val_idx in gkf.split(X, y, groups):
    model.fit(
        X.iloc[train_idx], y.iloc[train_idx],
        group=groups.iloc[train_idx].value_counts().sort_index().values,
        eval_set=[(X.iloc[val_idx], y.iloc[val_idx])],
        eval_group=[groups.iloc[val_idx].value_counts().sort_index().values],
        early_stopping_rounds=10
    )

• GroupKFold: 커밋 단위 묶음
• Lambdarank: 순위 예측에 특화된 손실 함수

4.3 모델 서빙
• 학습 완료 모델을 pickle로 저장,
• FastAPI나 Flask로 간단한 예측 서버 구축:

from fastapi import FastAPI
import pickle
import pandas as pd

app = FastAPI()
model = pickle.load(open('lgb_ranker.pkl','rb'))

@app.post('/predict')
def predict(features: list[dict]):
    df = pd.DataFrame(features)
    scores = model.predict(df)
    return {'scores': scores.tolist()}




5. CI/CD 파이프라인 연동

5.1 GitHub Actions 워크플로우

name: Predict & Run Tests

on: push

jobs:
  prioritize-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Detect changes
        run: |
          git diff --name-only origin/main...HEAD > changed_files.txt
          git diff --unified=0 origin/main...HEAD | grep -E "^\+[^+]" > changed_lines.diff

      - name: Gather features
        run: python scripts/extract_features.py

      - name: Predict test scores
        run: |
          pip install -r requirements.txt
          python scripts/predict_tests.py \
            --model-path=lgb_ranker.pkl \
            --features=features.json \
            --output=prioritized_tests.txt

      - name: Run top tests
        uses: dorny/test-reporter@v1
        with:
          name: "Top 20 Tests"
          command: |
            head -n 20 prioritized_tests.txt | xargs -n1 -P4 pytest -q --junitxml=reports/{}.xml

• extract_features.py: 2장 피처 엔지니어링 스크립트
• predict_tests.py: FastAPI 서버 호출 or 직접 model.predict

5.2 Jenkins Pipeline 예시

pipeline {
  agent any
  stages {
    stage('Checkout & Feature Extraction') {
      steps {
        checkout scm
        sh 'python3 scripts/extract_features.py'
      }
    }
    stage('Predict & Select Tests') {
      steps {
        sh 'python3 scripts/predict_tests.py --model lgb_ranker.pkl --features features.json --top 30 > tests_to_run.txt'
      }
    }
    stage('Execute Prioritized Tests') {
      parallel {
        script {
          def tests = readFile('tests_to_run.txt').split('\n')
          def branches = tests.collate(5)  // 그룹당 5개씩 병렬
          for (int i=0; i<branches.size(); i++) {
            def batch = branches[i]
            stage("Batch ${i+1}") {
              steps {
                sh "pytest ${batch.join(' ')} --junitxml=reports/batch${i+1}.xml"
              }
            }
          }
        }
      }
    }
  }
}

• batch 그룹화와 병렬 실행으로 피드백 극대화



6. “아, 이런 방법도 있었구나” 팁
1. 온라인 학습: 테스트 실패 데이터를 스트리밍해 주기적 모델 업데이트 (River, creme 라이브러리)
2. Transfer Learning: 신규 프로젝트에 기존 모델 가중치 활용 후 미세조정
3. Explainability: SHAP으로 각 피처별 예측 기여도 분석 → 팀에 피드백
4. Adaptive Threshold: 전체 테스트 커버리지 목표에 따라 동적 N 자동 조정
5. Dashboard: Grafana + Prometheus로
• 예측 정확도(Precision@K)
• 실행 대비 실패 탐지율
• CI 시간 절감 효과 시각화



7. 결론
• 데이터 기반으로 Test Suite를 동적 우선순위화
• LightGBM 랭킹 모델로 실패 확률 예측 → 상위 N개 테스트만 실행
• CI/CD에 ML 파이프라인 연동 → 피드백 루프 단축

“교과서엔 절대 없는, 실리콘밸리·심천 IT기업 톱티어 엔지니어급 AI 기반 테스트 최적화”를 직접 구현해 보세요. 최소한의 테스트로 최대한의 안정성을 보장하는 그날까지!

반응형