testdouble/cypress-rails と shakacode/cypress-on-rails の実体
CypressというイケイケE2EテストフレームワークをRailsに対向させて試験するための補助ライブラリとして、
という2つの似た名前のライブラリがある。
どっちも利用実績はそれなりにあるのだけど、設計思想が結構違うっぽいので、そのメモ。
2つのライブラリに共通する設計思想
- Railsアプリケーションに、Cypress側からRails側に何かを命令するためのエンドポイントをこっそり追加してHTTPサーバーを起動する
- Cypress側から、テスト開始時など必要なタイミングでHTTPリクエストを送る
- HTTPリクエストを受けて、Ruby側の処理(テストデータの作成・破棄など)を行う
cypress-rails
-
GET /cypress_rails_reset_state
という、テストセッションのリセット処理(個々のtest exampleの境目で呼び出す想定)を定義 https://github.com/testdouble/cypress-rails/blob/v0.5.2/lib/cypress-rails/starts_rails_server.rb#L22- Cypress側では、「beforeEachで呼んでね!」というきまりらしい(自動ではない。ユーザにゆだねている) https://github.com/testdouble/cypress-rails/blob/v0.5.2/README.md#managing-your-test-data
-
サーバーの起動処理は、Capybaraからのコピペのようだ
- 起動時に
/__identity__
というエンドポイントを生やして https://github.com/testdouble/cypress-rails/blob/v0.5.2/lib/cypress-rails/server/middleware.rb#L51 - 起動するまで100ミリ秒のポーリングをする https://github.com/testdouble/cypress-rails/blob/v0.5.2/lib/cypress-rails/server.rb#L82
- この辺のロジックは完全にCapybaraのコピペ
- 起動時に
- 以下の4つのタイミングで、Ruby側の処理をはさむことができる
- サーバーの開始・終了
- 各テストケースのリセット前・リセット後(CypressのbeforeEach)
テストデータの作成・削除について
これがcypress-railsの最大の特徴で、DatabaseCleanerに依存せず、ActiveRecordのtransactional test fixtureをそのまま (ActiveRecord::TestFixturesモジュールをコピペして) 利用している。
-
https://github.com/testdouble/cypress-rails/blob/bb1bf9bb1c9e21749fae228c120d415c5b3ac2e7/lib/cypress-rails/manages_transactions.rb#L58
- ActiveRecordのこのあたりをコピペしてそう https://github.com/rails/rails/blob/v6.1.7.6/activerecord/lib/active_record/test_fixtures.rb#L186
ActiveRecordのバックエンドのDBコネクションを共有することで、トランザクションの中のテストデータも見える、みたいなアレ。 lock_thread = true
などのRailsのシステムテストが利用しているやつが、cypress-railsにはきれいにコピペ実装されている。(動作確認してみた記事がこれ)
GET /cypress_rails_reset_state
が呼ばれた後の、最初にRailsがリクエストを受けたタイミングで、リセット処理が行われる。(呼んだ瞬間ではないので、CypressのbeforeEachのタイミングとは大きくずれることがある!!)
fixturesやFactoryBotでのテストデータ差し込みは after_transaction_start
あたりでやるのがよいだろう。
ただ、CypressのbeforeEachで「リセットしてね!」とHTTPリクエストをする際に、どのテストケースかというメタデータは送られておらず、個々のテストケースで使用するテストデータを変えることはできなさそうだ。
cypress-on-rails
- Railsサーバーはユーザが手で起動する必要がある。
-
RAILS_ENVはtestではなくdevelopment
- CYPRESSという環境変数で使用するデータベースをテスト用に変える
- cypress-railsはRailsアプリケーションをラップするRackアプリケーションをpumaに指定してサーバー起動していたのに対して、cypress-on-railsはRailsアプリケーションに直接 use してミドルウェアを追加しているだけ。
-
RAILS_ENVはtestではなくdevelopment
-
POST /__cypress__/command
という、コマンド注入の口をつくる。- Cypress側から↑にajaxリクエストを必要に応じて投げて、Ruby側の処理結果をJSONで返される。
- https://github.com/shakacode/cypress-on-rails/blob/v1.11.0/lib/cypress_on_rails/middleware.rb#L17
cypress-on-railsは Cypress側からRuby側の処理を呼ぶ という性格が強い。
evalだってすることができる。ワイルドだろぉ〜
テストデータの作成・削除について
基本的には、「これこれのFactoryBot定義をもとにテストデータを作れ」って命令をする。READMEに書いてあるとおりの実装。
cypress-railsのように、トランザクションの中でテストデータを作ったりDBコネクションを共有したり、といったことは一切していない。本当にただFactoryBotでテストデータを実際にDBにコミットするだけ。
なので、
クリーンアップも「データを消してね」って命令をする。実装はDatabaseCleanerのtruncationストラテジ。https://github.com/shakacode/cypress-on-rails/blob/v1.11.0/lib/generators/cypress_on_rails/templates/spec/cypress/app_commands/clean.rb#L3
個々のテストケースごとにテストデータを変えることは難なくできる。
ただし、cypress-railsがトランザクションのロールバックでデータリセットするのに比べて、cypress-on-railsは実際にTRUNCATE TABLEを実行してデータリセットするため、テーブル数が多いアプリケーションでは、とても時間を要するテストになる。
まとめ
cypress-railsは、Railsのシステムテストに近い状態でCypressのテストが動く。
cypress-on-railsは、Railsとしてはテストされているとは思っていない環境(RAILS_ENV=development)でCypressのテストが動く。
Discussion