Rails system tests 倒し方
最初にまとめ
Railsでsystem testsを減らすにはロジックをRailsに集中させ、MinitestやRSpecでのユニットテストが書きやすい技術を選定し、開発する
初めに
DHHがXで、自身のプロジェクトであるHEYで359件ものsystem testsをやめて、ControlletでのIntegration Testで代替してるというポストしていました。
意図や具体的にどう進めるかというのもなんとなく理解できるのですが、
自分の理解を整理するためにもRailsでのsystem testsを無くすためにやらないといけないことを言語化してみたのがこの投稿になります。詳しい説明などはこの後書いていきます。
より細かい解説や見解
単体テストがしやすいようにFat Modelにしておく
ActiveModelやPORO(Plain Old Ruby Object)を使ってさまざまなコードをモデルクラスで管理しておくことで単体テストのしやすさがかなり異なります。
これは大前提みたいなところがあるので、解説はこのあたりでやめておきます。
重要なロジックにJavaScriptを使わない
system testsをRailsであまり使わないようにするには、JavaScriptのコードを減らすのがとにかく手っ取り早いです。どうしてもクライアントサイドで計算しなければいけないもの以外はサーバと通信するなりしてサーバーサイドで計算を行う方がRailsとの相性は良いと考えています。
また、Hotwireなnobuildなやり方では厳しいですが、どうしても計算ロジックをフロントエンドで実装しなければならない場合はJSの単体テストも利用すると良いでしょう。
JSもテストしやすいような純粋関数にできるだけしておくなど、Componentのテストを書く以外にもやりようはあるのかなと思っています。
IntegrationTest/RequestSpecを活用する
DHHはIntegrationTestを活用しているということでしたが、RSpecの場合はRequestSpecを利用するのが良いと考えています。Request SpecもIntegerationTestもステータスコードの検証以外にも、レスポンスのHTMLを受け取ることができるので、NokogiriでHTMLの検証を行うようなテスト書けば良いです。
それでも面倒な部分は面倒なのでCapybara::DSLをincludeするなどして、
できるだけテストが書きやすくなる工夫をすると良いでしょう。
config.include Capybara::RSpecMatchers, type: :request
ViewTest, HelperTestを活用する
特にRSpecの場合は、Request Specが登場して以来HelperSpecやViewSpecはあまり推奨されない風潮があるようです。しかし、公式にはしっかりサポートしていますし、サポート終了の匂わせも特に行なっていないようです。
ViewSpec
HelperSpec
Request Specで細かくテストを書くのではなく、HTMLをレンダリングする際に利用しているHelperやViewに対して網羅的にSpecを書くことで動作を担保するケースも取れると思います。
HotwireなプロジェクトでViewやHelperが複雑なのに網羅的にテストが書かれていないこともそれなりにはあるので、Request specがつらい場合はViewSpecやHelperSpecを書いて担保してもよいのかと思いました。
ViewSpecについては下記の記事を参考になります。
ViewComponentを活用する
GitHub謹製のライブラリであるViewComponentを利用しても良いと思います。
GitHubではSystem TestやViewSpec,Helper Specで課題を抱えていたためにこのライブラリを作ったそうです。当然ながら、Componentに対してもSpecが書けますので、同じようにViewのロジックをRailsに持たせながら品質の担保ができそうです。
とにかくRailsとOSSで完結するように
例えばAmazon CloudSearchのような完全なエミュレーターがないようなSaaSやAWSのサービスを使って開発をしてしまうと、実質RSpecを使っての動作チェックはできず、モックを使わざるを得ません。そのためRSpecだけでは完結しない部分もあるためテストの生産性がかなり下がってしまいます。OSSであれば気軽にDockerで動かせるので、RSpecでもテスト可能かと思います。
DHHがPaaSをあまり好ましいと思わないのはこういったテストコスト削減の意図もあるのではないかと勝手に想像しています。
おわりに
先ほどのようにDHHは自身のプロジェクトで300を超えるsystem testをなくしてIntegration Testに頼りながらも、手動でのチェックも合わせて行なっているようです。
しかし、system testsではなくAutifyのようなテストのSaaSを使ったりすることで機械的なテストのメンテコストを幾分下げたりする事ができます。決して安くない値段ですが、このコストでテストコストを下げるのは良い事だとと思います。いくらロジックをバックエンドに寄せたところでブラウザでの処理はゼロにならないので、基本的な正常系テストはsystem testsやそれに相当するテストはあって良いと私は考えています。
Discussion