I Built Tautest: AI가 작성한 테스트를 위한 Mutation Testing 워크플로우
Source: Dev.to
소개
AI 코딩 에이전트가 테스트를 작성하는 데 매우 능숙해지고 있지만, 테스트가 통과된다고 해서 그 테스트가 충분히 강력하다는 의미는 아닙니다.
종종 AI가 생성한 테스트는 현재 구현이 실행되는지만 확인하고, 의도된 동작이 보호되고 있다는 것을 입증하지 못합니다.
Tautest란 무엇인가?
Tautest는 풀 리퀘스트에서 변경된 라인에 대해 변이 테스트를 수행하고, 약한 테스트를 식별하며, Claude Code, Cursor, Codex 또는 인간 리뷰어를 위한 AI‑준비된 수정 프롬프트를 생성하는 오픈‑소스 CLI 및 GitHub Action입니다.
- GitHub:
- npm package:
Tautest 자체는 변이‑테스트 엔진이 아니며; StrykerJS를 활용하고 그 위에 워크플로 레이어를 추가합니다.
How Tautest Works
git diff에서 변경된 소스 라인을 읽습니다.- 해당 라인에 only StrykerJS 변이 테스트를 실행합니다.
- 살아남은 변이체를 파싱합니다.
- Markdown, JSON, 그리고 터미널 보고서를 생성합니다.
- AI‑ready 수정 프롬프트(
.tautest/fix-prompt.md)를 작성합니다. - (선택 사항) 고정된 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 테스트를 피하세요.
워크플로우
tautest를 실행합니다..tautest/fix-prompt.md를 엽니다.- 프롬프트를 Claude Code, Cursor, Codex에 붙여넣거나 직접 사용합니다.
- 누락된 테스트를 추가합니다.
- 기존 테스트 스위트를 실행합니다.
- 변이 점수를 확인하기 위해
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 패키지:
- 핵심 패키지: