Yii2的Fixture类需定义在tests/fixtures/目录下,类名与文件名一致(如UserFixture.php),命名空间为testsixtures,继承ActiveFixture或Fixture,并通过$tableName指定表名;数据文件须置于tests/fixtures/data/下,以return [ ... ];返回数组。

fixture类怎么定义才能被Yii2自动加载
Yii2的yii estFixture机制依赖命名和路径约定,不是随便放个PHP文件就能用。核心是类名必须匹配文件名,且继承ActiveFixture或Fixture,同时放在tests/fixtures/目录下(默认路径)。
- 类名必须是
UserFixture,对应文件名必须是UserFixture.php - 命名空间需为
testsixtures(若改了配置,得同步更新config/test.php里的'fixturePath' => '@tests/fixtures') - 如果表名不是类名小写复数(如
UserFixture→user),必须显式设置$tableName = 'tbl_user' - 别在fixture里写业务逻辑——它只负责数据准备和清理,
load()和unload()由框架调用,不建议手动触发
数据文件放哪、格式怎么写才不报错
默认用PHP数组返回测试数据,文件必须放在tests/fixtures/data/下,且文件名要和fixture类名一致(如UserFixture对应tests/fixtures/data/user.php)。常见错误是路径错、扩展名错、返回值不是数组。
- 文件必须以
return [ ... ];结尾,不能有echo、var_dump或多余输出 - 键名对应数据库字段名,值支持标量、
null、甚至new DateTime()(但要注意时区和序列化兼容性) - 外键关联数据不会自动解析——比如
profile_id字段写1,就得确保ProfileFixture已加载且第1条数据存在 - 避免用
YAML或JSON:虽然Yii2支持,但PHP数组更直观、调试友好,也避开了解析失败的静默问题
测试里怎么正确加载多个fixture并控制顺序
顺序决定外键约束是否通过。Yii2按数组索引顺序加载fixture,但depends属性才是关键——它强制前置依赖,比数组顺序更可靠。
- 在fixture类中声明
public $depends = ['tests\fixtures\ProfileFixture'];,比在测试里写['ProfileFixture', 'UserFixture']更安全 - 测试方法里用
public $fixtures = ['users' => UserFixture::class];即可注入,变量名$users会自动指向fixture实例 - 如果需要部分加载(比如只清空某张表),别直接删数据库——用
unload()配合$this->dropTable('{{%user}}');容易破坏事务隔离;应改用ActiveFixture::unload()的默认行为(truncate或delete) - 注意:MySQL严格模式下,若fixture数据违反NOT NULL或CHECK约束,
load()会直接抛IntegrityConstraintViolationException,而不是跳过
为什么测试跑完数据没清掉,或者第二次跑就报主键冲突
根本原因是fixture的生命周期没被正确纳入测试事务,或数据库连接没走测试专用配置。
- 检查
tests/config/test.php里'db'组件是否指向测试专用DB(如mysql:host=localhost;dbname=test_yii2),而非开发库 - 确保测试类继承
yiicodeceptionTestCase或yii estActiveFixtureTestCase,它们会自动开启/回滚事务;纯PHPUnitFrameworkTestCase不会 -
ActiveFixture默认用TRUNCATE TABLE清表,但SQLite不支持TRUNCATE,会退化为DELETE——此时自增ID不会重置,导致主键冲突,得手动DELETE FROM sqlite_sequence WHERE name='user'; - 如果用了
DbFixture(非ActiveFixture),它不自动处理外键约束,容易因删除顺序错引发ForeignKeyConstraintViolationException
最常被忽略的是测试数据库权限——fixture执行TRUNCATE需要DROP权限,而很多CI环境只给了SELECT/INSERT/UPDATE/DELETE。遇到“access denied”却没报具体SQL,先查MySQL用户权限。










