patorashのブログ

方向性はまだない

system spec実行時に事前にwebpacker:compileさせる

これは、以下の記事を参考にしていたときの副産物ですが、ちょいちょいハマったところがあるので記事にしておきます。

techracho.bpsinc.jp

ローカルでついうっかりsystem specを行うと、初回アクセス時にwebpacker:compileが実行され、最初のテストがほぼ、タイムアウトに引っかかって失敗するという現象に悩んでいました。 それが上記の記事の途中で出てくる、system specを検出したらテスト実行前にwebpacker:compileが行われるコードを導入しました。

github.com

このprecompile_assets.rbを丸っとコピーして取り込んだところ、初回テストも失敗しないようなりました。快適!!😀 早速PRを作ってpushしたところ、CIでrake taskのテストが落ちました😩どうして…どうして…🐱

現象

標準出力に出てくる文字列の検証を行なっている箇所で二重に出力が行われていました。

テストが落ちる原因

原因は、先ほどのprecompile_assets.rbで行っているRails.application.load_tasksであることは突き止めましたが、これを行わないとRake::Task["webpacker:compile"].executeができないので、これはなんとしてもやりたい…。ちなみにRails.application.load_tasksを何度も呼んでみたところ、標準出力に出てくる文字列が読み込んだ回数だけ増えたので、どうもテストしているrake taskをどこかで重複して読み込んでいるな、というところまでは想像できました。

rake_shared_contextが怪しそう

rake_shared_contextという、rake taskのテストを簡単に書けるようになるgemがあります。

github.com

これを読むと、before(:suite)でプロジェクトのrake taskのファイルを読み込んでいるので、当たってた模様。まぁこれはrake_shared_contextが悪いわけではなくて、私がRails.application.load_tasksを読んでいるのが原因です。ならば、どうすればいいのか?

解決策

Rails.application.load_tasksを行う前に、Rake.Task.clearを呼びます。こうすることで、rake_shared_contextによって登録されたプロジェクトのタスクを一旦破棄します。その後のRails.application.load_tasksで再びrake taskとして登録されるので問題ありません👍

Rake::Task.clear
Rails.application.load_tasks
Rake::Task["webpacker:compile"].execute

まだCIで落ちる

今度は、CIで不可解な現象が起きました。テストは全部通っているのに、終了コードが1になって落ちていました😢しかも、parallel_testsを使っているはずなのに、並列で行われていない…。

以下はCircleCIのログ。

🐢  Precompiling assets.
Finished in 0.27 seconds
..........


🐢  Precompiling assets.
Finished in 0.25 seconds
..........


🐢  Precompiling assets.
Finished in 0.25 seconds
...........


🐢  Precompiling assets.
Finished in 0.26 seconds
......

// 省略。何度も呼ばれる🐢…

🐢  Precompiling assets.
Finished in 0.26 seconds
...................

Finished in 18 minutes 31 seconds
842 examples, 0 failures, 2 pending
--------------------After Queue Hook - run after test suite--------------------
Coverage report generated for rspec_ci_node_1 to /home/circleci/project/tmp/coverage. 6624 / 13479 LOC (49.14%) covered.

Exited with code exit status 1
CircleCI received exit code 1

なんで何度もbefore(:suite)が呼ばれてるのかも不可解だし、2並列で動いてないのもおかしい。

原因

原因は、CIで2並列でprecompile_assets.rbの処理が行われて、webpacker:compileが同時に2並列で動いて、片方がなんらかの原因で落ちていたせいでした。

何度も呼ばれる🐢は、knapsack_proを使っている影響でテストケースを取得しにいってテストを実行するたびにbefore(:suite)が実行されているからでした。そうだったのか…。

対策

CIでは、元々事前にassets:precompileを実行させていたので、そもそも並列でこの処理実行させたくなかったので、CIの時はprecompile_assets.rbの処理をスキップさせるようにしました。

RSpec.configure do |config|
  config.before(:suite) do
    unless ENV['CI']
      # 元々のprecompile_assets.rbの処理をこの中に移動させる
    end
  end
end

これで、ローカルでsystem specを実行する場合は事前にwebpacker:compileを実行してくれるようになり、CIでは何もしないようになりました。