- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2018-07-03T15:44:47+09:00","default:wikiwriter","wikiwriter")
#author("2018-07-05T13:51:38+09:00","default:wikiwriter","wikiwriter")
&tag(Railsテスティングガイド);
*目次 [#b63ae818]
#contents
*関連ページ [#l728a547]
-[[Rails テスティングガイド | Rails ガイド:https://railsguides.jp/testing.html]]…日本語版サイト
-[[Testing Rails Applications — Ruby on Rails Guides:http://guides.rubyonrails.org/testing.html]]…公式サイト。日本語版は情報が古いのでこちらを参考にすること。
*参考情報 [#z8d31c99]
* 1 Railsアプリケーションでテストを作成しなければならない理由 [#m7e16995]
* 2 テストを導入する [#xe4e9d82]
** 2.1 Rails Sets up for Testing from the Word Go [#j52f7a32]
-Railsプロジェクトを作成すると以下のようなフォルダが作成される
#pre{{
$ ls -F test
controllers/ helpers/ mailers/ system/ test_helper.rb
fixtures/ integration/ models/ application_system_test_case.rb
}}
** 2.2 The Test Environment [#v67cb360]
-testはtest環境で実施される。
-config/database.ymlでテスト用のデータベースが設定できる。
** 2.3 Rails meets Minitest [#ye73ffcd]
-モデルを生成するとテストが自動的に作成される。
#pre{{
$ bin/rails generate model article title:string body:text
...
create app/models/article.rb
create test/models/article_test.rb
create test/fixtures/articles.yml
...
}}
-test/models/article_test.rbは以下のような内容となる。
#pre{{
require 'test_helper'
class ArticleTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
}}
-テストメソッド名は以下のどちらでもよい。
#pre{{
test "the truth" do
assert true
end
def test_the_truth
assert true
end
}}
-失敗テスト。articleのtitleが設定されていないので以下のテストは失敗する。
#pre{{
test "should not save article without title" do
article = Article.new
assert_not article.save
end
}}
-実行。本来失敗してほしいのだが成功してしまう。
#pre{{
$ bin/rails test test/models/article_test.rb
}}
-articleに検証メソッドを追加すると成功する。
#pre{{
class Article < ApplicationRecord
validates :title, presence: true
end
}}
** 2.4 Available Assertions [#fbdc2b74]
**2.5 Rails Specific Assertions [#se6208f2]
**2.6 A Brief Note About Test Cases [#ib167b69]
**2.7 The Rails Test Runner [#seb79b61]
-bin/rails testコマンドで全てのテストを実行できる。
-特定のテストやメソッドを実行する場合。
#pre{{
$ bin/rails test test/models/article_test.rb
$ bin/rails test test/models/article_test.rb -n test_the_truth
$ bin/rails test test/models/article_test.rb:6 #行番号の指定
$ bin/rails test test/controllers #ディレクトリ
}}
*3 The Test Database [#a221eb97]
-テスト用のデータベースが使われる。
-config/database.ymlで定義。
**3.1 Maintaining the test database schema [#cbfed533]
-テストを実行するためには現在のデータがデータベースに反映されていることが必要。
-テストヘルパーは保留中のmigrationsが存在しないあかどうかをチェックする。
**3.2 The Low-Down on Fixtures [#q1f10dde]
-Railsではフィクスチャと呼ばれる仕組みを利用してテストデータを準備っする。
-test/fixturesディレクトリ以下に存在しYAMLで定義。
#pre{{
# lo & behold! I am a YAML comment!
david:
name: David Heinemeier Hansson
birthday: 1979-10-15
profession: Systems development
steve:
name: Steve Ross Kellock
birthday: 1974-09-27
profession: guy with keyboard
}}
-関連の定義。
#pre{{
# In fixtures/categories.yml
about:
name: About
# In fixtures/articles.yml
first:
title: Welcome to Rails!
body: Hello world!
category: about
}}
-fixturesのYAMLではERBを使用することもできる。
-テストの中で以下のように使用可能。
#pre{{
# this will return the User object for the fixture named david
users(:david)
# this will return the property for david called id
users(:david).id
# one can also access methods available on the User class
david = users(:david)
david.call(david.partner)
}}
* 4 Model Testing [#u1bb93a0]
-モデルテストはモデルのテストを実行するもの。
-test/modelsディレクトリに作成する。
#pre{{
$ bin/rails generate test_unit:model article title:string body:text
create test/models/article_test.rb
create test/fixtures/articles.yml
}}
* 5 System Testing [#ubac64da]
-システムテストは、アプリケーションとシステムの相互作用をテストする。
-リアルorヘッドレスブラウザを使用してテスト実行。
-Capybaraを利用する。
-railsではtest/systemディレクトリ以下に作成する。
#pre{{
$ bin/rails generate system_test users
}}
-以下の内容
#pre{{
require "application_system_test_case"
class UsersTest < ApplicationSystemTestCase
# test "visiting the index" do
# visit users_url
#
# assert_selector "h1", text: "Users"
# end
end
}}
-デフォルトでシステムテストはSeleniumドライバで実行される。
** 5.1 Changing the default settings [#e0f1f73e]
-railsのシステムテストの設定は簡単に変更できる。
-例えばSeleniumからPoltergeistに変更したい場合Gemfileにpoltergeistを追加したあとで、テストファイルを以下のように変更する。
#pre{{
require "test_helper"
require "capybara/poltergeist"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :poltergeist
end
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :firefox
end
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :headless_chrome
end
}}
**5.2 Screenshot Helper [#x1e3cf99]
-ScreenshotHelperはスクリーンショットヘルパーを取得するのを助ける。
-take_screenshotとtake_failed_screenshotが存在。
**5.3 Implementing a system test [#u21cd85c]
-システムテストのスケルトンを生成する。
$ bin/rails generate system_test articles
-テストファイルの中身。
#pre{{
require "application_system_test_case"
class ArticlesTest < ApplicationSystemTestCase
test "viewing the index" do
visit articles_path
assert_selector "h1", text: "Articles"
end
end
}}
-テストの実行。デフォルトでrails testではシステムテストを実行しないのに注意。
bin/rails test:system
★chromedriverが存在しないと実行できない。macOSの場合「brew cask install chromedriver」でインストール可能。
-新しい記事を作成するテスト。
#pre{{
test "creating an article" do
visit articles_path
click_on "New Article"
fill_in "Title", with: "Creating an Article"
fill_in "Body", with: "Created this article successfully!"
click_on "Create Article"
assert_text "Creating an Article"
end
}}
-最初にarticles_pathに移動して、"New Article"ボタンをクリック。そしてタイトルとボディを埋めて保存する。
-システムテストはユーザーが利用しているソフトをシステムをテストする実情に最も近いテストとなる。
* 6 Integration Testing [#kb7fa84e]
-統合テストはアプリケーションのさまざまな箇所が相互作用するさまをテストする。
-アプケーション内の重要なワークフローをテストする。
-railsでは統合テストはtest/integrationディレクトリに作成される。
-以下のように作成できる。
#pre{{
$ bin/rails generate integration_test user_flows
}}
-その中身。
#pre{{
require 'test_helper'
class UserFlowsTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
}}
-テストはActionDispatch::IntegrationTestを警鐘する。
** 6.1 Helpers Available for Integration Tests [#r6b1a798]
-ActionDispatch::IntegrationTestでは標準のテストに加え追加のヘルパーが使用できる。
--ActionDispatch::Integration::Runner
--ActionDispatch::Integration::RequestHelpers
--ActionDispatch::Integration::Session
**6.2 Implementing an integration test [#a5bd52de]
-ブログシステムに統合テストを追加する。
$ bin/rails generate integration_test blog_flow
-
-BlogFlowTestを実装する。"/"を取得しh1タグの中身が"Welcome#index"であることを確認。なおこのテストを実行するにはwelcome_controllerを先に作っておかないと行けない(後述)。
#pre{{
require 'test_helper'
class BlogFlowTest < ActionDispatch::IntegrationTest
test "can see the welcome page" do
get "/"
assert_select "h1", "Welcome#index"
end
end
}}
-welcome_controllerの作成。
bundle exec rails generate controller Welcome index
-routes.rbの設定。
#pre{{
Rails.application.routes.draw do
get 'welcome/index'
root 'welcome#index'
resources :articles
end
}}
-記事作成のテスト。
#pre{{
test "can create an article" do
get "/articles/new"
assert_response :success
post "/articles",
params: { article: { title: "can create", body: "article successfully." } }
assert_response :redirect
follow_redirect!
assert_response :success
assert_select "p", "Title:\n can create"
# assert_select "p" do |element|
# # 該当するNokogiri::XML::NodeSetがかえってくる(この場合3つの"p"要素のNokogiri::XML::Elementを含む)
# # textでタグを除いた状態のテキストが取得できる。これとの比較になる。
# p element.text
# end
end
}}
* 7 Functional Tests for Your Controllers [#fa363cc5]
-Railsではコントローラーの個別の(?)メソッドをテストするにはfunctional test(機能テスト)を使用する。
**7.1 What to include in your Functional Tests [#sb6d18bf]
-以下の内容を機能テストに記述する
--リクエストが成功したか
--リダイレクトが正しいか。
--ユーザー認証が成功したか。
--正しいオブジェクトがtemplateに埋め込まれたか。
--適切なメッセージがビューに表示されたか。
-機能テストを作成するにはscaffoldを使用するのが簡単。test/controllers/articles_controller_test.rbがそれ。
$ bin/rails generate scaffold_controller article title:string
-すでにコントローラーを作成済みで、7つのデフォルトアクションに対するテストを作成したい場合以下のように実行する。
$ bin/rails generate test_unit:scaffold article
-テストの一つ。
#pre{{
# articles_controller_test.rb
class ArticlesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get articles_url
assert_response :success
end
end
}}
-getメソッドwebリクエストを送信し、@responseで結果を受ける。以下のパラメータを受け取る。
--URI(例えばarticles_url)。
--params
--headers
--env
--xhr
--as
-showアクションでリファラを設定する場合。
#pre{{
get article_url, params: { id: 12 }, headers: { "HTTP_REFERER" => "http://example.com/home" }
}}
-updateアクションをAjaxリクエストする場合。
#pre{{
patch article_url, params: { id: 12 }, xhr: true
}}
-記事作成メソッドのテスト(この前にテストを実行すると、新たに追加したvalidationにより失敗するとあるがデフォルト状態でも作成できそうだが…)。
#pre{{
test "should create article" do
assert_difference('Article.count') do
post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello Rails' } }
end
assert_redirected_to article_path(Article.last)
end
}}
** 7.2 Available Request Types for Functional Tests [#zfe02798]
-以下のリクエストタイプが使用できる。
--get
--post
--patch
--put
--head
--delete
**2.2 Railsを即座にテスト用に設定する [#qb174f1a]
-プロジェクトを作成すると、以下のフォルダが自動的に作成される。
** 7.3 Testing XHR (AJAX) requests [#xcda9c3b]
-AJAXリクエストをテストするには、「xhr: true」オプションを指定する。
#pre{{
test "ajax request" do
article = articles(:one)
get article_url(article), xhr: true
assert_equal 'hello world', @response.body
assert_equal "text/javascript", @response.content_type
end
}}
$ ls -F test
controllers/ helpers/ mailers/ test_helper.rb
fixtures/ integration/ models/
** 7.4 The Three Hashes of the Apocalypse [#jc03b04f]
-requestが作成されて処理されると、以下の3種類のハッシュが利用できるようになる。
--cookies
--flash
--session
-サンプル
#pre{{
flash["gordon"] flash[:gordon]
session["shmession"] session[:shmession]
cookies["are_good_for_u"] cookies[:are_good_for_u]
}}
**2.3 フィクスチャのしくみ [#e38e1008]
-フィクスチャ=サンプルデータ。
-YAMLで記述。test/fixtures以下。
**7.5 Instance Variables Available [#r37ce069]
-また以下のインスタンス変数にもアクセスできるようになる。
--@controller
--@request
--@@response
-サンプル
#pre{{
david:
name: David Heinemeier Hansson
birthday: 1979-10-15
profession: Systems development
class ArticlesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get articles_url
steve:
name: Steve Ross Kellock
birthday: 1974-09-27
profession: guy with keyboard
assert_equal "index", @controller.action_name
assert_equal "application/x-www-form-urlencoded", @request.media_type
assert_match "Articles", @response.body
end
end
}}
-YAMLはERBで処理される。
-フィクスチャはActive Recordオブジェクト。
** 7.6 Setting Headers and CGI variables [#pf1913ac]
-HTTPヘッダーとCGI変数をヘッダーとして設定できる。
#pre{{
# davidという名前のフィクスチャに対応するUserオブジェクトを返す
users(:david)
# setting an HTTP Header
get articles_url, headers: { "Content-Type": "text/plain" } # simulate the request with custom header
# idで呼び出されたdavidのプロパティを返す
users(:david).id
# Userクラスで利用可能なメソッドにアクセスすることもできる
email(david.girlfriend.email, david.location_tonight)
# setting a CGI variable
get articles_url, headers: { "HTTP_REFERER": "http://example.com/home" } # simulate the request with custom env variable
}}
-フィクスチャとして容易したymlファイルはテストの実行前にテストDBにロードされる。
*3 モデルに対する単体テスト [#c65fc1b9]
-articleモデルを生成。
**7.7 Testing flash notices [#jb55e33a]
-flashのテスト。
-test_should_create_articleにassertionを追加する。
#pre{{
$ bin/rails generate scaffold article title:string body:text
test "should create article" do
assert_difference('Article.count') do
post article_url, params: { article: { title: 'Some title' } }
end
assert_redirected_to article_path(Article.last)
assert_equal 'Article was successfully created.', flash[:notice]
end
}}
-以下のようなテストクラスが生成される。
-テストを実行すると失敗する(が実際は成功した)。
-article.rbを変更する。
#pre{{
require 'test_helper'
def create
@article = Article.new(article_params)
class ArticleTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
if @article.save
flash[:notice] = 'Article was successfully created.'
redirect_to @article
else
render 'new'
end
end
}}
**3.1 テストデータベースのスキーマを管理する [#re44c079]
** 7.8 Putting it together [#nf982660]
-その他のテスト。show
#pre{{
test "should show article" do
article = articles(:one)
get article_url(article)
assert_response :success
end
}}
-destroy
#pre{{
**3.2 テストを実行する。 [#pfc34540]
-以下のように実行できる。
test "should destroy article" do
article = articles(:one)
assert_difference('Article.count', -1) do
delete article_url(article)
end
assert_redirected_to articles_path
end
}}
-update
#pre{{
bin/rails test test/models/article_test.rb
test "should update article" do
article = articles(:one)
patch article_url(article), params: { article: { title: "updated" } }
assert_redirected_to article_path(article)
# Reload association to fetch updated data and assert that title is updated.
article.reload
assert_equal "updated", article.title
end
}}
-特定のメソッドdけをテスト。
** 7.9 Test helpers [#g3fa1982]
-重複をさけるためヘルパーが存在。
#pre{{
bin/rails test test/models/article_test.rb test_the_truth
module SignInHelper
def sign_in_as(user)
post sign_in_url(email: user.email, password: user.password)
end
end
class ActionDispatch::IntegrationTest
include SignInHelper
end
require 'test_helper'
class ProfileControllerTest < ActionDispatch::IntegrationTest
test "should show profile" do
# helper is now reusable from any controller test case
sign_in_as users(:david)
get profile_url
assert_response :success
end
end
}}
-失敗のテスト。article.rbを変更。
* 8 Testing Routes [#l8b5c86a]
-ルートのテストも可能。
-test/controllersなどでテストする。
*9 Testing Views [#j3cc3ffb]
-ビューに対するテストではassert_selectが使用できる。
-assert_select(selector, [equality], [message]) : selectorを通じて選択された要素に対して等しいかどうか。
-assert_select(element, selector, [equality], [message]): elementからはじめてselectorを通じて選択された要素に対してひとしいかどうか。
-titleが文字列を含んでいることを確認する場合
assert_select 'title', "Welcome to Rails Testing Guide"
-nestさせることもできる
#pre{{
class Article < ActiveRecord::Base
validates :title, presence: true
assert_select 'ul.navigation' do
assert_select 'li.menu_item'
end
}}
-テスト追加。
-選択された複数の要素は繰り返し呼び出すこともできる。
-例えば4つの要素を持つ二つの順序つきリストを処理する場合、以下のどちらもパスする。
#pre{{
class ArticleTest < ActiveSupport::TestCase
test "the truth" do
assert true
assert_select "ol" do |elements|
elements.each do |element|
assert_select element, "li", 4
end
end
assert_select "ol" do
assert_select "li", 8
end
}}
-https://github.com/rails/rails-dom-testing/blob/master/lib/rails/dom/testing/assertions/selector_assertions.rb
test "should not save article without title" do
article = Article.new
assert_not article.save, "Saved the article without a title"
*10 Testing Helpers [#y1c50370]
-ヘルパーはシンプルなモジュール。
-ヘルパーに関するtest/helpersディレクトリ以下に置く。
-以下のヘルパーが存在するとき
#pre{{
module UserHelper
def link_to_user(user)
link_to "#{user.first_name} #{user.last_name}", user
end
end
}}
-以下のテストを作成する
#pre{{
class UserHelperTest < ActionView::TestCase
test "should return the user's full name" do
user = users(:david)
assert_dom_equal %{<a href="/user/#{user.id}">David Heinemeier Hansson</a>}, link_to_user(user)
end
}}
**3.3 単体テストに含めるべき項目 [#j3ce5ae7]
*11 Testing Your Mailers [#f2890d53]
**3.4 利用可能なアサーション [#g83e6cdb]
**11.1 Keeping the Postman in Check [#tf2a2a3b]
-メーラーもテストする必要がある。
--メールが処理されるか
--メールの内容が正しいか
--正しいタイミングでメールが送信されたか。
-単体テストあるいは機能テストでテストできる。
**3.5 Rails固有のアサーション [#y9342428]
**11.2 Unit Testing [#bc3ff60e]
-単体テストを使用してメールの結果を比較する。
-メーラーをテストするためにフィクスチャを利用する。UserMailerをテストする場合test/fixtures_user_mailerディレクトリにフィクスチャを保存する。
-UserMailerのテスト
#pre{{
require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
test "invite" do
# Create the email and store it for further assertions
email = UserMailer.create_invite('me@example.com',
'friend@example.com', Time.now)
# Send the email, then test that it got queued
assert_emails 1 do
email.deliver_now
end
# Test the body of the sent email contains what we expect it to
assert_equal ['me@example.com'], email.from
assert_equal ['friend@example.com'], email.to
assert_equal 'You have been invited by me@example.com', email.subject
assert_equal read_fixture('invite').join, email.body.to_s
end
end
}}
-メールを送信しその結果を検証している。
-inviteフィクスチャの中身。
#pre{{
*4 コントローラの機能テスト [#t3c058fd]
-1つのコントローラーに含まれる複数のアクションをテスト。
-機能テスト=functional test。
Hi friend@example.com,
You have been invited.
Cheers!
}}
-config/environments/test.rbのActionMailer::Base.delivery_method = :testによりテストモードが設定され実際はメールが送信されない。
**11.3 Functional Testing [#f1eb5c12]
-functional testでは配信のテストを行う。
#pre{{
**4.1 機能テストに含める項目 [#y660cd93]
-以下の項目を含める
--Webリクエストが成功したか
--正しいページにリダイレクトされたか
--ユーザー認証が成功したか
--レスポンスのテンプレートに正しいオブジェクトが保存されたか
--ビューに表示されたメッセージは適切か
-test/controllers/articles_controller_test.rb
require 'test_helper'
class UserControllerTest < ActionDispatch::IntegrationTest
test "invite friend" do
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post invite_friend_url, params: { email: 'friend@example.com' }
end
invite_email = ActionMailer::Base.deliveries.last
assert_equal "You have been invited by me@example.com", invite_email.subject
assert_equal 'friend@example.com', invite_email.to[0]
assert_match(/Hi friend@example\.com/, invite_email.body.to_s)
end
end
}}
* 12 Testing Jobs [#c9a2d58f]
-ジョブのテストも行う必要がある。
**12.1 A Basic Test Case [#h01f20db]
-jobのテストはtest/job以下に作成される。
#pre{{
test "should get new" do
get new_article_url
assert_response :success
require 'test_helper'
class BillingJobTest < ActiveJob::TestCase
test 'that account is charged' do
BillingJob.perform_now(account, product)
assert account.reload.charged_for?(product)
end
end
}}
-showアクションに対するテストの場合。
**12.2 Custom Assertions And Testing Jobs Inside Other Components [#kf361b2c]
-job用のカスタムアサーションが多数存在する。
#pre{{
test "should show article" do
get article_url(@article)
assert_response :success
require 'test_helper'
class ProductTest < ActiveJob::TestCase
test 'billing job scheduling' do
assert_enqueued_with(job: BillingJob) do
product.charge(account)
end
end
end
}}
*13 Additional Testing Resources [#o362bc7c]
**13.1 Testing Time-Dependent Code [#b9948801]
-時間に関連したためのテストを行うヘルパーメソッドも存在する。
-例えばtravel_to
#pre{{
# Lets say that a user is eligible for gifting a month after they register.
user = User.create(name: 'Gaurish', activation_date: Date.new(2004, 10, 24))
assert_not user.applicable_for_gifting?
travel_to Date.new(2004, 11, 24) do
assert_equal Date.new(2004, 10, 24), user.activation_date # inside the `travel_to` block `Date.current` is mocked
assert user.applicable_for_gifting?
end
assert_equal Date.new(2004, 10, 24), user.activation_date # The change was visible only inside the `travel_to` block.
}}