首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

Yii 2 單元測試完整指南:Codeception 整合、Model 驗證測試、資料庫 Fixtures、依賴注入與 CI/CD 整合

     編輯:PHP 綜合知識

Yii 2 官方推薦使用 Codeception 作為測試框架,支援單元測試、功能測試、驗收測試三層。本文以 yii2-app-basic 為例,示範 Model 驗證單元測試、Fixtures 資料管理、依賴注入、資料庫隔離、GitHub Actions CI 整合。

環境安裝與配置

composer create-project --prefer-dist yiisoft/yii2-app-basic yii2-test
cd yii2-test
composer require --dev codeception/codeception codeception/specify codeception/verify
php vendor/bin/codecept bootstrap
# 生成 tests/unit.suite.yml, tests/functional.suite.yml, tests/acceptance.suite.yml
# tests/unit.suite.yml
actor: UnitTester
modules:
    enabled:
        - Asserts
        - Yii2:
            configFile: 'config/test.php'
        - \Helper\Unit
    config:
        Yii2:
            cleanup: true # 每測試自動事務回滾
// config/test.php
return [
    'id' => 'basic-tests',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'sqlite:@runtime/test.db', // 記憶體/檔案 SQLite,隔離測試
        ],
        'mailer' => ['useFileTransport' => true],
        'cache' => ['class' => 'yii\caching\DummyCache'],
    ],
];

Model 與測試案例

// models/EntryForm.php
namespace app\models;
use yii\base\Model;

class EntryForm extends Model {
    public $name;
    public $email;
    public function rules() {
        return [
            [['name', 'email'], 'required'],
            ['email', 'email'],
        ];
    }
}
// tests/unit/models/EntryFormTest.php
namespace tests\unit\models;
use app\models\EntryForm;
use Codeception\Test\Unit;

class EntryFormTest extends Unit {
    public function testValidInput() {
        $model = new EntryForm();
        $model->name = 'Harry Qin';
        $model->email = '15848778@qq.com';
        $this->assertTrue($model->validate());
        return $model; // 供 @depends 使用
    }

    public function testInvalidInput() {
        $model = new EntryForm();
        $model->name = 'Harry Qin';
        $model->email = 'invalid';
        $this->assertFalse($model->validate());
        $this->assertArrayHasKey('email', $model->getErrors());

        $model = new EntryForm();
        $model->name = '';
        $model->email = '15848778@qq.com';
        $this->assertFalse($model->validate());
        $this->assertArrayHasKey('name', $model->getErrors());
    }

    /**
     * @depends testValidInput
     */
    public function testModelProperty(EntryForm $model) {
        $this->assertEquals('Harry Qin', $model->name);
        $this->assertEquals('15848778@qq.com', $model->email);
    }
}

進階:Fixtures 資料庫測試

// tests/unit/fixtures/UserFixture.php
namespace tests\unit\fixtures;
use yii\test\ActiveFixture;

class UserFixture extends ActiveFixture {
    public $modelClass = 'app\models\User';
    public $dataFile = '@tests/unit/fixtures/data/user.php';
    public $depends = []; // 依賴其他 fixture
}
// tests/unit/fixtures/data/user.php
return [
    ['id' => 1, 'username' => 'admin', 'email' => 'admin@example.com', 'auth_key' => 'key1', 'password_hash' => '$2y$13$...'],
    ['id' => 2, 'username' => 'user', 'email' => 'user@example.com', 'auth_key' => 'key2', 'password_hash' => '$2y$13$...'],
];
// tests/unit/models/UserTest.php
namespace tests\unit\models;
use app\models\User;
use Codeception\Test\Unit;

class UserTest extends Unit {
    public function _fixtures() {
        return ['users' => \tests\unit\fixtures\UserFixture::class];
    }

    public function testFindByUsername() {
        $user = User::findByUsername('admin');
        $this->assertNotNull($user);
        $this->assertEquals('admin@example.com', $user->email);
    }
}

執行測試與覆蓋率

# 執行所有單元測試
php vendor/bin/codecept run unit

# 執行特定測試
php vendor/bin/codecept run unit tests/unit/models/EntryFormTest.php

# 產生覆蓋率報告 (需安裝 xdebug/pcov)
php vendor/bin/codecept run unit --coverage --coverage-html=tests/_output/coverage

GitHub Actions CI 整合

# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: yii2_test
        ports: ['3306:3306']
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, intl, pdo_mysql, pcov
          coverage: pcov
      - name: Install dependencies
        run: composer install --prefer-dist --no-progress
      - name: Run unit tests
        run: php vendor/bin/codecept run unit --coverage --coverage-clover=clover.xml
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./clover.xml

最佳實踐清單

  1. 測試命名:test[Method]_[Condition]_[Expected],如 testValidate_InvalidEmail_ReturnsFalse。
  2. 單一斷言:每個測試方法驗證一個行為,失敗易定位。
  3. 資料隔離:Yii2 模組 cleanup: true 自動事務回滾;SQLite 記憶體資料庫最快。
  4. 依賴注入:測試中手動構建 Service,注入 Mock Repository,避免 Service Locator 反模式。
  5. 避免測試實作細節:測試公開 API 行為,不測試私有方法、SQL 語句。
  6. 覆蓋率門檻:核心業務邏輯 > 90%,整體 > 70%,配置 --coverage-xml 檢查。

常見坑點

  • Yii::$app 在單元測試中不可用,需在 Yii2 模組配置中啟用或手動 Yii::setAlias()、new \yii\console\Application()。
  • 資料庫 Migration 未在測試前執行:codecept run unit -c tests/unit.suite.yml --bootstrap=_bootstrap.php 中執行 $this->getModule('Yii2')->_runMigration()。
  • Fixture 循環依賴:depends 需拓撲排序,避免死循環。
  • Yii 2 + Codeception 提供開箱即用的測試基礎設施。關鍵在於「配置隔離測試環境、善用 Fixtures 管理測試資料、CI 自動化門檻」。將測試視為文檔與重構安全網,持續投資測試覆蓋率,才能在快速迭代中保持代碼品質。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved