yii单元测试不可用于控制器和依赖运行时的模型,因其不启动应用导致yii::$app为null;应改用functional测试,它加载完整上下文并支持路由触发、响应断言及fixture数据管理。

Yii 框架里写“单元测试”,别被名字骗了——真想测控制器、模型方法或依赖 Yii 运行时上下文的逻辑,unit 测试模块基本不可用。它不启动应用,Yii::$app 是 null,$this->action 会直接报错。
为什么 unit 测试跑不通控制器和带依赖的模型
Codeception 的 unit 模块默认是纯 PHP 单元测试环境,不加载 Yii 应用栈。哪怕你手动 require 了 bootstrap 文件,Yii::$app 也不会自动初始化,更不会注入 request、response、db 等组件。
- 常见错误现象:
Call to a member function get() on null(调Yii::$app->request报空)、Trying to get property 'id' of non-object($this->user为空) - 适用场景:真正无框架依赖的工具类、独立服务类(比如字符串处理、算法封装)
- 如果硬要用
unit测模型方法,必须手动 mock 所有依赖(ActiveRecord::getDb()、Yii::$app->security等),成本远高于换模块
该用 functional 还是 acceptance?选 functional
functional 是 Yii 官方推荐且最实用的测试入口:它启动轻量级应用实例,加载配置、组件、路由,但不走浏览器驱动,执行快、可控性强。
- 触发 action:
$I->amOnPage('/site/index')或$I->sendAjaxPostRequest('/api/login', ['username'=>'test']) - 验证响应:
$I->seeResponseCodeIs(200)、$I->see('Welcome')、$I->seeCurrentUrlEquals('/site/success') - 重定向不能用
$I->canSeeResponseContainsJson()—— 重定向后响应体是空的,JSON 在跳转前就已返回,得在中间件或 action 内部加日志或改用sendAjax*避免跳转 - CSRF 必须处理:默认开启时,
$I->submitForm()会自动提取_csrf隐藏字段;但$I->sendAjaxPostRequest()不会,要么在测试配置里关掉enableCsrfValidation,要么提前$I->grabCSRF()并手动塞进请求头或参数
模型和数据库逻辑怎么测:用 fixtures + functional
别手写 SQL 插入测试数据。Yii 的 fixtures 支持声明式数据加载,配合 functional 测试能还原真实运行环境。
- 定义 fixture 文件(如
tests/_data/user.php)返回数组,键为表名,值为记录列表 - 在测试中调用:
$this->tester->haveFixtures(['user' => UserFixture::className()]) - 获取 fixture 数据:
$user = $this->tester->grabFixture('user', 0),拿到的是已插入 DB 的 ActiveRecord 实例,可直接调save()、validate()等 - 注意事务隔离:每个 test 方法默认在事务中执行,结束后自动 rollback,不影响其他测试
- 性能影响:fixture 加载本身有开销,但比每次手动
INSERT清晰、安全;若某测试需大量数据,考虑用beforeSuite预热或拆分场景
测试文件放哪、怎么运行:路径和命令别搞错
Codeception 默认约定路径,改了就得同步更新 codeception.yml 配置,否则 codecept run 找不到。
- 文件位置:
tests/functional/SiteControllerCest.php(不是unit目录) - 类命名:必须以
Cest结尾,方法名用test*前缀(如testIndexAction) - 运行命令:
codecept run functional SiteControllerCest:testIndexAction,加--debug可看请求/响应详情 - 容易踩的坑:在
functional测试里用了unit模块的断言(如$I->assertEquals()),其实没问题;但若误配了enabled: [Unit]而非Functional,整个测试就退化成空壳
真正难的不是写断言,而是判断哪段逻辑该在哪一层测:纯计算逻辑扔 unit,带 Yii 上下文的扔 functional,涉及前端交互或跨域才上 acceptance。很多人卡在第一步——没意识到 unit 对 Yii 来说几乎只是个摆设。











