アイミツ開発チームのテスト実行時間を1/2にしました
アイミツ開発チームでエンジニアリングしてる deliku と言います。
私の自己紹介やユニラボでどんなことをしているかは、下記の記事を読んでいただければと思います!
開発サイクルを高速にまわすうえで、CIの時間効率化はどこの現場も悩みの種ではないでしょうか?結論はタイトルの通りですが、今回はアイミツ開発チームで取り組んだ施策について話したいと思います。
Seeder依存したテストケースをFactory利用へ。
アンチパターンですが、開発当初はテストを行うためにSeederにデータを追加するということを行なっており、下記のデメリットがありました。(途中からはFactory利用にチームの方針を変えました)
failedになっているテストがSeederに依存したテストケース数です。
これらを全てFactory利用するように愚直に修正していきました...
また全てのSeeder利用をやめるのではなく、マスタデータのSeederは引き続き利用するようにしています。(マスタデータをFactoryで生成することは可能ですがコードの記述量が増えるため)
RefreshDatabaseを利用し、テスト開始初回時にmigrate および マスタデータSeederを実行する。
下記のように記述することで、最初のテストケースのタイミングで、migrate:freshと、特定のSeederを実行してくれます。
さらに、テストメソッド単位でトランザクション処理になるため、他テストケースとのデータ依存がなくなります。
詳しくは、https://readouble.com/laravel/8.x/ja/database-testing.html を確認いただくのと、RefreshDatabaseトレイトの処理を読み解くと良いと思います。
<?php
namespace Tests;
use Database\Seeders\TestSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
use RefreshDatabase;
protected $seeder = TestSeeder::class;
}
今回のケースではボトルネックが大量のデータをSeederで投入していたため、この方法で実行時間を短縮することができました。
処理時間のBefore / After
下記図の通りです。(実際には上述の通り、php artisan test を実行するだけで、migrate:fresh と Seederが処理が行われますが、比較しやすいようにしています)
テスト並列実行が可能に。
さらなる恩恵としてテストケースが互いに疎結合になったので、並列処理も可能になりました。ローカル環境で試してみたのですのですが、処理時間の改善にはつながりませんでした。(むしろパフォーマンスが下がる結果になりました)こちらについてはまた時間をみつけてボトルネックを調査したいなと考えています。
また、トラーナさんの記事にあるように、
GithubActionsでも並列実行ができるようですので、
こちらもいずれ試していきたいです。
終わりに
【宣伝】ユニラボ に興味がある方へ
今回の記事を読んでユニラボに興味を持っていただけた方は、
まずはカジュアル面談でざっくりお話させていただければと思います!