自动化测试开发 - 让Migration生成数据库镜像 ¶
作者:KK
发表日期:2017.5.27
前提知识 ¶
阅读本文章前请先确保你了解Codeception的Db模块,并且知道如何通过Db模块指定dump.sql在每次测试启动时恢复数据库,相关阅读《PHP-Codeception测试开发 - 验收测试 - 基础 - 自动恢复测试数据》
一般极少甚至不可能会有人专门找这种解决方案,然而如果发现了它的好处的时候,用起来的确是很棒的,能使Codeception单元测试的数据镜像与Yii的migration实现自动同步,大大增加了开发的便利性!
问题的思考 ¶
如何每次运行Migration的时候都生成对应的dump.sql
思路1:执行完migrate控制器后在日志messages里取出SQL保存起来 ¶
这个简单点,通过监听yii\console\controllers\BaseMigrateController::EVENT_AFTER_ACTION
事件来收集所有执行过的SQL:
//配置如下:
$config['controllerMap'] = [
'migrate' => [
'class' => 'yii\console\controllers\MigrateController',
'on afterAction' => function($event){
if($event->action->id != 'up'){
return;
}
$messages = &Yii::getLogger()->messages;
$sqlList = [];
foreach($messages as $message){
if(in_array($message[2], [
'yii\db\Command::query',
'yii\db\Command::execute',
])){
$sqlList[] = $message[0] . ';';
}
}
file_put_contents(Yii::getAlias('@app/tests/codeception/_data/dump.sql'), implode(PHP_EOL . PHP_EOL, $sqlList));
}
],
];
每次调整了数据库结构后,执行migrate/down all
清空数据库,再migrate/up all
然后发现dump.sql被生成了
有了这个dump.sql就可以在启动测试时导入一个与开发数据库结构一样的镜像了!
缺点:当执行的SQL条数有很多的时候,logger的$messages不会全部记下,而且它里面存的也不只是SQL,一切Yii的消息都在里面,在这种情况下多出的一些旧消息会被unset掉,所以在数据库有一定规模的时候无法记录全部的SQL记录
思路2:边执行边记录 ¶
既然执先完migrate/up后保存的messages可能会出现数量过多而丢失消息的情况,那就趁它还没被丢弃就保存下来好了——那就是边执行边记录SQL
这样的话要改造一下了,这里我实施的过程倒是有点难以说清的,涉及的修改代码位置有几个,我先说一下思路:
创建一个
app\ext\DbCommand
类继承yii\db\Command
,接下来重写父类的execute
方法,所做的事情与父类一模一样,但在执行完SQL后加多一个事件触发代码,触发一个afterExecute
的事件(具体自己命名)创建一个
app\ext\MigrationController
类,重写父类的beforeAction
方法,在这个方法里将db的comandClass
的值修改为app\ext\DbCommand
,使得migrate的整个过程创建的command都是新定义的类,于是每次执行SQL就会触发事件了监听事件,还是在
MigrationController::beforeAction
方法里,通过yii\base\Event::on
方法监听app\ext\DbCommand
类的afterExecute
事件,提供一个回调,这个回调就是用于将刚执行完的SQL写到dump.sql里
这个过程有点复杂,多数人还是会看不懂,建议去我的项目xoa里看相关代码,涉及的地方有:
xoa\common\ext\controllers\MigrateController的beforeAction、afterAction和dumpSql方法
server/console/config/config.php的controllerMap配置