#author("2018-07-05T13:51:00+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

** 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
}}

** 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]

}}

**7.5 Instance Variables Available [#r37ce069]
-また以下のインスタンス変数にもアクセスできるようになる。
--@controller
--@request
--@@response
-サンプル
#pre{{

class ArticlesControllerTest < ActionDispatch::IntegrationTest
  test "should get index" do
    get articles_url
 
    assert_equal "index", @controller.action_name
    assert_equal "application/x-www-form-urlencoded", @request.media_type
    assert_match "Articles", @response.body
  end
end
}}

** 7.6 Setting Headers and CGI variables [#pf1913ac]
-HTTPヘッダーとCGI変数をヘッダーとして設定できる。
#pre{{

# setting an HTTP Header
get articles_url, headers: { "Content-Type": "text/plain" } # simulate the request with custom header
 
# setting a CGI variable
get articles_url, headers: { "HTTP_REFERER": "http://example.com/home" } # simulate the request with custom env variable
}}

**7.7 Testing flash notices [#jb55e33a]
-flashのテスト。
-test_should_create_articleにassertionを追加する。
#pre{{

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{{

def create
  @article = Article.new(article_params)
 
  if @article.save
    flash[:notice] = 'Article was successfully created.'
    redirect_to @article
  else
    render 'new'
  end
end
}}

** 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{{

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{{

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
}}

** 7.9 Test helpers [#g3fa1982]
-重複をさけるためヘルパーが存在。
#pre{{
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
}}

* 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{{
assert_select 'ul.navigation' do
  assert_select 'li.menu_item'
end
}}
-選択された複数の要素は繰り返し呼び出すこともできる。
-例えば4つの要素を持つ二つの順序つきリストを処理する場合、以下のどちらもパスする。
#pre{{

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

*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

}}

*11 Testing Your Mailers [#f2890d53]

**11.1  Keeping the Postman in Check [#tf2a2a3b]
-メーラーもテストする必要がある。
--メールが処理されるか
--メールの内容が正しいか
--正しいタイミングでメールが送信されたか。
-単体テストあるいは機能テストでテストできる。

**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{{

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{{

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{{

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
}}

**12.2 Custom Assertions And Testing Jobs Inside Other Components [#kf361b2c]
-job用のカスタムアサーションが多数存在する。
#pre{{

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.
}}


**2.2 Railsを即座にテスト用に設定する [#qb174f1a]
-プロジェクトを作成すると、以下のフォルダが自動的に作成される。
#pre{{

$ ls -F test
controllers/    helpers/        mailers/        test_helper.rb
fixtures/       integration/    models/
}}

**2.3 フィクスチャのしくみ [#e38e1008]
-フィクスチャ=サンプルデータ。
-YAMLで記述。test/fixtures以下。
#pre{{
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
}}

-YAMLはERBで処理される。
-フィクスチャはActive Recordオブジェクト。
#pre{{
# davidという名前のフィクスチャに対応するUserオブジェクトを返す
users(:david)
 
# idで呼び出されたdavidのプロパティを返す
users(:david).id
 
# Userクラスで利用可能なメソッドにアクセスすることもできる
email(david.girlfriend.email, david.location_tonight)

}}

-フィクスチャとして容易したymlファイルはテストの実行前にテストDBにロードされる。
*3 モデルに対する単体テスト [#c65fc1b9]
-articleモデルを生成。
#pre{{
$ bin/rails generate scaffold article title:string body:text
}}
-以下のようなテストクラスが生成される。
#pre{{

require 'test_helper'
 
class ArticleTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
end
}}

**3.1 テストデータベースのスキーマを管理する [#re44c079]


**3.2 テストを実行する。 [#pfc34540]
-以下のように実行できる。
#pre{{
 bin/rails test test/models/article_test.rb
}}
-特定のメソッドdけをテスト。
#pre{{
 bin/rails test test/models/article_test.rb test_the_truth
}}
-失敗のテスト。article.rbを変更。
#pre{{
class Article < ActiveRecord::Base
  validates :title, presence: true
end
}}
-テスト追加。
#pre{{
class ArticleTest < ActiveSupport::TestCase
  test "the truth" do
    assert true
  end

  test "should not save article without title" do
    article = Article.new
    assert_not article.save, "Saved the article without a title"
  end
end

}}

**3.3 単体テストに含めるべき項目 [#j3ce5ae7]

**3.4 利用可能なアサーション [#g83e6cdb]

**3.5 Rails固有のアサーション [#y9342428]


*4 コントローラの機能テスト [#t3c058fd]
-1つのコントローラーに含まれる複数のアクションをテスト。
-機能テスト=functional test。


**4.1 機能テストに含める項目 [#y660cd93]
-以下の項目を含める
--Webリクエストが成功したか
--正しいページにリダイレクトされたか
--ユーザー認証が成功したか
--レスポンスのテンプレートに正しいオブジェクトが保存されたか
--ビューに表示されたメッセージは適切か
-test/controllers/articles_controller_test.rb
#pre{{
  test "should get new" do
    get new_article_url
    assert_response :success
  end
}}
-showアクションに対するテストの場合。
#pre{{
  test "should show article" do
    get article_url(@article)
    assert_response :success
  end
}}


トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS