无法通过Codeception保存新ActiveRecord记录,请求技术协助
解决Yii2单元测试中BlamemableBehavior导致created_by不能为空的问题
我之前在做Yii2单元测试时也踩过这个一模一样的坑!咱们来一步步拆解问题,找到解决办法:
问题根源
BlamemableBehavior的核心逻辑是从Yii::$app->user获取当前登录用户ID来填充created_by字段,但单元测试环境默认是没有登录用户的,所以行为会把created_by设为null,而你的数据库字段又设置了非空约束,就触发了IntegrityException。
你说手动传值和解绑行为没用,大概率是操作时机不对或者方法没到位,下面给你几个靠谱的解决方案:
方案1:测试时模拟登录用户(最推荐)
这是最贴近真实业务场景的做法,让BlamemableBehavior能正常工作:
在你的测试方法里,先登录一个测试用户,再创建订单:
// 假设你有一个预先创建好的测试用户,ID为1 $testUser = \frontend\models\User::findOne(1); // 模拟登录 Yii::$app->user->login($testUser); // 接下来创建订单,created_by会被行为自动填充 $order = new \frontend\models\Order(); $order->attributes = [ // 其他字段赋值 'order_no' => 'TEST_001', 'amount' => 99.99, ]; $order->save();
这样行为能正常获取到用户ID,就不会出现null的情况了。
方案2:正确解绑BlamemableBehavior
如果你确实需要手动控制created_by的值,要注意解绑行为的时机——必须在赋值和保存之前解绑:
$order = new \frontend\models\Order(); // 解绑行为,注意行为的名称默认是'blameable',如果你的模型里自定义了名称要对应修改 $order->detachBehavior('blameable'); // 手动设置created_by $order->created_by = 1; // 其他字段赋值 $order->order_no = 'TEST_002'; $order->amount = 199.99; $order->save();
要是你之前是先赋值再解绑,那行为已经在beforeSave阶段触发过了,自然没用。
方案3:测试环境下临时禁用行为
可以在模型的behaviors()方法里判断当前环境,测试环境不注册BlamemableBehavior:
use yii\behaviors\BlamemableBehavior; class Order extends \yii\db\ActiveRecord { // ...其他代码 public function behaviors() { $behaviors = parent::behaviors(); // 非测试环境才加载BlamemableBehavior if (!YII_ENV_TEST) { $behaviors['blameable'] = [ 'class' => BlamemableBehavior::class, 'createdByAttribute' => 'created_by', // 其他配置项 ]; } return $behaviors; } }
这样测试时模型不会加载这个行为,你可以完全手动控制created_by的值。
额外检查点
- 确认模型的
rules()里给created_by加上了safe规则,确保可以手动赋值:public function rules() { return [ // ...其他规则 [['created_by'], 'integer'], [['created_by'], 'safe'], ]; } - 检查数据库表的
created_by字段,确认没有设置错误的非空约束(当然如果业务需要非空,那前面的方案必须落实)。
内容的提问来源于stack exchange,提问作者user3102290
相关产品推荐
相关产品推荐

