test-quality-analysis について
テストスイートのレビューと改善を支援する、テスト品質分析スキルです。テストの匂いやアンチパターンの見つけ方に加え、信頼性、アサーション、モック、カバレッジ、保守性に関する指針を示します。
主な機能
- 01過剰なモックとテストの匂いを検出
- 02不安定で壊れやすいテストを特定
- 03アサーションの具体性を向上
- 04カバレッジと重要経路を確認
- 05モックと単体テストの指針
- 06コードレビュー用チェックリスト
活用シーン
- 01テストスイートの過剰なモックを点検
- 02断続的に失敗するテストを調査
- 03アサーションとエラー経路のカバレッジを強化
- 04コードレビューでテスト品質を評価
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill test-quality-analysisインストール後、Claude Code で/test-quality-analysisを入力して読み込まれたか確認します。
詳細
テスト品質の分析
テストの不吉な臭い、過剰なモック、カバレッジ不足、テストのアンチパターンを検出し、テスト品質を分析・向上させるための専門知識です。
主な評価軸
- 正確性:テストが正しい振る舞いを検証していること
- 信頼性:テストが決定的で、不安定でないこと
- 保守性:テストが理解しやすいこと
- パフォーマンス:テストがすばやく実行されること
- カバレッジ:テストが重要なコードパスを網羅していること
- 独立性:テストが外部の状態に依存しないこと
テストの不吉な臭い
過剰なモック
問題:依存関係をモックしすぎると、テストが壊れやすくなります。
// ❌ BAD: Overmocked
test('calculate total', () => {
const mockAdd = vi.fn(() => 10)
const mockMultiply = vi.fn(() => 20)
// Testing implementation, not behavior
})
// ✅ GOOD: Mock only external dependencies
test('calculate order total', () => {
const mockPricingAPI = vi.fn(() => ({ tax: 0.1 }))
const total = calculateTotal(order, mockPricingAPI)
expect(total).toBe(38)
})
検出方法:モックが3〜4個を超えている、純粋関数をモックしている、複雑なモックのセットアップが必要になっている。
対処方法:I/O の境界(API、データベース、ファイルシステム)のみをモックします。
壊れやすいテスト
問題:無関係なコードの変更によってテストが失敗します。
// ❌ BAD: Tests implementation details
await page.locator('.form-container > div:nth-child(2) > button').click()
// ✅ GOOD: Semantic selector
await page.getByRole('button', { name: 'Submit' }).click()
不安定なテスト
問題:テストの成否が非決定的です。
// ❌ BAD: Race condition
test('loads data', async () => {
fetchData()
await new Promise(resolve => setTimeout(resolve, 1000))
expect(data).toBeDefined()
})
// ✅ GOOD: Proper async handling
test('loads data', async () => {
const data = await fetchData()
expect(data).toBeDefined()
})
不適切なアサーション
// ❌ BAD: Weak assertion
test('returns users', async () => {
const users = await getUsers()
expect(users).toBeDefined() // Too vague!
})
// ✅ GOOD: Strong, specific assertions
test('creates user with correct attributes', async () => {
const user = await createUser({ name: 'John' })
expect(user).toMatchObject({
id: expect.any(Number),
name: 'John',
})
})
分析ツール
# Vitest coverage (prefer bun)
bun test --coverage
open coverage/index.html
# Check thresholds
bun test --coverage --coverage.thresholds.lines=80
# pytest-cov (Python)
uv run pytest --cov --cov-report=html
open htmlcov/index.html
ベストプラクティスのチェックリスト
単体テストの品質(FIRST)
- 高速:テストが数ミリ秒で実行される
- 独立:テスト間に依存関係がない
- 再現可能:毎回同じ結果になる
- 自己検証可能:合否が明確である
- 適時:コードと同時に作成される
モックのガイドライン
- 外部依存関係のみをモックする
- ビジネスロジックや純粋関数はモックしない
- 可能な場合は実際の実装を使用する
- 1つのテストにつきモックは最大3〜4個に制限する
カバレッジの目標
- ビジネスロジックの行カバレッジを80%以上にする
- 重要なパス(認証、支払い)は100%にする
- すべてのエラーパスをテストする
- 境界条件をテストする
テストの構成(AAA パターン)
test('user registration', async () => {
// Arrange
const userData = { email: '[email protected]' }
// Act
const user = await registerUser(userData)
// Assert
expect(user.email).toBe('[email protected]')
})
コードレビューのチェックリスト
- テストが実装ではなく振る舞いを検証している
- アサーションが具体的で意味のあるものになっている
- 不安定なテスト(タイミングや順序の問題)がない
- async/await が適切に使われている
- テスト名から振る舞いが明確にわかる
- コードの重複が最小限である
- 重要なパスにテストがある
- 正常系とエラーケースの両方が網羅されている
よくあるアンチパターン
実装の詳細をテストする
// ❌ BAD
const spy = vi.spyOn(Math, 'sqrt')
calculateDistance()
expect(spy).toHaveBeenCalled() // Testing how, not what
// ✅ GOOD
const distance = calculateDistance({ x: 0, y: 0 }, { x: 3, y: 4 })
expect(distance).toBe(5) // Testing output
モックの過剰な使用
// ❌ BAD
const mockAdd = vi.fn((a, b) => a + b)
// ✅ GOOD: Use real implementations
import { add } from './utils'
// Only mock external services
const mockPaymentGateway = vi.fn()
関連項目
vitest-testing- TypeScript/JavaScript のテストplaywright-testing- E2E テストmutation-testing- テストの有効性を検証する
コメント
Markdown 対応