功能测试必须用Codeception的yii2模块,它自动启动Web应用实例、支持完整请求生命周期及CSRF/session处理;禁用CSRF验证、正确配置hostInfo和assetManager是避免404、空白页和资源加载失败的关键。

功能测试必须用 CWebTestCase 或 Codeception 的 yii2 模块
Yii 功能测试本质是模拟真实 HTTP 请求 + 浏览器交互,不能靠纯 PHP 单元测试类(如 CTestCase)完成。直接继承 CWebTestCase 是 Yii 1 的方式;Yii 2/3 推荐用 Codeception 配合 yii2 或 yii3 模块——它会自动启动应用上下文、加载路由、处理 session 和 CSRF,避免手动构造请求时漏掉关键环节。
常见错误现象:$this->tester->amOnPage('/login') 报 404,或页面返回空白但无报错。原因通常是 configFile 指向的配置没返回完整 yii\web\Application 实例,或 urlManager 的 hostInfo 未设为测试域名(如 'http://localhost:8080'),导致生成的链接和实际请求地址不一致。
- Yii 2:确认
tests/unit.suite.yml中启用了yii2模块,且configFile: tests/_config/test.php返回的是可运行的 Web 应用实例 - Yii 3:改用
yii3模块,configFile必须是 DI 容器能加载的配置路径(如config/test/web.php),不是入口脚本 - 别在
tests/_bootstrap.php里手动requireYii 自动加载器——模块已接管,重复加载会触发Cannot redeclare class
表单提交失败大概率是 CSRF 验证没关
功能测试走的是真实请求链路,CSRF token 默认开启。若用 PhpBrowser 或 WebDriver 提交表单,页面虽能加载,但 POST 请求会被拦在中间件层,返回 400 或重定向到登录页。
解决方法是在测试专用配置中显式关闭验证:
// tests/_config/test.php
return [
'components' => [
'request' => [
'enableCsrfValidation' => false,
],
'urlManager' => [
'hostInfo' => 'http://localhost:8080',
],
],
];
- 不关 CSRF 就别指望
$I->submitForm()能成功,哪怕你手动抓取了 token 并传入——Yii 的验证逻辑会校验 session、cookie 和 POST 字段三者一致性,测试环境 session 状态不稳定 - 若必须保留 CSRF(极少见),得用
WebDriver启动真实浏览器,并确保acceptance.suite.yml中chromedriver版本与 Chrome 匹配,否则 JS 不执行,token 不渲染
数据库状态隔离靠事务回滚,不是 truncate
功能测试涉及真实 DB 操作(如注册、下单),但每次测试后必须还原数据。用 TRUNCATE TABLE 清库既慢又破坏外键约束;正确做法是让每个测试用例包裹在事务中,执行完立刻 ROLLBACK。
Yii 2 的 CWebTestCase 默认不启用事务;Codeception 的 yii2 模块需手动开启:
# tests/functional.suite.yml
modules:
enabled:
- Yii2:
transaction: true
cleanup: true
-
transaction: true让每个测试方法运行前开启事务,结束后回滚 -
cleanup: true是保险项,确保即使事务异常也会清空临时数据 - 注意:MySQL 必须用 InnoDB 引擎,MyISAM 不支持事务;SQLite 默认支持,但并发测试慎用
页面元素定位失败别怪 XPath,先查 AssetBundle 是否加载
用 WebDriver 找不到按钮或输入框,90% 不是选择器写错,而是页面根本没加载 JS/CSS 资源。Yii 的 AssetBundle 默认按环境输出不同路径(如 /web/assets/abc123/main.js),测试环境下若 basePath 或 baseUrl 配置不对,资源 404,DOM 树不完整。
检查点:
- 确认
test.php配置中assetManager的basePath指向真实可写的目录(如@app/web/assets),且 Web 服务器有读写权限 - 在测试页面源码里搜
.js或.css,看资源链接是否 404;若是,把assetManager改成forceCopy: true避免符号链接问题 - 别依赖 ID 或 class 做断言——它们可能被 AssetBundle 注入的 JS 动态修改;优先用语义化属性,如
input[name="LoginForm[username]"]
功能测试最耗时间的从来不是写用例,而是调试环境链路:从请求发出、路由匹配、session 初始化、CSRF 校验、Asset 加载,到最终 DOM 渲染。每一步都可能静默失败,只留下一个“页面没反应”的假象。


















