概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Rails Frontend Testing with JavaScript Insights - via @codeship | via @codeship
- 原文公開日: 2018/05/08
- 著者: Daniel P. Clark -- RubyとRustを愛してやまないフリーランス開発者です。
- サイト: https://6ftdan.com/
記事を2本に分割しました。日本語タイトルは内容に即したものにしています。
Railsのフロントエンドのノウハウ#1: システムテスト編(翻訳)
記事の長さ: 8分
Rails 5以降の新しい機能のひとつに「システムテスト」があります。システムテストは、Capybaraを用いたフル装備のフロントエンドテスト機能を提供し、通常のユーザーが体験する完璧なWebエクスペリエンスを提供する本物のブラウザウィンドウを用いて各テストを実行します。この機能がRailsに組み込まれたことでテストがエレガントになり、テストに全力で集中できるようになります。
さらにうれしいことに、標準のRailsジェネレータを用いてモデル名や属性フィールドを生成すると、標準のCRUDシステムテストを自動生成してくれるので、フロントエンドフレームワークの差し替え作業が大幅にシームレスになります。
今回の記事では、前回の記事で用いたRails+VueJSアプリでコード変更が生じたときにシステムテストをすべてパスさせてご覧にいれます。その他に、テストで有用なJavaScriptの知識についてもいくつかご紹介いたします。
“Exploring Rails’ system tests for a seamless frontend experience.” via @6ftdan https://t.co/ktIgTp542z pic.twitter.com/f19GiW4fuW
— CodeShip (@CodeShip) May 10, 2018
フロントエンドアプリを更新する
ここでは、前回の「VueJS Components with CoffeeScript for Rails」記事で使ったアプリを変更します。前回までのソースコードはdanielpclark/vue_exampleでご覧いただけます。
システムテストを実行するには、プロジェクトディレクトリで以下のコマンドを実行します。
rails test:system
訳注: 初めて実行する人はyarnをインストールしたうえで
yarn add rails-erb-loader
を実行しておきましょう。場合によってはyarn install --ignore-engines
の実行も必要かもしれません。
リソースの作成と更新の部分でエラーが2件表示されるはずです。「Unable to find visible field "Body" that is not disabled」というエラーメッセージが表示されます。
上述のエラーはこのフィールドでのみ表示されます。これがこのテストで最初のエラーであるためです。テストの順序を変更すると、作成や更新にあるすべてのフィールドについて同様にテストが失敗します。これはテストするフィールドをCapybaraから探索する方法に関連しているはずです。
訳注: 手元のRuby 2.5.1/Rails 5.2.0環境では、最初だけ上述のエラーが1件表示されましたが、その後エラーは表示されなくなりました。
spring
を止めてやり直しても、やはりエラーは表示されませんでした。
Railsのフォーム系メソッドでラベルや入力フィールドを生成すると、HTMLのlabel
要素にfor
フィールドができます。ここには、対象となるあらゆる入力のid
オブジェクトの名前が含まれます。私たちが独自のVueJSコンポーネントテンプレートを用いて新しいフォームを手作りしたときに、これらのフィールドについてはテストを書いていませんでした。そのため、app/javascript/form-document.vue.erb
ファイル内のフォーム用フィールドを変更する必要があります。
<label>Subject</label>
<input type="text" v-model="document.subject" />
<label>State</label>
<select v-model="document.state">
<%= options_for_select(Document.states.keys, "concept") %>
</select>
<label>Body</label>
<textarea v-model="document.body"></textarea>
上を以下のように変更します。
<label for="document_subject">Subject</label>
<input id="document_subject" type="text" v-model="document.subject" />
<label for="document_state">State</label>
<select id="document_state" v-model="document.state">
<%= options_for_select(Document.states.keys, "concept") %>
</select>
<label for="document_body">Body</label>
<textarea id="document_body" v-model="document.body"></textarea>
CapybaraにSubject
をfill_in
するよう指示を出すと、Capybaraはそのラベルのコンテンツとfor
の値を元に対象を取得し、そのfor
の値で使われているid
を対象とします。この方法のよい点は、それに続く入力フィールドを対象として指定しなくても、このラベルを用いて同じように対象を指定できることです。
(修正後に)rails test:system
を再実行すると、同じエラーメッセージが表示されます。これはRails 5の設定でprotect_from_forgery
がテスト環境で使われていないことに関連するはずで、そのためCSRFトークンが生成されなくなっています。私たちのVueJSコードが失敗したのは、メタ属性フィールドが利用できることを明示的に要求しているためです。
これは以下のいずれかの方法で修正できます。
- 自分のVueJSコードを編集して、CSRFトークンが存在しなくても動作するようにする(上述のアドバイス)。
config/environments/test.rb
ファイルを以下のように変更する。
# Forgery protection
config.action_controller.allow_forgery_protection = true
フロントエンド側のフォーム送信の実装によっては、ApplicationController
に以下のコードがないと動かない可能性もあります。私たちの場合はこれは不要になります。
protect_from_forgery prepend: true
原注: 問題発生の原因を突き止めるには、
RAILS_ENV=test
を指定してrails server
を実行する必要があります。これにより、Documentリソースのnew
やedit
リソースを表示したときにブラウザのコンソールでJavaScriptのエラーが発生してる様子を確認できるようになります。
rails test:system
を実行すると、今度は「Update DocumentボタンやCreate Documentボタンが見つからない」というエラーメッセージに変わります。これはRailsが送信ボタンとして生成したと命名スキーマがフォームヘルパーで使われている可能性があるため、テストを更新して現在のボタン名を反映する必要があります。
test/system/documents_test.rb
を開き、click_on
の対象を"Create Document"
や"Update Document"
から"Submit"
に変更します。これでテストを実行すると、今度は正しいflash通知が結果の中に見当たらないというメッセージが表示されますので、このflashメッセージを追加する必要があります。以下のapp/views/layouts/application.html.erb
アプリケーションテンプレート内の<body>
タグの内側に以下を追加します。
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
続いてコントローラのupdate
アクションやnew
アクションを更新して、適切なflash通知が表示されるようにします。app/controllers/documents_controller.rb
を以下のように変更します。
def create
@document = Document.new(document_params)
respond_to do |format|
if @document.save
flash[:notice] = 'Document was successfully created.'
format.html { redirect_to @document, notice: 'Document was successfully created.' }
format.json { render :show, status: :created, location: @document }
else
format.html { render :new }
format.json { render json: @document.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @document.update(document_params)
flash[:notice] = 'Document was successfully updated.'
format.html { redirect_to @document, notice: 'Document was successfully updated.' }
format.json { render :show, status: :ok, location: @document }
else
format.html { render :edit }
format.json { render json: @document.errors, status: :unprocessable_entity }
end
end
end
これでrails test:system
を実行すると、システムテストがすべてパスするようになります。