LLaravel Bootcamp

Day 7チーム開発の作法 — 規約・Gitフロー・レビュー・テスト・秘匿情報

テスト — なぜ書くのか、単体とFeature、CI

リグレッション防止という最大の価値。単体テストとFeatureテストの違いとCI。

なぜテストを書くのか

テストの最大の価値は (リグレッション、後退)の防止です。一度直したバグや作った機能が、後の変更で再び壊れることをリグレッションと呼びます。テストがあれば、変更のたびに自動で『前の機能がまだ動くか』を確認でき、壊したことにすぐ気づけます。

テストは『安心して変更するため』の投資

テストがないコードは、直すたびに『どこか壊していないか』を手で確認する必要があり、変更が怖くなります。テストがあると、変更後にテストを走らせて緑ならOK、赤なら壊した箇所が分かる、という安心が得られます。つまりテストは、将来の変更コストを下げるための投資です。AIに実装を任せる場合でも、テストが『期待する振る舞いの仕様』として振る舞いを固定してくれます。

単体テストとFeatureテスト

  • 単体テスト(): サービスのメソッドなど、小さな部品を単独で検証する。例: 割引計算が正しいか。速くて数を多く書ける。
  • Featureテスト: HTTPリクエストからレスポンスまで、機能を通しで検証する。例: 注文APIを叩くと注文が作られ在庫が減るか。実際の使われ方に近い。
PHP
// Day6ではサービス内の private メソッドだった割引計算を、
// 単体テストしやすいよう public な専用クラス AmountCalculator に切り出す。
// (OrderService はこの AmountCalculator を使う)
class AmountCalculatorTest extends TestCase
{
    public function test_プレミアム会員は10パーセント割引される(): void
    {
        $calculator = new AmountCalculator();
        // premium=true / 単価1000円 / 数量2
        $amount = $calculator->calculate(premium: true, price: 1000, qty: 2);

        // 2000円の10%引きで1800円になること
        $this->assertSame(1800, $amount);
    }
}
単体テスト — 割引計算だけを検証(計算を専用クラスに切り出しておく)
PHP
class OrderApiTest extends TestCase
{
    public function test_注文すると在庫が減り注文が作られる(): void
    {
        $user    = User::factory()->create();
        $product = Product::factory()->create(['stock' => 5]);

        $response = $this->actingAs($user)->post('/orders', [
            'product_id' => $product->id,
            'quantity'   => 2,
        ]);

        $response->assertRedirect();
        // 在庫が2減っていること
        $this->assertSame(3, $product->fresh()->stock);
        // 注文が1件作られていること
        $this->assertDatabaseHas('orders', ['user_id' => $user->id]);
    }
}
Featureテスト — 注文機能を通しで検証

CI — 自動でテストを走らせる

CI(継続的インテグレーション)は、PRが出るたびに自動でテストと整形チェックを走らせる仕組みです。人が走らせ忘れても機械が必ず確認するので、壊れたコードがmainに入るのを防げます。

YAML
name: CI
on: [pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
      - run: composer install --no-interaction
      # 書式チェック(違反があれば失敗)
      - run: ./vendor/bin/pint --test
      # テスト実行(1つでも赤なら失敗し、マージをブロック)
      - run: php artisan test
.github/workflows/ci.yml — PRごとに整形チェックとテストを実行

テストが仕様書になる

良いテストは『このコードはこう振る舞うべき』という仕様を、実行可能な形で示します。テスト名やアサーションを読めば、期待される動きが分かります。ドキュメントと違い、テストは古くなれば失敗して知らせてくれるので、嘘をつかない仕様書として機能します。

面接ではこう言う

「テストの一番の価値はリグレッション防止です。変更のたびに前の機能が壊れていないか自動で確認でき、安心して直せます。細かいロジックは単体テスト、機能全体はFeatureテストで見て、CIでPRごとに自動実行します。テストは実行可能な仕様書でもあるので、AIに実装させる場合でも期待する振る舞いを固定できると考えています」

読んでも腑に落ちない所は、AIに噛み砕いてもらいましょう。

理解度チェック

答えられれば、面接で説明できる状態に一歩近づきます。

Q1

テストを書く最大の価値として最も適切なものは?

Q2

単体テストとFeatureテストの違いとして正しいものは?

Q3

CI(継続的インテグレーション)でテストを走らせる意義を説明してください。

記述式です。まず自分の言葉で答えを考えてから、模範解答を開いて自己採点してください。