BASEでバックエンドエンジニアをしている翠川です。
以前、@nazonohito51 が 資産価値の高いテストを書くためにFabricateを使い始めました という記事で、Fabricateというライブラリを導入した話を紹介しました。 今回はサービス開発エンジニア側の視点で、Fabricateというライブラリがプロダクトに導入されるまでの流れと、実際に使ってみての感想をお話しさせていただきます。
導入されるまで
まず最初に基盤グループと呼ばれる、言語やフレームワークのアップデートや今後を見据えた技術選定などを行うチームで先行採用され、その中で運用できそうと判断されたところでKibelaで使い方のドキュメント公開、同時に社内ハンズオンが開催されました。
Fabricate導入における一番のネックであろう全モデルのテンプレートもすべて基盤グループ側で用意され、テストデータもできるだけリアルに近いものにするためステージング環境のデータから作成しているなど、時間をかけて新ツール導入へのサポート体制ができあがっていました。
実際にサービスエンジニア側で使ってみた感想
実際に使い始めてみて、以下のようなメリットを感じました。
コードを書く量が減ってテストを書きやすくなった
サンプルとして、CakePHP 2.X Cookbook から以下のテストを例に説明します。 https://book.cakephp.org/2.0/ja/development/testing.html#id21
<?php App::uses('Article', 'Model'); class ArticleTest extends CakeTestCase { public $fixtures = ['app.article']; public function setUp() { parent::setUp(); $this->Article = ClassRegistry::init('Article'); } public function testPublished() { $result = $this->Article->published(['id', 'title']); $expected = [ ['Article' => ['id' => 1, 'title' => 'First Article']), ['Article' => ['id' => 2, 'title' => 'Second Article']), ['Article' => ['id' => 3, 'title' => 'Third Article']) ]; $this->assertEquals($expected, $result); } }
ここで、testPublished()
は公開されている記事一覧を取得するArticle->published()
のテストになります。
Fixtureを使う場合は以下のようにテストデータを用意します。
<?php ... class ArticleFixture extends CakeTestFixture { public $records = [ [ 'id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31' ), [ 'id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31' ], [ 'id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31' ] ]; }
一方、Fabricateを使うとテストデータの用意はこのようになります。
<?php ... Fabricate::create('Article', [ 'id' => 1, 'title' => 'First Article', 'published' => '1' ]); Fabricate::create('Article', [ 'id' => 2, 'title' => 'Second Article', 'published' => '1' ]); Fabricate::create('Article', [ 'id' => 3, 'title' => 'Third Article', 'published' => '1' ]);
テストに必要なパラメータのみの指定となりコードを書く量が大幅に減るので、「ちょっと多めにテストを書いておこうかな」という気持ちになれます。
既存テストに影響しないので安心してテストを追加できるようになった
これまでは、テストデータを毎回全て用意するのが大変なのでFixtureを使い回すことがありましたが、使い回せないときにFixtureの一部を修正や追加したときに他のテストが失敗して辛いことがありました。
たとえば先ほど例として出したArticleFixture
に、他のテストで必要だからとFixtureを追加したとします。
<?php ... // bodyが空の公開テストデータを追加 [ 'id' => 4, 'title' => 'Fourth Article', 'body' => '', 'published' => '1', 'created' => '2019-09-12 00:00:00', 'updated' => '2019-09-12 00:00:00' ]
そうすると先ほどの testPublished()
は、何も手を加えてないにも関わらずテストが失敗してしまいます。
Fabricateを使うことで、テストデータがテスト内で完結するので他のテストを合わせて修正することが無くなります。
もちろん、Fabricateを使わずともテスト内でテストデータを用意することは可能です。しかしその場合は、スキーマに変更が入ったときに各テスト内で定義されたテストデータを全て修正して回らなければならず、大きな修正コストが発生していました。 Fabricateならば、スキーマ変更時の修正が基本的にテンプレート定義の変更だけで済むため修正コストも大幅に少なくなります。
このように既存テストへの影響が少なくなるので、「ここテストが足りていない気がするので追加しておこうかな」という気持ちになれます。
テストコードの意図が明確なのでコードレビューの負荷が減った
弊社では、リリースされるコードはテストを含めて全てコードレビューを行っています。
これまではテストデータから本当に必要な情報がどれか分からず、テストを理解しづらいことがありました。 Fabricateでは、テストデータ生成時に指定されたカラムを見ることでそのテストの意図が分かるようになります。逆に、それ以外はそのテストでは関心が無いことがわかるので、見るべき範囲も限定されるようになります。
先ほどの例を見てみると、このテストでは body や created はチェックしていない(から気にしなくていい)ことがテストデータを見るだけで分かります。
<?php ... Fabricate::create('Article', [ 'id' => 1, 'title' => 'First Article', 'published' => '1' ]);
このようにテストの意図が明確になるので、「テストコードの中身までもう少し詳しく見ておこうかな」という気持ちになれます。
最後に
テストコードの書き方が変わるという比較的大きな変更のあるツール導入でしたが、
- 強制移行ではなく『従来のFixtureも残しつつこちらを推奨』という形での導入だったこと
- Kibelaで使い方ドキュメント公開・社内ハンズオン開催と、新ツール導入へのサポート体制がしっかりしていたこと
から混乱はありませんでした。 その結果、新しい取り組みでしたがすでに浸透しており、みんな新しくテストを書くときはほぼ全てFabricateを使っています。
BASEではフロントエンド、バックエンドを問わず中長期的な技術基盤の改善を担っていくエンジニア、およびその中でMove Fastにプロダクトをリリースしていくエンジニアを募集しています。