Skip to content

コーディングとテストガイド

概要

このガイドは、開発ガイドのコーディングとテスト部分を詳細化したドキュメントです。TDD(テスト駆動開発)を中核とした実装プロセスの具体的な手順と、品質を維持するための規律を定義します。

基本原則

TDD(Test-Driven Development)の三つの原則

  1. 失敗するテストを書くまで、プロダクションコードを書いてはならない
  2. 失敗するテストは、失敗するのに十分なだけ書く
  3. 現在失敗しているテストを成功させる以上のプロダクションコードを書いてはならない

Red-Green-Refactor サイクル

uml diagram

イテレーション開発フロー

全体プロセス

uml diagram

アプローチ戦略

インサイドアウト vs アウトサイドイン

uml diagram

インサイドアウトアプローチ

uml diagram

アウトサイドインアプローチ

uml diagram

TDD実装の詳細手順

1. TODO作成

## TODOリストの例

- [ ] ユーザー登録機能
  - [ ] Userエンティティの作成
    - [ ] 必須フィールドの検証
    - [ ] メールアドレスの形式検証
    - [ ] パスワードの強度チェック
  - [ ] UserRepositoryの実装
    - [ ] save()メソッド
    - [ ] findByEmail()メソッド
    - [ ] existsByEmail()メソッド
  - [ ] UserServiceの実装
    - [ ] register()メソッド
    - [ ] 重複チェック
    - [ ] パスワードハッシュ化
  - [ ] UserControllerの実装
    - [ ] POST /users エンドポイント
    - [ ] バリデーション
    - [ ] エラーハンドリング

2. Red フェーズ(失敗するテストを書く)

// UserTest.java
@Test
void ユーザー作成時に必須フィールドが検証される() {
    // Given
    String email = "test@example.com";
    String password = "SecurePass123!";
    String name = "山田太郎";

    // When
    User user = new User(email, password, name);

    // Then
    assertThat(user.getEmail()).isEqualTo(email);
    assertThat(user.getName()).isEqualTo(name);
    // パスワードはハッシュ化されている
    assertThat(user.getPassword()).isNotEqualTo(password);
}

@Test
void 無効なメールアドレスで例外が発生する() {
    // Given
    String invalidEmail = "invalid-email";

    // When/Then
    assertThrows(IllegalArgumentException.class, () -> {
        new User(invalidEmail, "password", "name");
    });
}

3. Green フェーズ(最小限の実装)

// User.java
public class User {
    private String email;
    private String password;
    private String name;

    public User(String email, String password, String name) {
        if (!isValidEmail(email)) {
            throw new IllegalArgumentException("無効なメールアドレス");
        }
        this.email = email;
        this.password = hashPassword(password);
        this.name = name;
    }

    private boolean isValidEmail(String email) {
        return email != null && email.contains("@");
    }

    private String hashPassword(String password) {
        // 簡単なハッシュ化の実装(後で改善)
        return "hashed_" + password;
    }

    // getters...
}

4. Refactor フェーズ(設計改善)

// User.java (リファクタリング後)
public class User {
    private final Email email;
    private final Password password;
    private final Name name;

    public User(String email, String password, String name) {
        this.email = new Email(email);
        this.password = new Password(password);
        this.name = new Name(name);
    }

    // getters...
}

// Email.java (値オブジェクト)
public class Email {
    private static final Pattern VALID_EMAIL_PATTERN =
        Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");

    private final String value;

    public Email(String value) {
        if (value == null || !VALID_EMAIL_PATTERN.matcher(value).matches()) {
            throw new IllegalArgumentException("無効なメールアドレス: " + value);
        }
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

// Password.java (値オブジェクト)
public class Password {
    private static final int MIN_LENGTH = 8;
    private final String hashedValue;

    public Password(String rawPassword) {
        validate(rawPassword);
        this.hashedValue = BCrypt.hashpw(rawPassword, BCrypt.gensalt());
    }

    private void validate(String password) {
        if (password == null || password.length() < MIN_LENGTH) {
            throw new IllegalArgumentException(
                "パスワードは" + MIN_LENGTH + "文字以上必要です"
            );
        }
    }

    public boolean matches(String rawPassword) {
        return BCrypt.checkpw(rawPassword, hashedValue);
    }
}

品質チェックリスト

コミット前の必須確認事項

# 1. テスト実行
./gradlew test  # または npm test

# 2. コードフォーマット
./gradlew spotlessApply  # または npm run format

# 3. 静的解析
./gradlew check  # または npm run lint

# 4. ビルド確認
./gradlew build  # または npm run build

# 5. カバレッジ確認
./gradlew jacocoTestReport  # または npm run test:coverage

品質基準

項目 基準 必須/推奨
テストカバレッジ 80%以上 必須
循環的複雑度 10以下 必須
メソッドの行数 20行以下 推奨
クラスの行数 200行以下 推奨
重複コード 0% 必須
コンパイラ警告 0個 必須
Linter警告 0個 必須

コミット規約

コミットメッセージフォーマット

<type>(<scope>): <subject>

<body>

<footer>

タイプ一覧

タイプ 説明
feat 新機能追加 feat(auth): ユーザー認証機能を追加
fix バグ修正 fix(api): NullPointerExceptionを修正
docs ドキュメント変更 docs(readme): インストール手順を更新
style コードスタイル変更 style: インデントを修正
refactor リファクタリング refactor(user): Userクラスを値オブジェクトに分割
test テスト追加・修正 test(user): ユーザー登録のテストを追加
chore ビルド・ツール変更 chore(gradle): 依存関係を更新

コミット単位

  • 1コミット = 1論理的変更
  • TODOリストの項目単位でコミット
  • Red-Green-Refactorサイクル完了時にコミット
  • ビルドが通る状態でコミット

テストの種類と戦略

テストピラミッド

uml diagram

テスト作成のベストプラクティス

1. AAA パターン(Arrange-Act-Assert)

@Test
void 商品の在庫が減少する() {
    // Arrange(準備)
    Product product = new Product("商品A", 10);
    int orderQuantity = 3;

    // Act(実行)
    product.reduceStock(orderQuantity);

    // Assert(検証)
    assertThat(product.getStock()).isEqualTo(7);
}

2. テストの命名規則

// パターン1: 日本語での説明的な名前
@Test
void 在庫が不足している場合は注文できない() { }

// パターン2: Given-When-Then形式
@Test
void given在庫10個_when15個注文_then在庫不足例外() { }

// パターン3: メソッド名_条件_期待結果
@Test
void reduceStock_在庫不足_IllegalStateException() { }

3. テストデータの準備

// テストフィクスチャの使用
public class UserTestFixture {
    public static User createDefaultUser() {
        return new User(
            "test@example.com",
            "password123",
            "テストユーザー"
        );
    }

    public static User createUserWithEmail(String email) {
        return new User(
            email,
            "password123",
            "テストユーザー"
        );
    }
}

リファクタリングパターン

よく使うリファクタリング手法

パターン 適用場面
メソッド抽出 長いメソッドの分割 検証ロジックを別メソッドへ
変数抽出 複雑な式の簡略化 計算結果を一時変数へ
クラス抽出 責務の分離 値オブジェクトの抽出
メソッド移動 適切なクラスへの配置 ユーティリティメソッドの移動
条件記述の分解 複雑な条件式の整理 if文の条件をメソッド化
ループの分割 単一責任の原則適用 異なる処理を別ループへ

リファクタリング実施のタイミング

  1. Rule of Three(3回ルール)

  2. 1回目: そのまま実装

  3. 2回目: 重複に気づくが我慢
  4. 3回目: リファクタリング実施

  5. コードの臭い(Code Smells)を検知したとき

  6. 長すぎるメソッド(20行以上)

  7. 大きすぎるクラス(200行以上)
  8. 長すぎるパラメータリスト(4個以上)
  9. データの群れ(同じ引数の組み合わせ)
  10. スイッチ文の重複

継続的な改善

ふりかえりでの確認項目

## イテレーションふりかえりテンプレート

### Keep(継続すること)
- [ ] TDDサイクルの実践
- [ ] コードレビューの実施
- [ ] 品質基準の維持

### Problem(問題点)
- [ ] テスト作成に時間がかかった箇所
- [ ] リファクタリングが不十分な箇所
- [ ] 技術的負債の発生箇所

### Try(次に試すこと)
- [ ] 新しいテスト手法の導入
- [ ] リファクタリングの自動化
- [ ] 品質メトリクスの改善

### 数値指標
- テストカバレッジ: ___%
- ビルド成功率: ___%
- バグ発生率: ___件/イテレーション
- 平均サイクルタイム: ___時間

技術的負債の管理

uml diagram

まとめ

コーディングとテストは、よいソフトウェアを作るための最も基本的で重要な活動です。TDDを中心としたこれらの規律を守ることで:

  1. 品質の作り込み - バグの早期発見と修正
  2. 設計の改善 - 継続的なリファクタリング
  3. 変更容易性 - テストによる安全網
  4. ドキュメント性 - テストが仕様書として機能

これらの実践により、「変更を楽に安全にできて役に立つソフトウェア」の実現を目指します。