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'],
],
];
// 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);
}
}
// 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/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
test[Method]_[Condition]_[Expected],如 testValidate_InvalidEmail_ReturnsFalse。Yii2 模組 cleanup: true 自動事務回滾;SQLite 記憶體資料庫最快。--coverage-xml 檢查。Yii::$app 在單元測試中不可用,需在 Yii2 模組配置中啟用或手動 Yii::setAlias()、new \yii\console\Application()。codecept run unit -c tests/unit.suite.yml --bootstrap=_bootstrap.php 中執行 $this->getModule('Yii2')->_runMigration()。depends 需拓撲排序,避免死循環。Yii 2 + Codeception 提供開箱即用的測試基礎設施。關鍵在於「配置隔離測試環境、善用 Fixtures 管理測試資料、CI 自動化門檻」。將測試視為文檔與重構安全網,持續投資測試覆蓋率,才能在快速迭代中保持代碼品質。