Yii单元测试需严格区分unit(纯逻辑)和functional(带应用上下文)场景:unit中Yii::$app默认为null,须在unit.suite.yml启用Yii2模块并正确配置configFile;functional需同时启用Yii2与PhpBrowser模块,且DB、CSRF等配置须完整有效。

Yii 的单元测试不是“写个断言调个方法”就完事——它分清楚 unit(纯逻辑) 和 functional(带应用上下文) 两种场景,混用会直接报 Yii::$app is null 或 Connection is not established。必须按测试目标选对套件、配对模块、加载对配置。
unit 测试里 Yii::$app 总是 null?检查 suite 配置是否启用 Yii2 模块
unit 套件默认不启动 Yii 应用,Yii::$app 就是未初始化的静态属性,直接访问必 null。
实操要点:
- 确认
tests/unit.suite.yml中启用了Yii2模块,且part: [orm]或part: [init]根据需要存在(orm启用 ActiveRecord 支持,init启动完整应用) -
depends: functional在 unit 套件中无效,别写;unit 不继承其他 suite 的模块 -
configFile必须指向一个返回完整yii\web\Application或yii\console\Application实例的 PHP 文件,例如tests/_config/test.php,不能是空数组或片段 - 若只测模型规则、验证逻辑,可不用 Yii2 模块,改用
Asserts+ 手动 new Model +$model->validate()
functional 测试中 $I->amOnPage() 报错:Call to a member function sendRequest() on null
这是典型的模块未正确加载或测试类用错 suite 的表现。functional 测试依赖 Yii2 + PhpBrowser(或 WebDriver),缺一不可。
常见原因与修复:
-
tests/functional.suite.yml里漏了PhpBrowser模块,或url配置为空/非法(如没协议、没端口) -
Yii2模块的configFile指向错误路径,比如写成config/web.php而非测试专用的tests/_config/test.php - 在 unit 测试类里误调
$I->amOnPage()—— 这个方法只在 functional/acceptance suite 的 tester 中可用 - PHP 版本 >= 8.1 时,若
test.php中用了已被废弃的create_function或未声明返回类型,会导致应用构建失败,静默中断
测试带数据库的 Model,$I->grabRecord() 返回 false 或抛 Connection 异常
这不是模型写错了,而是测试环境根本没连上 DB。functional 测试中 DB 连接由 Yii2 模块自动管理,unit 则完全不碰真实 DB(除非你手动 new Connection)。
排查顺序:
- 先确认
tests/_config/test.php是否把 DB 组件正确注册进components,且该配置文件最终返回的是完整应用配置数组 - 检查
config/test/db.php是否存在,并返回有效yii\db\Connection实例(host/user/password/dbname 全要对) - functional 测试中,用
$I->grabRecord(User::class, ['id' => 1])前,确保 DB 已有对应数据——Codeception 不自动迁移,得自己跑yii migrate/up或用Db模块的cleanup: true+populator - unit 测试中别硬连 DB:用
Stub::make(User::class, ['id' => 1])模拟实例,或用Mockery::mock(User::class)拦截find()等方法
submitForm() 提交成功但后端收不到 POST 数据?检查 CSRF 和 Content-Type
Yii 默认开启 CSRF 验证,而 $I->submitForm() 虽能自动提取隐藏字段,但前提是页面已渲染出 —— 这要求 action 返回的视图里真有这个字段。
更常见的坑是请求头和参数格式错位:
- 用
$I->submitForm('form#login', ['username' => 'a', 'password' => 'b'])时,表单 method 必须是POST,且 action 路由对应的方法确实用$request->post()取值 - 若后端用
$request->getBodyParam()或解析 JSON,则必须换$I->sendAjaxPostRequest('/site/login', [...]),并手动加 header:$I->haveHttpHeader('Content-Type', 'application/json') - 测试环境务必关掉 CSRF:在
tests/_config/test.php的components['request']里设'enableCsrfValidation' => false,否则所有 POST 请求都会卡在验证层返回 400
真正容易被忽略的,是 tests/_config/test.php 这个文件——它不是“配一下就行”,而是整个测试上下文的起点。里面任何语法错误、路径错误、组件缺失,都会导致 Yii::$app 构建失败,且错误信息极不直观(常表现为 null reference 或空白异常)。建议每次改完立刻运行 php tests/_config/test.php 看是否能无报错执行并返回数组。











