Divisionの川島(@nazonohito51)です。BASEでは創業当時よりCakePHPによるWebアプリケーション開発を行っており、同時にそのテストも充実させてきました。その過程で気づくのは、CakePHP標準の仕組みだけではテストを増やせば増やすほどテストデータの管理が難しくなり、テストをメンテナンスするのが困難になる問題でした。長期的にサービスを良くしてくれる資産価値の高いテストが書けるように、今回はその問題と向き合い、解決するために@sizuhikoさんの開発されたFabricateというライブラリを導入したお話を書かせていただこうかと思います。BASEの直面した課題CakePHP2のFixtureという仕組み詳細は公式ページを参照していただきたいのですが、CakePHP2にはテストデータを作るためのFixtureという仕組みがあります。Webフレームワークにもあるとは思いますが、事前にデータベースに対してテストに必要なテストデータを入れておくための仕組みです。このFixture、テストの数が少ないうちは良いのですが、テストの数が増えてくると扱いが難しくなる面が出てきます。BASE内部で発生した問題を挙げますと、まずテストメソッド毎に個別のテストデータを用意することがFixtureだと大変なので、複数のテストメソッドで同じFixtureを共有していましたが、後の改修案件の際にテストデータを更新するとたくさんテストが失敗していました。どこがいじって良くて、どこがいじっちゃだめなのか、テストからそれを読み解こうと思っても各テストメソッドにとって本当に必要なカラムがどれなのかを理解することが困難でもはや手を出せません。した改修でも本来なら全然関係ない箇所のテストの修正まで派生し、テストのメンテナンスに大きなコストがかかっていました。Fixtureという仕組みの問題というより、BASEの運用の問題もあったりしますが、本質的にFixtureはテストのスケールに耐えられる仕組みではないと捉えています。正確性には欠けますが、社内にFixtureの問題を理解していただくために簡易的に用意した図が以下となります。違反している状況と捉えています。意図が明確なテストは理解することとメンテナンスすることが容易である、という目的で掲げられている原則BASEにおける原則違反そのテストメソッドが本当に必要としているテストデータをFixtureから読み解きづらいため、何故テストが成功/失敗しているのかその因果関係を読み解くことが出来ない理解することが出来ないため、後から改修することも難しい以上の原則から照らし合わせて、現状のBASEはテストを作ったは良いものの、後から理解し修正することが容易ではない、資産価値の低いテストコードを量産してしまっている状況であると言えます。(資産価値の低いテストコードとは、テストを通して開発コストを下げたかったはずが、逆にメンテナンスするコストが大きいようなテストコードを指しています)これらの問題を解決するには、各テストが独立しやすく、かつ可読性が高くできる新しい仕組みが必要であると考えました。それは後述のFabricateというライブラリが最適であると考えました。Fabricateによる解決Fabricateとはなにかを身も蓋もなく言ってしまえば「RailsのFactoryBotみたいなやつ」と言ってしまったほうが多くの人に伝わりやすいかもしれません。概要を書くなら「テストメソッドごとの差分をオーバーライドしながら実テストデータを生成する」ライブラリです。Laravelにも同様の仕組みがあります。UsageまずはFabricateの1テーブル辺りの定義を書きます。これは実テストデータを作るためのテンプレートのようなものであり、コレ自体は実テストデータではありません。属性のセットないしはその生成方法を定義しておくと、これをベースにFabricateはテストデータを作成してくれます。この作業は必須ではありませんが、なるべくリアルなテストデータを使いたいという理由からBASEでは全テーブル分の定義を書いています。}}そしてテストメソッド内では以下のようにしてテストデータを作成します。Fabricateによるテストデータ生成を行っている箇所となります。このステートメントでFabricateが事前に定義したテンプレートを元にテストデータを作成してデータベースに保存してくれています。これはサンプルなので特に意味のないテストではありますが、その後のアサーションでデータが1件保存されていることを確認しています。ちなみに実際のデータベースを覗くと以下のようなデータが保存されており、テンプレートの定義に沿ったデータが保存されていることが分かります。sec)Fabricateで独立したデータを作成するさて前述の課題のうち、テストが密結合してしまっている課題をテストメソッド個別のテストデータを作ることで解消してみます。Fixtureでもやろうと思えば出来なくもないのですが、Fabricateだととてもかんたんに実現できるという例になります。呼ばれていますが、その第2引数に違いがあります。Fabricateは生成したいテストデータのうち、特定のカラムの値をオーバーライドすることでテストメソッド上から明示的に指定することが出来ます。カラムの値を0として生成しており、testMethod2では1として生成しています。指定していないカラムの値はテンプレートの定義に従って作成されます。このようにFabricateではテストメソッド個別に必要なテストデータを柔軟に用意することが出来ます。違うことがほとんどです。テストメソッドの数を増やせば増やすほどテスト全体として必要とするテストデータのバリエーションは膨大な数になりますが、Fabricateはそのようなバリエーションの違いをテストメソッド上のオーバーライドによって実現し、柔軟なテストデータの供給を実現しています。「必要なカラムだけオーバーライドする」というこの作成方法は後述の可読性にも寄与していると捉えています。2つのカラムをオーバーライドしてテストデータを作成しています。このステートメントは「このテストメソッドではlogin_idとstateが必要であり、その他のカラムの値に関心はない」と読むことが出来ます。仮にこのテストが他のカラムも実は必要としている場合でも(テンプレートの作り方にもよりますが)Fakerによるランダムな値生成により安定したテストとはならないでしょう。このようにテストデータをオーバーライドで柔軟に用意する仕組みは、同時にそのテストに本当に必要なカラムをすべて明示的に指定することをある程度強制する力があると思っています。いう使い方もある程度に捉えていただけましたら幸いです)そしてそれはテストの可読性を上げ、メンテナンスしやすいテストにする効果があると考えています。FactoryBotをご存知のかたは「traitで属性の集合に名前つければいちいちテストメソッドで全部指定しなくて良い」といった指摘もきっとあるかと思いますが、もちろんそういったこともBASEでは行っています。今回はFabricateの詳細な機能まで触れる記事ではないので割愛させていただきます。v1はCakePHP2での利用を想定されたライブラリでした。v2ではフレームワーク非依存を目指し、アダプタを書けばどのフレームワークでも動くことを目指されています。(ただしアダプタはCakePHP3用のもののみが用意されているので、それ以外で動かす場合はアダプタを自作する必要があります)弊社ではこのうちあえてv2を使用しています。いうのも以下の標語が頭をかすめたためです。テストコードのライフサイクルはフレームワークのライフサイクルよりも長い要するにフレームワークのバージョンアップ時にアプリケーションの振る舞いが変わっていないことを確認できるようにテストを書いているのに、バージョンアップと同時に動かなくなるようなテストでは本末転倒だという話です。テストコードはフレームワークとは関係なく動かなくてはなりません。まとめテスト原則と照らし合わせながら現状の課題を整理し、目指す方向を見据えて弊社ではFabricateを導入しました。Fabricateはまだまだ機能があり、実テストを書く上ではそれらも使いこなさないと実際の運用に耐えるテストは書けないのですが、今回はBASEにおいて導入して感じたメリットを端的にまとめる形でこのぐらいで記事は締めさせていただこうかと思います。BASEではサービスを成長させながら同時にテストを充実させてきました。今度はテストをスケールさせる過程で発生する問題をどう解決するかを考えるフェーズに既にあります。今回はテストデータを供給する部分の問題をFabricateというライブラリの導入によって乗りこえようと試みました。導入したてで効果の程は測りかねますが概ねうまく行っている感触です。)の開発チームによるブログです。Groupの大木です。Developerをしている東口です。Groupのマネージャーをしている山田です。12/1から始まっ...Archiveスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。