본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 5장: 변이 테스트(Mutation Testing)
2026년 3월 12일·AI / ML·

5장: 변이 테스트(Mutation Testing)

변이 테스트의 원리와 변이 연산자를 이해하고, Stryker, PIT, mutmut 도구로 AI 생성 테스트의 품질을 검증하는 방법을 다룹니다. 변이 점수 측정과 비용-효과 분석도 포함합니다.

15분432자7개 섹션
testingautomationquality-assuranceai
공유
ai-testing5 / 10
12345678910
이전4장: E2E 테스트 -- AI 에이전트 기반 자동화다음6장: 시각적 회귀 테스트

학습 목표

  • 변이 테스트의 원리와 핵심 개념을 이해합니다
  • 주요 변이 연산자의 종류와 역할을 파악합니다
  • Stryker, PIT, mutmut 도구의 특성과 활용법을 학습합니다
  • AI 생성 테스트의 변이 점수를 측정하고 품질을 평가하는 방법을 실습합니다

변이 테스트란?

코드 커버리지 100%라는 수치가 테스트의 품질을 보장할까요? 안타깝게도 그렇지 않습니다. 코드를 실행하기만 하고 결과를 제대로 검증하지 않는 테스트는 커버리지는 높이지만, 실제 결함을 놓칩니다.

Mutation Testing(변이 테스트)은 이 문제를 해결합니다. 소스 코드에 의도적으로 작은 변경(변이)을 주입하고, 기존 테스트 스위트가 이 변이를 감지하는지 확인합니다. 테스트가 변이를 잡아내면 killed(사살), 잡아내지 못하면 survived(생존)으로 분류합니다.

핵심 개념

Mutation Score(변이 점수)는 전체 변이체 중 사살된 변이체의 비율입니다.

plaintext
변이 점수 = 사살된 변이체 / 전체 변이체 x 100%

변이 점수가 높을수록 테스트 스위트의 결함 검출 능력이 뛰어납니다.

Info

변이 테스트는 "테스트를 테스트하는 테스트"라고 할 수 있습니다. 코드 커버리지가 테스트의 범위를 측정한다면, 변이 점수는 테스트의 깊이를 측정합니다. 이 둘을 함께 사용할 때 테스트 품질을 종합적으로 평가할 수 있습니다.


변이 연산자

변이 연산자는 소스 코드에 적용하는 변경 규칙입니다. 각 연산자는 개발자가 실수할 수 있는 실제 결함 패턴을 모방합니다.

주요 변이 연산자

범주연산자원본변이
산술ArithmeticOperatora + ba - b
관계RelationalOperatora > ba >= b
논리LogicalOperatora && ba || b
조건ConditionalExpressionif (x)if (true) / if (false)
단항UnaryOperator-xx
문자열StringLiteral"hello"""
배열ArrayDeclaration[a, b][]
반환값ReturnValuereturn xreturn 0 / return null
블록BlockStatement코드 블록빈 블록
동등성EqualityOperator===!==

변이 연산자 실제 예시

다음 함수에 변이 연산자를 적용해 보겠습니다.

price-calculator.ts (원본)
typescript
function calculateFinalPrice(
  basePrice: number,
  quantity: number,
  discountRate: number
): number {
  if (quantity <= 0) {
    throw new Error("수량은 1 이상이어야 합니다");
  }
 
  let total = basePrice * quantity;
 
  if (quantity >= 10) {
    total *= 0.95; // 대량 구매 할인 5%
  }
 
  total *= 1 - discountRate;
 
  return Math.round(total);
}

이 함수에서 생성되는 변이체 예시는 다음과 같습니다.

변이체 목록
typescript
// 변이체 1: 관계 연산자 변경 (quantity <= 0 -> quantity < 0)
if (quantity < 0) { ... }
 
// 변이체 2: 산술 연산자 변경 (basePrice * quantity -> basePrice + quantity)
let total = basePrice + quantity;
 
// 변이체 3: 관계 연산자 변경 (quantity >= 10 -> quantity > 10)
if (quantity > 10) { ... }
 
// 변이체 4: 상수 변경 (0.95 -> 1.05)
total *= 1.05;
 
// 변이체 5: 산술 연산자 변경 (1 - discountRate -> 1 + discountRate)
total *= 1 + discountRate;
 
// 변이체 6: 블록 제거 (대량 구매 할인 블록 전체 제거)
// if (quantity >= 10) { ... } -> 삭제
 
// 변이체 7: 반환값 변경 (Math.round(total) -> 0)
return 0;

도구별 실습

Stryker -- JavaScript/TypeScript 환경

Stryker는 JavaScript/TypeScript 생태계에서 가장 널리 사용되는 변이 테스트 도구입니다.

Stryker 설치 및 초기화
bash
npm install --save-dev @stryker-mutator/core @stryker-mutator/jest-runner @stryker-mutator/typescript-checker
npx stryker init
stryker.config.json
json
{
  "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
  "mutate": ["src/**/*.ts", "!src/**/*.test.ts", "!src/**/*.spec.ts"],
  "testRunner": "jest",
  "checkers": ["typescript"],
  "reporters": ["html", "clear-text", "progress"],
  "thresholds": {
    "high": 80,
    "low": 60,
    "break": 50
  },
  "concurrency": 4,
  "timeoutMS": 10000
}
Stryker 실행
bash
npx stryker run

Stryker 실행 결과는 다음과 같은 형태로 출력됩니다.

Stryker 결과 리포트
plaintext
----------|---------|----------|-----------|
File      | % score | # killed | # survived|
----------|---------|----------|-----------|
cart.ts   |   85.71 |       12 |         2 |
price.ts  |   77.78 |        7 |         2 |
user.ts   |   90.00 |        9 |         1 |
----------|---------|----------|-----------|
All files |   84.85 |       28 |         5 |
Tip

Stryker의 thresholds 설정에서 break 값은 변이 점수가 이 값 미만으로 떨어지면 빌드를 실패시키는 기준입니다. CI 파이프라인에서 테스트 품질의 최소 기준을 강제할 수 있습니다.

PIT -- Java 환경

PIT(PITest)는 Java 생태계의 표준 변이 테스트 도구입니다.

pom.xml (Maven 설정)
xml
<plugin>
    <groupId>org.pitest</groupId>
    <artifactId>pitest-maven</artifactId>
    <version>1.16.0</version>
    <configuration>
        <targetClasses>
            <param>com.example.service.*</param>
        </targetClasses>
        <targetTests>
            <param>com.example.service.*Test</param>
        </targetTests>
        <mutators>
            <mutator>DEFAULTS</mutator>
            <mutator>REMOVE_CONDITIONALS</mutator>
        </mutators>
        <mutationThreshold>70</mutationThreshold>
        <outputFormats>
            <outputFormat>HTML</outputFormat>
            <outputFormat>XML</outputFormat>
        </outputFormats>
    </configuration>
</plugin>
PIT 실행
bash
mvn org.pitest:pitest-maven:mutationCoverage

mutmut -- Python 환경

mutmut은 Python 생태계의 변이 테스트 도구입니다.

mutmut 설치 및 실행
bash
pip install mutmut
mutmut run --paths-to-mutate=src/
생존 변이체 확인
bash
mutmut results
mutmut show 5  # 5번 생존 변이체의 상세 내용 확인

AI 생성 테스트의 변이 점수 측정

2장에서 AI가 생성한 테스트가 실제로 결함을 잡아내는지 변이 테스트로 검증해 보겠습니다.

실험 설계

비교 결과 예시

측정 항목AI 생성 테스트수동 작성 테스트
테스트 케이스 수15개8개
라인 커버리지92%85%
분기 커버리지88%80%
변이 점수75%82%
생존 변이체8개5개
작성 시간30초2시간

흥미로운 점은 AI 생성 테스트가 커버리지는 높지만, 변이 점수는 수동 테스트보다 낮을 수 있다는 것입니다. 이는 AI가 코드를 실행하는 데는 탁월하지만, 결과를 검증하는 단언의 정밀도가 부족할 수 있기 때문입니다.

생존 변이체 분석

생존한 변이체를 분석하면 테스트의 약점을 파악할 수 있습니다.

생존 변이체 예시
typescript
// 원본: total *= 0.95
// 변이: total *= 1.05
 
// AI 생성 테스트에서 이 변이가 생존한 이유:
// 테스트가 대량 구매 시 할인이 적용되는지만 확인하고,
// 정확한 할인 금액을 검증하지 않았기 때문
test("대량 구매 시 할인이 적용된다", () => {
  const result = calculateFinalPrice(1000, 10, 0);
  expect(result).toBeLessThan(10000); // 느슨한 단언 -- 변이 감지 실패
});
 
// 개선된 테스트:
test("대량 구매 시 5% 할인이 적용된다", () => {
  const result = calculateFinalPrice(1000, 10, 0);
  expect(result).toBe(9500); // 정밀한 단언 -- 변이 감지 성공
});
Warning

AI 생성 테스트의 가장 흔한 약점은 느슨한 단언(loose assertion)입니다. toBeTruthy(), toBeDefined(), toBeGreaterThan(0) 같은 단언은 커버리지는 올리지만, 미묘한 결함을 놓칩니다. 변이 테스트는 이러한 약점을 정확히 드러냅니다.


비용-효과 분석

변이 테스트는 강력하지만 비용이 큽니다. 모든 변이체에 대해 전체 테스트 스위트를 실행해야 하므로, 대규모 프로젝트에서는 실행 시간이 수 시간에 달할 수 있습니다.

비용 절감 전략

전략설명효과
증분 변이변경된 코드에만 변이 적용실행 시간 70-90% 감소
변이 샘플링전체 변이의 일부만 무작위 선택실행 시간에 비례하여 감소
빠른 실패하나의 테스트가 변이를 감지하면 나머지 건너뜀평균 50% 감소
병렬 실행변이체별 테스트를 병렬로 실행코어 수에 비례하여 감소
정적 분석 연계도달 불가능한 변이를 사전 제거불필요한 변이 10-20% 제거

CI 파이프라인 통합 전략

.github/workflows/mutation-test.yml
yaml
name: Mutation Testing
 
on:
  pull_request:
    branches: [main]
 
jobs:
  mutation-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
 
      - name: Get changed files
        id: changed
        run: |
          echo "files=$(git diff --name-only origin/main...HEAD -- 'src/**/*.ts' | tr '\n' ',')" >> $GITHUB_OUTPUT
 
      - name: Run incremental mutation test
        if: steps.changed.outputs.files != ''
        run: |
          npx stryker run --mutate="${{ steps.changed.outputs.files }}"
 
      - name: Check mutation score
        run: |
          SCORE=$(cat reports/mutation/mutation.json | jq '.schemaVersion' -r)
          echo "Mutation score: $SCORE%"
Tip

모든 PR에 전체 변이 테스트를 실행하는 것은 비현실적입니다. 권장 전략은 다음과 같습니다. (1) PR 단위에서는 변경된 파일에 대해서만 증분 변이 테스트를 실행합니다. (2) 주간 또는 릴리스 전에 전체 변이 테스트를 실행합니다. (3) 변이 점수가 기준 이하로 떨어지면 빌드를 실패시킵니다.


정리

이 장에서는 변이 테스트를 통해 AI 생성 테스트의 실질적 품질을 검증하는 방법을 살펴보았습니다.

핵심 내용을 정리하면 다음과 같습니다.

  • 변이 테스트는 코드에 의도적 변이를 주입하여 테스트의 결함 검출 능력을 측정합니다
  • 변이 점수는 커버리지와 함께 사용할 때 테스트 품질을 종합적으로 평가할 수 있습니다
  • Stryker(JS/TS), PIT(Java), mutmut(Python)이 각 생태계의 대표 도구입니다
  • AI 생성 테스트는 커버리지는 높지만, 느슨한 단언으로 인해 변이 점수가 낮을 수 있습니다
  • 증분 변이, 샘플링, 병렬 실행 등의 전략으로 비용을 관리할 수 있습니다

다음 장 미리보기

6장에서는 시각적 회귀 테스트를 다룹니다. 픽셀 단위 비교의 한계를 넘어, Applitools Eyes의 Visual AI가 어떻게 강화 학습을 활용하여 의미 있는 시각적 변화만 감지하는지, 그리고 Percy, Chromatic 등과의 차이점을 분석합니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#testing#automation#quality-assurance#ai

관련 글

AI / ML

6장: 시각적 회귀 테스트

픽셀 비교의 한계를 넘어 Visual AI 기반 시각적 회귀 테스트를 다룹니다. Applitools Eyes, Percy, Chromatic 비교 분석과 동적 콘텐츠 처리, 반응형 레이아웃 테스트, 스토리북 통합을 안내합니다.

2026년 3월 14일·17분
AI / ML

4장: E2E 테스트 -- AI 에이전트 기반 자동화

자연어를 E2E 테스트로 변환하는 Momentic, testRigor, Functionize와 DOM 변경에 자동 적응하는 셀프 힐링 기능, Playwright와 AI를 결합한 실전 E2E 테스트 자동화를 다룹니다.

2026년 3월 10일·17분
AI / ML

7장: 테스트 유지보수 자동화

테스트 로트(Test Rot) 문제의 근본 원인과 AI 기반 셀프 힐링, 셀렉터 자동 재바인딩, 테스트 코드 리팩터링, 중복 테스트 감지, 커버리지 갭 분석 등 유지보수 비용 절감 전략을 다룹니다.

2026년 3월 16일·16분
이전 글4장: E2E 테스트 -- AI 에이전트 기반 자동화
다음 글6장: 시각적 회귀 테스트

댓글

목차

약 15분 남음
  • 학습 목표
  • 변이 테스트란?
    • 핵심 개념
  • 변이 연산자
    • 주요 변이 연산자
    • 변이 연산자 실제 예시
  • 도구별 실습
    • Stryker -- JavaScript/TypeScript 환경
    • PIT -- Java 환경
    • mutmut -- Python 환경
  • AI 생성 테스트의 변이 점수 측정
    • 실험 설계
    • 비교 결과 예시
    • 생존 변이체 분석
  • 비용-효과 분석
    • 비용 절감 전략
    • CI 파이프라인 통합 전략
  • 정리
    • 다음 장 미리보기