博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
写Laravel测试代码(1)
阅读量:5977 次
发布时间:2019-06-20

本文共 5743 字,大约阅读时间需要 19 分钟。

hot3.png

本文主要探讨写数据库测试。

写laravel程序时,除了写生产代码,还需要写测试代码。其中,写数据库测试比较麻烦,因为需要针对每一个test case需要建立好数据集,该次test case污染的数据表还需要恢复现场,避免影响下一个test case运行,同时还得保证性能问题,否则随着程序不断膨胀,测试数量也越多,那每一次测试运行需要花费大量时间。

有两个比较好的方法可以提高数据库测试性能:

  1. 对大量的tests按照功能分组。如有1000个tests,可以按照业务功能分组,如group1:1-200, group2:201-800, group3: 801-1000。这样可以并发运行每组测试包裹。

  2. 只恢复每个test case污染的表,而不需要把所有的数据表重新恢复,否则表数量越多测试代码执行越慢。

这里聊下方法2的具体做法。

假设程序有50张表,每次运行测试时首先需要为每组构建好独立的对应数据库,然后创建数据表,最后就是填充测试数据(fixtures)。fixtures可用yml格式定义,既直观也方便维护,如:

#simple.ymlaccounts:  - id: 1    person_id: 2    type: investment    is_included: true  - id: 2    person_id: 2    type: investment    is_included: truetransactions:  - account_id: 1    posted_date: '2017-01-01'    amount: 10000    transaction_category_id: 1     - account_id: 2    posted_date: '2017-01-02'    amount: 10001    transaction_category_id: 2

然后需要写个yamlSeeder class来把数据集填充到临时数据库里:

abstract class YamlSeeder extends \Illuminate\Database\Seeder{    private $files;    public function __construct(array $files)    {        $this->files = $files    }        public function run(array $tables = []): void    {        // Close unique and foreign key constraint        $db = $this->container['db'];        $db->statement('SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;');        $db->statement('SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;');                foreach($this->files as $file) {            ...                        // Convert yaml data to array            $fixtures = \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file));                        ...                        foreach($fixtures as $table => $data) {                // Only seed specified tables, it is important!!!                if ($tables && !in_array($table, $tables, true)) {                    continue;                }                                $db->table($table)->truncate();                if (!$db->table($table)->insert($data)) {                    throw new \RuntimeException('xxx');                }            }                        ...        }                // Open unique and foreign key constraint        $db->statement('SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;');        $db->statement('SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;');    }}class SimpleYamlSeeder extends YamlSeeder{    public function __construct()    {        parent::__construct([database.path('seeds/simple.yml')]);    }}

上面的代码有一个关键处是参数$tables:如果参数是空数组,就把所有数据表数据插入随机数据库里;如果是指定的数据表,只重刷指定的数据表。这样会很大提高数据库测试的性能,因为可以在每一个test case里只需要指定本次测试所污染的数据表。在tests/TestCase.php中可以在setUp()设置数据库重装操作:

abstract class TestCase extends \Illuminate\Foundation\Testing\TestCase    {        protected static $tablesToReseed = [];                public function seed($class = 'DatabaseSeeder', array $tables = []): void        {            $this->artisan('db:seed', ['--class' => $class, '--tables' => implode(',', $tables)]);        }                protected function reseed(): void        {            // TEST_SEEDERS is defined in phpunit.xml, e.g. 
$seeders = env('TEST_SEEDERS') ? explode(',', env('TEST_SEEDERS')) : []; if ($seeders && is_array(static::$tablesToReseed)) { foreach ($seeders as $seeder) { $this->seed($seeder, static::$tablesToReseed); } } \Cache::flush(); static::$tablesToReseed = false; } protected static function reseedInNextTest(array $tables = []): void { static::$tablesToReseed = $tables; } }

这样就可以在每一个test case中定义本次污染的数据表,保证下一个test case在运行前重刷下被污染的数据表,如:

final class AccountControllerTest extends TestCase    {        ...                public function testUpdateAccount()        {            static::reseedInNextTest([Account::class, Transaction::class]);                        ...        }            }

这样会极大提高数据库测试效率,不推荐使用Laravel给出的\Illuminate\Foundation\Testing\DatabaseMigrations 和 \Illuminate\Foundation\Testing\DatabaseTransactions,效率并不高。

laravel的db:seed命令没有--tables这个options,所以需要扩展\Illuminate\Database\Console\Seeds\SeedCommand:

class SeedCommand extends \Illuminate\Database\Console\Seeds\SeedCommand{    public function fire()    {        if (!$this->confirmToProceed()) {            return;        }        $this->resolver->setDefaultConnection($this->getDatabase());        Model::unguarded(function () {            $this->getSeeder()->run($this->getTables());        });    }        protected function getTables()    {        $tables = $this->input->getOption('tables');        return $tables ? explode(',', $tables) : [];    }    protected function getOptions()    {        $options   = parent::getOptions();        $options[] = ['tables', null, InputOption::VALUE_OPTIONAL, 'A comma-separated list of tables to seed, all if left empty'];        return $options;    }}

当然还得写SeedServiceProvider()来覆盖原有的Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::registerSeedCommand()中注册的command.seed,然后在config/app.php中注册:

class SeedServiceProvider extends ServiceProvider{    /**     * Indicates if loading of the provider is deferred.     *     * @var bool     */    protected $defer = true;    /**     * @see \Illuminate\Database\SeedServiceProvider::registerSeedCommand()     */    public function register()    {        $this->app->singleton('command.seed', function ($app) {            return new SeedCommand($app['db']);        });        $this->commands('command.seed');    }    public function provides()    {        return ['command.seed'];    }}

OK,这样所有的工作都做完了。。以后写数据库测试性能会提高很多,大量的test case可以在短时间内运行完毕。

最后,写测试代码是必须的,好处非常多,随着项目程序越来越大,就会深深感觉到写测试是必须的,一劳永逸,值得花时间投资。也是作为一名软件工程师的必备要求。

转载于:https://my.oschina.net/botkenni/blog/1528320

你可能感兴趣的文章
Behavioral模式之Memento模式
查看>>
Work Management Service application in SharePoint 2016
查看>>
Dos 改动IP 地址
查看>>
Laravel 源码解读:php artisan make:auth
查看>>
[译]高性能浏览器网络(第九章)--HTTP简史
查看>>
【转】ionic run android 成功launch success,但是genymotion虚拟机没有显示
查看>>
厚积薄发,看腾讯云如何快速从IPv4向IPv6演进?
查看>>
百度举办第七届技术开放日,揭秘春晚红包技术支撑
查看>>
广发银行运维实践分享:Docker适配传统运维那些事
查看>>
EF Core数据库Provider一览
查看>>
Kafka团队修改KSQL开源许可,怒怼云厂商
查看>>
苹果在GitHub上正式开源iOS内核源码
查看>>
测试人员面临的测试挑战和必备技能
查看>>
使用Flutter之后,我们的CPU占用率降了50%
查看>>
同事反馈环:为什么度量和会议还不够充分
查看>>
[转]十问 Linux 虚拟内存管理 (glibc)
查看>>
老司机带你深入浅出 Collection
查看>>
查询系统-vba
查看>>
[译]Spring Session 与 Spring Security
查看>>
python学习笔记(05)
查看>>