概要
テストとは「書くもの」ではなく「設計を証明するもの」である。
それは単なるチェックリストではなく、設計が意図どおりであることを保証する、もう一つのインターフェースである。
本稿では、JavaScriptにおけるテストを「単なるツール」から「設計構造の一部」へと昇華させるために必要な戦略を解説する。
1. テスト三層構造の理解と責務分離
レイヤー | 主な対象 | 特徴 |
---|---|---|
Unit Test | 関数 / クラス | 高速、ピュア、責務明確 |
Integration | モジュール / サービス | 実際の依存間の整合性検証 |
E2E Test | UI + サーバ | ユーザー視点での振る舞い保証 |
→ ✅ それぞれ何を守るためのテストかを明確に設計する
2. ユニットテスト:純粋な構造の保証
function add(a, b) {
return a + b;
}
test('加算関数', () => {
expect(add(1, 2)).toBe(3);
});
- ✅ 入力と出力のみで定義される関数は最小の保証対象
- ✅ 副作用なし・依存なしが前提
3. 結合テスト:依存間の接続保証
import { fetchUser } from './api';
import { renderUser } from './ui';
test('ユーザー情報の取得と表示', async () => {
const user = await fetchUser();
const ui = renderUser(user);
expect(ui).toContain(user.name);
});
- ✅ 外部呼び出し(API / DBなど)はmock/stubで制御
- ✅ 依存先の“契約”を守るテスト → インターフェース保証
4. モック戦略:何を偽装し、何を残すか
jest.mock('./api', () => ({
fetchUser: jest.fn().mockResolvedValue({ id: 1, name: 'Toto' }),
}));
- ✅ モックは副作用の排除 or テスト高速化のための構造
- ✅ モックが乱立するとテストの意味が失われる → 構造の一部として設計すべき
5. E2Eテスト:動作の保証ではなく、流れの保証
// e.g. with Playwright
test('ログインしてダッシュボードが表示される', async ({ page }) => {
await page.goto('/login');
await page.fill('#email', 'user@example.com');
await page.fill('#password', 'securepassword');
await page.click('button[type=submit]');
await expect(page).toHaveURL('/dashboard');
});
- ✅ E2Eは振る舞いの確認 + 回帰テストの最後の砦
- ❌ 頻度は少なめでよい → 回帰用に絞って設計
6. テスト容易性を前提にした構造設計
- 関数は副作用と切り離して設計
- グローバル依存を減らし、DI / hook / context などで外部注入
- データ構造は関数引数で与える
function createUserStore(apiClient) {
return {
fetch: () => apiClient.get('/user'),
};
}
→ ✅ モック可能な構造にすることでテストしやすさ = 設計品質となる
7. CI / カバレッジとの統合戦略
- ✅ 全てのテストをCI上で実行し、“守られている”構造を保つ
- ✅ カバレッジはあくまで補助指標 → 低くても「テストすべき場所」がカバーされているならOK
- ✅ 重要なのは**“設計の意図がテストで守られているか”**
設計判断フロー
① テストがUI中心に偏っていないか? → ロジック単位の分離を検討
② モックが意味をなしているか? → 副作用・契約・境界で切る
③ ロジックとDOMが分離されているか? → ユニットとE2Eの分離設計へ
④ テストが壊れたとき“なぜ”かがわかるか? → 名前 / 構造を明示的に
⑤ テスト対象が関数でなければテスト不能では? → 設計見直し
よくあるミスと対策
❌ UIの見た目ばかりをsnapshotでテストし、内部ロジックが壊れても気づかない
→ ✅ テストの粒度をUI / ロジックに明示的に分離
❌ モックだらけでテストが意味を成していない
→ ✅ モックの対象を**「制御不能な外部」だけに限定**
❌ E2Eで全てを保証しようとして時間がかかりすぎる
→ ✅ E2Eはユーザーフロー + 致命的回帰の監視用に絞る
結語
テストとは「安心を得るため」ではない。
それは**“設計を守るための防波堤”**であり、
未来の開発者への契約文書でもある。
- 責務単位で分離し
- モックは構造的に設計し
- 振る舞いではなく“意図”を検証する
JavaScriptにおけるテスト設計とは、
“壊れても壊れた理由が分かり、すぐに直せる構造”をつくる戦略である。