iT邦幫忙

2025 iThome 鐵人賽

DAY 30
0

前言:終點即起點

還記得 30 天前,我們從一個簡單的 expect(1 + 1)->toBe(2) 開始嗎?

今天,我們不僅完成了完整的 Todo API,更重要的是建立了 TDD 思維。這趟旅程的終點,正是你成為更好開發者的起點。

本日目標 🎯

今天我們要完成最後一塊拼圖:

  • 將 Todo API 部署到雲端
  • 設置 CI/CD 確保測試品質
  • 回顧 30 天的學習成果
  • 展望 TDD 的未來之路

一、30 天學習地圖回顧 🗺️

開始 → 部署前檢查 → CI/CD 設置 → 生產環境測試
  ↓        ↓           ↓            ↓
Day 1   Day 10      Day 20       Day 30
[基礎]  [進階]      [實戰]       [部署] ← 你在這裡!

進度:[████████████████████] 100% 🎉

二、部署前的最終檢查 ✅

在部署之前,讓我們確保所有測試都能順利通過:

# 執行所有測試
php artisan test

# 執行特定測試套件
php artisan test --testsuite=Feature
php artisan test --testsuite=Unit

# 生成測試覆蓋率報告
php artisan test --coverage --min=80

建立 tests/Feature/Day30/DeploymentReadinessTest.php

<?php

namespace Tests\Feature\Day30;

use Tests\TestCase;
use Illuminate\Support\Facades\Artisan;

it('passes all unit tests', function () {
    $result = Artisan::call('test', [
        '--testsuite' => 'Unit',
        '--stop-on-failure' => true
    ]);
    
    expect($result)->toBe(0);
});

it('passes all feature tests', function () {
    $result = Artisan::call('test', [
        '--testsuite' => 'Feature',
        '--stop-on-failure' => true
    ]);
    
    expect($result)->toBe(0);
});

it('has minimum test coverage', function () {
    $output = shell_exec('php artisan test --coverage --min=80 2>&1');
    
    expect($output)->not->toContain('Code coverage below');
});

it('has no pending migrations', function () {
    $result = Artisan::call('migrate:status');
    $output = Artisan::output();
    
    expect($output)->not->toContain('Pending');
});

it('can compile assets without errors', function () {
    $result = shell_exec('npm run build 2>&1');
    
    expect($result)->toContain('built in');
    expect($result)->not->toContain('ERROR');
});

三、CI/CD 管線設置 🔄

建立 .github/workflows/laravel.yml

name: Laravel CI/CD

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: laravel_test
    steps:
    - uses: actions/checkout@v3
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        coverage: xdebug
    - name: Install Dependencies
      run: |
        composer install -q --no-ansi --no-interaction
        php artisan key:generate
    - name: Run Tests
      run: php artisan test --coverage --min=80
    - name: Deploy to Production
      if: github.ref == 'refs/heads/main'
      run: |
        # Deployment commands here
        php artisan config:cache
        php artisan route:cache

四、部署到雲端 ☁️

使用 Laravel Forge 或其他平台部署

# 準備部署腳本
#!/bin/bash
cd /home/forge/todo-api
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan queue:restart

五、部署後的健康檢查 💡

建立 tests/Feature/Day30/ProductionSmokeTest.php

<?php

namespace Tests\Feature\Day30;

use Tests\TestCase;

it('validates critical endpoints', function () {
    $endpoints = [
        '/api/todos' => 200,
        '/api/health' => 200,
        '/api/invalid' => 404
    ];
    
    foreach ($endpoints as $endpoint => $expectedStatus) {
        $response = $this->get($endpoint);
        expect($response->status())->toBe($expectedStatus);
    }
});

it('monitors response time', function () {
    $start = microtime(true);
    $response = $this->get('/');
    $duration = (microtime(true) - $start) * 1000;
    
    expect($response->status())->toBe(200);
    expect($duration)->toBeLessThan(500);
});

五、部署後的健康檢查 💡

建立 tests/Feature/Day30/ConfigurationTest.php

<?php

namespace Tests\Feature\Day30;

use Tests\TestCase;

it('has correct environment configuration', function () {
    $requiredEnvVars = [
        'APP_NAME', 'APP_ENV', 'APP_KEY', 'APP_URL',
        'DB_CONNECTION', 'DB_HOST', 'DB_PORT', 'DB_DATABASE',
    ];
    
    foreach ($requiredEnvVars as $var) {
        expect(env($var))->not->toBeNull();
    }
});

it('uses secure settings in production', function () {
    if (app()->environment('production')) {
        expect(config('app.debug'))->toBeFalse();
        expect(config('app.url'))->toStartWith('https://');
        expect(config('session.secure'))->toBeTrue();
    }
});

六、監控部署狀態 📈

效能監控

// tests/Feature/Day30/PerformanceTest.php
<?php

namespace Tests\Feature\Day30;

use Tests\TestCase;
use Illuminate\Support\Facades\DB;

it('keeps database queries under threshold', function () {
    DB::enableQueryLog();
    $this->get('/api/todos');
    $queryCount = count(DB::getQueryLog());
    
    expect($queryCount)->toBeLessThanOrEqual(10);
});

it('maintains acceptable memory usage', function () {
    $startMemory = memory_get_usage();
    $this->post('/api/todos', ['title' => 'Test Todo']);
    $memoryUsed = (memory_get_usage() - $startMemory) / 1024 / 1024;
    
    expect($memoryUsed)->toBeLessThan(10);
});

建立 tests/Feature/Day30/ErrorHandlingTest.php

<?php

namespace Tests\Feature\Day30;

use Tests\TestCase;
use Illuminate\Support\Facades\Log;

it('logs errors correctly', function () {
    Log::shouldReceive('error')
        ->once()
        ->with('Application error', \Mockery::any());
    
    // Trigger error logging
    Log::error('Application error', ['test' => true]);
});

it('returns appropriate error responses', function () {
    $response = $this->getJson('/api/invalid-endpoint');
    
    expect($response->status())->toBe(404);
});

七、30 天學習成果總覽 🏆

讓我們回顧這 30 天的學習旅程:

第一週:基礎建立

  • ✅ Day 1-3:測試基礎與斷言
  • ✅ Day 4-7:測試組織與替身

第二週:進階概念

  • ✅ Day 8-10:Mocking 與隔離測試
  • ✅ Day 11-14:實戰演練與應用

第三週:深度實踐

  • ✅ Day 15-17:進階實作與重構
  • ✅ Day 18-21:Laravel 特色功能測試

第四週:整合應用

  • ✅ Day 22-27:完整應用開發
  • ✅ Day 28-29:前後端整合
  • ✅ Day 30:部署與總結

八、關鍵學習里程碑 🌟

# 執行測試並產生覆蓋率報告
php artisan test --coverage --min=80
php artisan test --coverage-html=coverage

九、TDD 帶來的改變 💡

團隊協作最佳實踐

  1. 每次提交前執行測試
  2. 保持高測試覆蓋率
  3. 定期重構測試程式碼
  4. 分享測試經驗

恭喜你完成了這 30 天的 TDD 學習之旅!你現在已經具備了:

技術能力清單

  • ✅ 紮實的 TDD 開發流程
  • ✅ Pest 測試框架精通
  • ✅ Laravel 特色測試技巧
  • ✅ API 測試完整知識
  • ✅ CI/CD 自動化部署

實戰成果

  • 🏆 完整的實戰專案
  • 🏆 功能完善的應用程式
  • 🏆 高測試覆蓋率的程式碼
  • 🏆 可部署的生產級應用

總結:TDD 之旅的延續

今天我們完成了整個 TDD 學習旅程的最後一哩路!從部署前的檢查、CI/CD 設置,到生產環境的監控,我們建立了一個完整的測試驅動開發流程。

重要觀念回顧

  1. 部署即信心:完整的測試給予部署的勇氣
  2. 自動化一切:CI/CD 確保品質一致性
  3. 持續監控:生產環境也需要測試保護
  4. 團隊文化:TDD 不只是技術,更是團隊文化

最後的話

30 天前,我們從一個簡單的測試開始;30 天後,我們擁有了一個經過完整測試、可以自信部署的應用程式。TDD 不僅改變了我們寫程式的方式,更重要的是改變了我們思考問題的角度。

感謝你陪伴我走完這 30 天的旅程。願 TDD 成為你開發路上的明燈!

Happy Testing, Happy Coding! 🚀


這是 iThome 鐵人賽「Laravel Pest TDD 實戰」系列的最後一篇文章。希望這 30 天的內容對你有所幫助。歡迎在實際專案中應用所學,並持續精進你的測試技能!


上一篇
Day 29 - 整合實戰
系列文
Laravel Pest TDD 實戰:從零開始的測試驅動開發30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言