Sooey

2017-11-19 01:46:20 +0900

rspec 3.7でOmniAuthのテストモードが失敗するようになった場合の対策。

rspec (rspec-rails) を3.7.xにアップデートしてから、Integration Testing · omniauth/omniauth Wikiにあるような OmniAuth.config.test_mode を使ったFeature specが失敗するようになってしまった場合は、Pumaがclusters modeで動作するように設定されていないか見直すこと。

具体的には、config/puma.rbHerokuの推奨する以下のような設定になっていると、

workers Integer(ENV['WEB_CONCURRENCY'] || 2)

テスト時にOmniAuthのモックがうまく設定されない状態でテストが実行されてしまうようになる。

これを、

if Rails.env.test?
  workers 0
else
  workers Integer(ENV['WEB_CONCURRENCY'] || 2)
end

のようにしてclusters modeでは動作しないようにした状態でテストを実行すると、おそらく従来通りの結果が得られる。

他にも VCR.use_cassette '...' { ... } のようにVCRで囲んだ部分でもリクエストをフックしてモックデータを返すような動作をしなくなるなど、clusters modeにしていることによってハマることがある。

2017-08-29 22:13:38 +0900

PhantomJSからChromeDriverに移行した場合にテストでダイアログのボタンをどのように押すか

Poltergeist + PhantomJSという組み合わせでRailsアプリのFeature specを書いている場合、JavaScriptのalertやconfirmでダイアログが開かれた時には常にOKボタンが押されるようになるため、クリックすると確認のダイアログが開くようなボタンを押すテストは以下のようなコードで実行することができる。

find("#button-with-confirm").click

これを、ChromeDriveとselenium-driverを使ったFeature specに移行した場合、以下のようにCapybaraのaccept_alertを利用して明示的にダイアログのボタンを押すようなコードにする必要がある。

accept_alert do
  find("#button-with-confirm").click
end

2017-07-20 00:23:28 +0900

Rails 5.1の新しいform_withヘルパーはINPUT要素にid属性を設定しない

Rails 5.1で新たに導入された form_with ヘルパーは、これまでの form_for ヘルパーと違って、そのフォームオブジェクトを利用して生成されるDOM要素にid属性やclass属性をできるだけ付けないように設計されているようだ。

したがって、従来の form_for ヘルパーによって以下のようなHTMLが生成されていた場合に、

<label for="title_genre">作品ジャンル</label>
<input type="text" id="title_genre" value="...">

Capybara を使った feature spec 内で、以下のようにLABEL要素とINPUT要素のfor/id属性値による紐付けを利用して、

fill_in '作品ジャンル', with: 'ホラー'

のように指定していた箇所の実行が Capybara::ElementNotFound: Unable to find field "作品ジャンル" で失敗するようになる。

これを回避するためには、新しい form_with ヘルパーのフォームオブジェクトを利用してDOM要素を生成する際に、以下のように明示的にid属性値を指定する必要がある。

<%= form_with(...) do |form| %>
  <%= form.label(:genre) %>
  <%= form.text_field(:genre, id: :title_genre) %>
<% end %>

このあたりの挙動についてはAPIリファレンスのform_withの項目で詳しく解説されている。

また、以下のように label ヘルパーをブロックを渡す形で利用している場合には、生成されるLABEL要素にfor属性が設定される一方でブロック内のINPUT要素などにはid属性値が自動設定されないため、意図した動作(「確認しました」の部分をクリックするとチェックボックスが切り替わる)にならない場合がある。

<%= form.label(:confirmation) do %>
  <%= form.check_box(:confirmation) %> 確認しました
<%= end %>

これを解消するには、今のところ for 属性値を設定しないことを label ヘルパーで明示する必要があるようだ。

<%= form.label(:confirmation, for: nil) do %>
  <%= form.check_box(:confirmation) %> 確認しました
<%= end %>

2016-05-21 23:31:51 +0900

CircleCI上でのRails 5.0.0.rc1 + factorygirlrailsのビルドがPG::UndefinedTableで失敗する件

以下のような構成のRailsアプリをCircleCIでビルドする際、

  • Rails 5.0.0.rc1
  • factory_girl_rails 4.7.0

rake db:create db:schema:loadrake db:create db:structure:loadのフェーズで以下のような例外が発生してビルドが失敗するようになった。

ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  relation "posts" does not exist
LINE 8:                WHERE a.attrelid = '"posts"'::regclass
                                          ^
:               SELECT a.attname, format_type(a.atttypid, a.atttypmod),
                     pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
             (SELECT c.collname FROM pg_collation c, pg_type t
               WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation),
                     col_description(a.attrelid, a.attnum) AS comment
                FROM pg_attribute a LEFT JOIN pg_attrdef d
                  ON a.attrelid = d.adrelid AND a.attnum = d.adnum
               WHERE a.attrelid = '"posts"'::regclass
                 AND a.attnum > 0 AND NOT a.attisdropped
               ORDER BY a.attnum    

スタックトレースやrake db:schema:load fails - CircleCIを見た結果、以下のような流れになっていることがわかった。

  • Railsのロード過程でfactory_girl_rails/railtie.rbが読み込まれる
  • FactoryGirl.find_definitionsが呼び出される
  • spec/factories/*.rbが読み込まれる
  • モデルクラスがロードされる
  • モデルクラスに対応するテーブルがまだ存在しないため例外が発生

Rails 5.0がリリースされるまでの過程で解消するのかもしれないが、ひとまずGemfileではfactory_girl_railsrequireしないようにして、

group :test do
  ...
  gem 'factory_girl_rails', '4.7.0', require: false
  ...
end

そのかわりにspec/spec_helper.rbで明示的にrequireする形にして問題を回避することにした。

#
# factory_girl_rails
#
# To prevent PG::UndefinedTable excetion during `rake db:structure:load` on
# CircleCI build, we don't require the gem in Gemfile.
# Because of this, we need to require it explicitly here.
#
# See: https://circleci.com/docs/ruby-exception-during-schema-load/
#
require "factory_girl_rails"

2016-04-02 17:57:46 +0900

CircleCI上でCapybara, Poltergeist, PhantomJSを利用したテストがMouseEventFailed失敗する問題を解消した。

以下のような構成のRailsアプリをCircleCI上でビルドすると、Capybara::Poltergeist::MouseEventFailedという例外が発生してテストが失敗するケースがあった。

  • Rails 4.2.x
  • RSpec 3.4.x
  • Capybara 2.6.2
  • Poltergeist 1.9.0
  • PhantomJS 2.1.1 (CircleCIのプロジェクト設定でUbuntu 14.04 (Trusty) containerを利用するように設定しておく必要あり)

この時、失敗の原因となっているテストコードは、

click_link '日本語のラベル'

のようなもの。

調査のためにCircleCIにSSHで接続し、テストコードにsave_screenshot('test.png', full: true)を追加&実行して取得した画像ファイルをscpでローカルにダウンロードして確認してみたところ、日本語フォントが無い状態でページがレンダリングされており「日本語のラベル」に該当する要素がページ上では目視できない状態になっていた。

そこで、cricle.ymlに以下のようなエントリを追加してビルド前に日本語フォントがインストールされるようにすることで、テストが無事に成功するようになった。

dependencies:
  pre:
    - sudo apt-get install fonts-migmix