最近自分の作っているサービスにActionCableを導入しました。そこでフィーチャスペックを書いていくつかハマったので内容を共有します。
使っているのはcapybara & poltergeistです。
Capybara.serverをpumaにする
Capybaraのデフォルトのサーバは、ざっと見た感じRailsアプリを別スレッドで動かしているだけのようです。これではActionCableを動かすことはできません。ActionCableを利用するにはpumaを使う必要があります。
次のようにすれば Capybara はpumaをサーバとして使ってくれます。
Capybara.server = :puma
これでオーケー…と言いたいところですが、まだ落とし穴が2つほどあります*1。
puma のデフォルトワーカ数(プロセス数)を1にする
pumaはconfig/puma.rb
があると、テスト時でもそれを設定に利用します。developmentやtestの環境ではActionCableのアダプタはasyncが設定されているため、pumaのプロセス数が2以上だとうまく動かないケースがあります。
次のように環境変数が設定されていない場合は1になるようにしておきましょう。
workers ENV.fetch("WEB_CONCURRENCY") { 1 }
ActiveJobのテストと環境を分ける
前提として、ActiveJobのテスト用のアダプタはデフォルトの:test
です。
Capybaraデフォルトのサーバを利用する場合は同一プロセス内でサーバが動くので、サーバ内でキューにジョブを詰めたとき、テストコード内でassert_enqueued_jobs
などのメソッドで実際にキューにジョブが入ったかを確認することができます。しかし、サーバをpumaにした場合は別プロセスになり、サーバ内でキューにジョブを詰めても、テストコード内では確認することができません。
ではActionCableを利用した場合だけサーバをpumaに変えればいいのでは?と思い次のようにしたのですが、フィーチャスペックが不安定になったのでやめました。どうやらserverはdriverのように頻繁に変更するようにはできていないようです(深掘りはしていません)。
config.around(:example, websocket: true) do |example| default_server = Capybara.server Capybara.server = :puma example.run Capybara.server = default_server end
とりあえず次のように、通常のテストはActionCable以外をテストし、ActionCableを使ったテストだけを実行したい場合は環境変数をつけるようにしました。
if ENV['WEBSOCKET'].present? config.filter_run_including :websocket else config.filter_run_excluding :websocket end
これでテストが通るようになりました🍏
*1:僕はきっちりハマりました><