Tag: Railsテスティングガイド

目次

関連ページ

参考情報

1 Railsアプリケーションでテストを作成しなければならない理由

2 テストを導入する

2.1 Rails Sets up for Testing from the Word Go

  • Railsプロジェクトを作成すると以下のようなフォルダが作成される
    $ ls -F test
    controllers/           helpers/               mailers/               system/                test_helper.rb
    fixtures/              integration/           models/                application_system_test_case.rb
    

2.2 The Test Environment

  • testはtest環境で実施される。
  • config/database.ymlでテスト用のデータベースが設定できる。

2.3 Rails meets Minitest

  • モデルを生成するとテストが自動的に作成される。
    $ 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は以下のような内容となる。
    require 'test_helper'
     
    class ArticleTest < ActiveSupport::TestCase
      # test "the truth" do
      #   assert true
      # end
    end
    
  • テストメソッド名は以下のどちらでもよい。
    test "the truth" do
      assert true
    end
    
    
    def test_the_truth
      assert true
    end
    
  • 失敗テスト。articleのtitleが設定されていないので以下のテストは失敗する。
    test "should not save article without title" do
      article = Article.new
      assert_not article.save
    end
    
  • 実行。本来失敗してほしいのだが成功してしまう。
    $ bin/rails test test/models/article_test.rb
    
  • articleに検証メソッドを追加すると成功する。
    class Article < ApplicationRecord
      validates :title, presence: true
    end
    

2.4 Available Assertions

2.5 Rails Specific Assertions

2.6 A Brief Note About Test Cases

2.7 The Rails Test Runner

  • bin/rails testコマンドで全てのテストを実行できる。
  • 特定のテストやメソッドを実行する場合。
    $ 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

  • テスト用のデータベースが使われる。
  • config/database.ymlで定義。

3.1 Maintaining the test database schema

  • テストを実行するためには現在のデータがデータベースに反映されていることが必要。
  • テストヘルパーは保留中のmigrationsが存在しないあかどうかをチェックする。

3.2 The Low-Down on Fixtures

  • Railsではフィクスチャと呼ばれる仕組みを利用してテストデータを準備っする。
  • test/fixturesディレクトリ以下に存在しYAMLで定義。
    # 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
    
  • 関連の定義。
    # In fixtures/categories.yml
    about:
      name: About
     
    # In fixtures/articles.yml
    first:
      title: Welcome to Rails!
      body: Hello world!
      category: about
    
  • fixturesのYAMLではERBを使用することもできる。
  • テストの中で以下のように使用可能。
    # 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

  • モデルテストはモデルのテストを実行するもの。
  • test/modelsディレクトリに作成する。
    $ 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

  • システムテストは、アプリケーションとシステムの相互作用をテストする。
  • リアルorヘッドレスブラウザを使用してテスト実行。
  • Capybaraを利用する。
  • railsではtest/systemディレクトリ以下に作成する。
    $ bin/rails generate system_test users
    
  • 以下の内容
    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

  • railsのシステムテストの設定は簡単に変更できる。
  • 例えばSeleniumからPoltergeistに変更したい場合Gemfileにpoltergeistを追加したあとで、テストファイルを以下のように変更する。
    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

  • ScreenshotHelperはスクリーンショットヘルパーを取得するのを助ける。
  • take_screenshotとtake_failed_screenshotが存在。

5.3 Implementing a system test

  • システムテストのスケルトンを生成する。
    $ bin/rails generate system_test articles
  • テストファイルの中身。
    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」でインストール可能。

  • 新しい記事を作成するテスト。
    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

  • 統合テストはアプリケーションのさまざまな箇所が相互作用するさまをテストする。
  • アプケーション内の重要なワークフローをテストする。
  • railsでは統合テストはtest/integrationディレクトリに作成される。
  • 以下のように作成できる。
    $ bin/rails generate integration_test user_flows
    
  • その中身。
    require 'test_helper'
     
    class UserFlowsTest < ActionDispatch::IntegrationTest
      # test "the truth" do
      #   assert true
      # end
    end
    
  • テストはActionDispatch::IntegrationTestを警鐘する。

6.1 Helpers Available for Integration Tests

  • ActionDispatch::IntegrationTestでは標準のテストに加え追加のヘルパーが使用できる。
    • ActionDispatch::Integration::Runner
    • ActionDispatch::Integration::RequestHelpers
    • ActionDispatch::Integration::Session

6.2 Implementing an integration test

  • ブログシステムに統合テストを追加する。
    $ bin/rails generate integration_test blog_flow
  • BlogFlowTestを実装する。"/"を取得しh1タグの中身が"Welcome#index"であることを確認。なおこのテストを実行するにはwelcome_controllerを先に作っておかないと行けない(後述)。
    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の設定。
    Rails.application.routes.draw do
      get 'welcome/index'
      root 'welcome#index'
      resources :articles
    end
    
  • 記事作成のテスト。
      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

  • Railsではコントローラーの個別の(?)メソッドをテストするにはfunctional test(機能テスト)を使用する。

7.1 What to include in your Functional Tests

  • 以下の内容を機能テストに記述する
    • リクエストが成功したか
    • リダイレクトが正しいか。
    • ユーザー認証が成功したか。
    • 正しいオブジェクトがtemplateに埋め込まれたか。
    • 適切なメッセージがビューに表示されたか。
  • 機能テストを作成するにはscaffoldを使用するのが簡単。test/controllers/articles_controller_test.rbがそれ。
    $ bin/rails generate scaffold_controller article title:string 
  • すでにコントローラーを作成済みで、7つのデフォルトアクションに対するテストを作成したい場合以下のように実行する。
    $ bin/rails generate test_unit:scaffold article
  • テストの一つ。
    # 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アクションでリファラを設定する場合。
    get article_url, params: { id: 12 }, headers: { "HTTP_REFERER" => "http://example.com/home" }
    
  • updateアクションをAjaxリクエストする場合。
    patch article_url, params: { id: 12 }, xhr: true
    
  • 記事作成メソッドのテスト(この前にテストを実行すると、新たに追加したvalidationにより失敗するとあるがデフォルト状態でも作成できそうだが…)。
    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

  • 以下のリクエストタイプが使用できる。
    • get
    • post
    • patch
    • put
    • head
    • delete

7.3 Testing XHR (AJAX) requests

  • AJAXリクエストをテストするには、「xhr: true」オプションを指定する。
    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

  • requestが作成されて処理されると、以下の3種類のハッシュが利用できるようになる。
    • cookies
    • flash
    • session
  • サンプル
    flash["gordon"]               flash[:gordon]
    session["shmession"]          session[:shmession]
    cookies["are_good_for_u"]     cookies[:are_good_for_u]
    
    

7.5 Instance Variables Available

  • また以下のインスタンス変数にもアクセスできるようになる。
    • @controller
    • @request
    • @@response
  • サンプル
    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

  • HTTPヘッダーとCGI変数をヘッダーとして設定できる。
    # 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

  • flashのテスト。
  • test_should_create_articleにassertionを追加する。
    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を変更する。
    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

  • その他のテスト。show
    test "should show article" do
      article = articles(:one)
      get article_url(article)
      assert_response :success
    end
    
  • destroy
    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
    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

  • 重複をさけるためヘルパーが存在。
    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

  • ルートのテストも可能。
  • test/controllersなどでテストする。

9 Testing Views

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

  • ヘルパーはシンプルなモジュール。
  • ヘルパーに関するtest/helpersディレクトリ以下に置く。
  • 以下のヘルパーが存在するとき
    module UserHelper
      def link_to_user(user)
        link_to "#{user.first_name} #{user.last_name}", user
      end
    end
    
  • 以下のテストを作成する
    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

11.1 Keeping the Postman in Check

  • メーラーもテストする必要がある。
    • メールが処理されるか
    • メールの内容が正しいか
    • 正しいタイミングでメールが送信されたか。
  • 単体テストあるいは機能テストでテストできる。

11.2 Unit Testing

  • 単体テストを使用してメールの結果を比較する。
  • メーラーをテストするためにフィクスチャを利用する。UserMailerをテストする場合test/fixtures_user_mailerディレクトリにフィクスチャを保存する。
  • UserMailerのテスト
    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フィクスチャの中身。
    Hi friend@example.com,
     
    You have been invited.
     
    Cheers!
    
  • config/environments/test.rbのActionMailer::Base.delivery_method = :testによりテストモードが設定され実際はメールが送信されない。

11.3 Functional Testing

  • functional testでは配信のテストを行う。
    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

  • ジョブのテストも行う必要がある。

12.1 A Basic Test Case

  • jobのテストはtest/job以下に作成される。
    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

  • job用のカスタムアサーションが多数存在する。
    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

13.1 Testing Time-Dependent Code

  • 時間に関連したためのテストを行うヘルパーメソッドも存在する。
  • 例えばtravel_to
    # 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.
    

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-07-05 (木) 13:51:38