I Built Tautest: AI가 작성한 테스트를 위한 Mutation Testing 워크플로우

발행: (2026년 5월 11일 AM 09:13 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

소개

AI 코딩 에이전트가 테스트를 작성하는 데 매우 능숙해지고 있지만, 테스트가 통과된다고 해서 그 테스트가 충분히 강력하다는 의미는 아닙니다.
종종 AI가 생성한 테스트는 현재 구현이 실행되는지만 확인하고, 의도된 동작이 보호되고 있다는 것을 입증하지 못합니다.

Tautest란 무엇인가?

Tautest는 풀 리퀘스트에서 변경된 라인에 대해 변이 테스트를 수행하고, 약한 테스트를 식별하며, Claude Code, Cursor, Codex 또는 인간 리뷰어를 위한 AI‑준비된 수정 프롬프트를 생성하는 오픈‑소스 CLI 및 GitHub Action입니다.

  • GitHub:
  • npm package:

Tautest 자체는 변이‑테스트 엔진이 아니며; StrykerJS를 활용하고 그 위에 워크플로 레이어를 추가합니다.

How Tautest Works

  1. git diff에서 변경된 소스 라인을 읽습니다.
  2. 해당 라인에 only StrykerJS 변이 테스트를 실행합니다.
  3. 살아남은 변이체를 파싱합니다.
  4. Markdown, JSON, 그리고 터미널 보고서를 생성합니다.
  5. AI‑ready 수정 프롬프트(.tautest/fix-prompt.md)를 작성합니다.
  6. (선택 사항) 고정된 GitHub PR 댓글을 게시합니다.

Supported test runners

  • Vitest (전체 지원)
  • Jest (베타)

Example

Code under test

// src/discount.ts
if (age >= 65) {
  return subtotal * 0.2;
}

Normal test suite (passes)

// test/discount.test.ts
test("applies senior discount", () => {
  expect(calculateDiscount(70, 100)).toBe(20);
});

Mutated code

// Mutant: change >= to >
if (age > 65) {
  return subtotal * 0.2;
}

테스트 스위트가 여전히 통과한다면, 65라는 경계는 보호되지 않은 것입니다 – 약한 테스트입니다.

Tautest를 실행하면 살아남은 변이가 표시됩니다:

Tautest: MIXED (75.00%, threshold 60.00%)
Killed: 3 | Survived: 1 | No coverage: 0

Top surviving mutants:
- src/discount.ts:2 EqualityOperator
  age >= 65  →  age > 65

경계 테스트를 추가한 후:

test("applies senior discount at exactly 65", () => {
  expect(calculateDiscount(65, 80)).toBe(16);
});

Tautest는 완벽한 점수를 보고합니다:

Tautest: STRONG (100.00%, threshold 60.00%)
Killed: 4 | Survived: 0

생성된 AI 수정 프롬프트

Tautest는 다음과 같은 규칙을 포함하는 .tautest/fix-prompt.md 파일을 생성합니다:

  • 프로덕션 코드를 변경하지 마세요.
  • 테스트 파일만 수정하거나 추가하세요.
  • 새로운 테스트는 원본 코드에 대해 통과해야 합니다.
  • 새로운 테스트는 변이된 동작에 대해 실패해야 합니다.
  • 기존 어설션을 약화시키지 마세요.
  • expect(true).toBe(true)와 같은 filler 테스트를 피하세요.

워크플로우

  1. tautest를 실행합니다.
  2. .tautest/fix-prompt.md를 엽니다.
  3. 프롬프트를 Claude Code, Cursor, Codex에 붙여넣거나 직접 사용합니다.
  4. 누락된 테스트를 추가합니다.
  5. 기존 테스트 스위트를 실행합니다.
  6. 변이 점수를 확인하기 위해 tautest를 다시 실행합니다.

설치

Vitest 프로젝트

pnpm add -D tautest @stryker-mutator/core @stryker-mutator/vitest-runner
pnpm exec tautest init --yes --runner vitest --no-install
pnpm exec tautest doctor
pnpm exec tautest run --base origin/main

Jest 프로젝트 (베타)

pnpm add -D tautest @stryker-mutator/core @stryker-mutator/jest-runner
pnpm exec tautest init --yes --runner jest --no-install

GitHub 액션

다음 워크플로 파일을 추가하세요 (예: .github/workflows/tautest.yml):

name: Tautest
on:
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  tautest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # git 히스토리가 필요함

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - uses: pnpm/action-setup@v4
        with:
          version: 10

      - run: pnpm install --frozen-lockfile
      - run: pnpm build

      - uses: canblmz1/tautest/packages/github-action@v1
        with:
          base: ${{ github.base_ref }}
          threshold: 60
          comment: changes
          cache: true

중요한 참고 사항

  • fetch-depth: 0 은 Tautest가 전체 git 히스토리를 필요로 하기 때문에 반드시 설정해야 합니다.
  • pull-requests: write 권한은 고정된 PR 댓글을 작성하기 위해 필요합니다.

제한 사항

Tautest는 의도적으로 다음을 하지 않습니다:

  • 자체 변이 엔진을 구현하지 않습니다 (StrykerJS에 의존합니다).
  • StrykerJS를 대체하지 않습니다.
  • 어떤 LLM API도 호출하지 않습니다.
  • 테스트가 완벽하다고 주장하지 않습니다.
  • v1에서 모노레포를 완전히 지원하지 않습니다.
  • AI‑작성 테스트를 확실히 분류하지 않습니다.

결정론적인 파이프라인을 제공합니다:

changed source lines → mutation testing → surviving mutants → report → fix prompt

향후 개선 사항

  • GitHub Action을 위해 Node 24 런타임으로 마이그레이션.
  • 캐시 가시성 향상.
  • 모노레포에 대한 베타 지원.
  • 독립형 GitHub Action 리포지토리.
  • PR 라인 주석.
  • 더 많은 Jest 픽스처.

피드백 요청

다음 항목에 대한 피드백을 받고 싶습니다:

  • README와 데모의 명확성.
  • GitHub Action 워크플로우의 합리성.
  • AI fix‑prompt 워크플로우의 유용성.
  • 현재 프로젝트를 JavaScript/TypeScript에 집중할지 여부.

링크

  • GitHub 저장소:
  • npm 패키지:
  • 핵심 패키지:
0 조회
Back to Blog

관련 글

더 보기 »