0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RSpec 基本まとめ02(Feature,Request)

Posted at

RSpec 基本まとめ01(Model, FactoryBot, Controller)の続きです

フューチャスペックでUIをテスト

  • 統合テストと呼ばれる。これまでの単体テストは違って、開発したソフトウェア全体がシステムとして動作するか検証
  • Rails5.1ではシステムテストという名前でデフォルトでサポートされるようになったので、システムスペックへ移行することがお勧めされている(内容的にはほぼ同じ)

Capybara gem インストール

ブラウザの操作をシミュレートするために、Capybara gemを使用する。主に、リンクのクリック、フォームの入力、画面の表示を検証することができる

(Rails5.1からはCapybaraは既にインストールされているが、補足として記載)

group :test do
  # selenium_chromeがデフォルト登録されたのがv2.15以降のためバージョンを指定
  gem 'capybara', '~> 2.15.2'
end

また、CapybaraはRails開発環境で実行可能なジェネレータは用意されていないため、テスト環境にだけ追加することで、開発環境のメモリ消費を軽くすることができる。

また、bundleコマンド後、Capybaraを読み込むよう設定

spec/rails_helper.rb
.
.
require 'spec_helper'
require 'rspec/rails'

# 追加
require 'capybara/rspec'
.

ファイル生成

# 例
rails g rspec:feature posts

テスト例

spec/features/posts_spec.rb
require 'rails_helper'

RSpec.feature "Posts", type: :feature do
  # 例:ユーザーがポストする
  
  scenario "user creates a new project" do
    user = FactoryBot.create(:user)

    visit root_path
    click_link "Sign in"
    fill_in "Password", with: user.password
    click_button "Log in"


    expect {
      click_link "New Post"
      fill_in "Name", with: "Test Post"
      click_button "Create Post"

      expect(page).to have_content "Post was successfully created"
      expect(page).to have_content "Test Post"
      expect(page).to have_content "Owner: #{user.name}"
    }.to change(user.posts, :count).by(1)
  end
end
  • コントローラスペックとフューチャスペックの違いは、利用者と同じようにフォーム等を使って、画面操作でのテスト。コントローラスペックはUIを無視して、パラメータを直接コントローラのメソッドに送信する

  • また、フューチャスペックでは1つのexample、もしくは1つのシナリオで複数のエクスペクテーションを書くのは問題ではない。

  • scenario : itと同様に、exampleの起点を表す。

  • visit : ページを開く

  • click_link : リンクを押す

  • fill_in ~~, with -- : フォームを入力。withの後に値を設定できる。

  • click_button : ボタンをクリック。(起動されたアクションが完了する前に、次の処理へうつってしまうことがあるため、1個以上のエクスペクテーションを実行し、処理の完了を待つ

  • page : ページの要素を取得

  • have_content : 想定した文字列がページ内にあるか検証

他にも、よく使用されると考えられるのが、

  • check : チェックボックスのラベルをチェック
  • uncheck : チェックボックスのラベルを外す
  • choose : ラジオボタンのラベルを選択する
  • select ~~, from: -- : セレクトメニューからオプションを選択
  • attach_file : ファイルアップロードのラベルでファイル添付
  • have_css : 指定したcssに一致する要素があるか検証
  • have_selector : 指定したセレクタに一致する要素があるか検証
  • have_current_path : 現在のパスが指定したパスであるか検証

Capybara DSL ドキュメントで、他のメソッドも多く掲載されている。

フューチャスペックをデバッグ

Capybaraはヘッドレスブラウザ(UIを持たない)を使ってテストを実行するため、処理ステップを目で確認することができない。
ただ、save_and_open_pageメソッドを、テストが失敗する直前の箇所に記述することで、Railsがブラウザに返したHTMLを確認することができる

# ログインしていないことによるエラーを確認
scenario "guest adds a post" do
  visit posts_path
  save_and_open_page
  # ログインしていないことで、別ページに遷移しており、該当リンクがないことでエラー
  click_link "New Post"
end

こうすると、エラー内容の中に、ファイルのパスが表示され、そこからブラウザ上で確認することが出来る。(ログインしてないから、別のページにリダイレクトしており、ボタンがなかった!などが判明する)

また、Launchy gem をインストールすることで、自動でブラウザに表示されるようになる。

save_and_open_pageメソッドは、デバッグ用のメソッドのため、不要になったら削除するのを忘れないように!

JavaScript込みの操作をテスト

この節の内容が、自分の開発環境ではまだ正常に動作していないため、後ほど更新します!💦

リクエストスペック

現在、コントローラスペックではなく、リクエストスペックが推奨されている。そのため、フィーチャスペックで使用した、Capybaraは使用しません。それは、ブラウザの操作を検証する訳ではないからです。代わりに、HTTPメソッド(GET, POST, DELETE, PATCH)を使用します。
ここでは、APIのテストに関して記述します。

ファイル作成

# 例
bin/rails g rspec:request posts_api

このコマンドで、spec/requests/posts_apis_spec.rbファイルが生成されるが、APIが複数形になり不自然なため、手動で変更します。

⇨ spec/requests/posts_api_spec.rb

テスト例

GETリクエスト

  • 記述に関しては、コントローラスペックとほぼ変わりませんが、リクエストスペックは出来ることが増えます。
spec/requests/posts_api_spec.rb
require 'rails_helper'

RSpec.describe "Posts Api", type: :request do
  it "loads a post" do
    user = FactoryBot.create(:user)
    FactoryBot.create(:post,
      name: "Sample Post"
    )
    FactoryBot.create(:post,
      name: "Second Sample Post",
      owner: user
    )

    get api_posts_path, params: {
      # APIではユーザーのメアドとサインインするためのトークンが必要
      user_email: user.email,
      user_token: user.authentication_token
    }

    expect(response).to have_http_status(:success)
    json = Json.parse(response.body)
    expect(json.length).to eq 1
    post_id = json[0]["id"]
 
    # ownerを設定したpostのみ読み込まれるか検証
    get api_post_path(post_id), params: {
      user_email: user.email,
      user_token: user.authentication_token
    }

    expect(response).to have_http_status(:success)
    json = JSON.parse(response.body)
    expect(json["name"]).to eq "Second Sample Post"

  end
end
  • コントローラスペックよりも、フューチャスペックぽさがある記述になります。
  • コントローラスペックとは異なり、好きなルーティング名を何度でも使用することが出来る。リクエストスペックはコントローラに結びつかないため。そのため、テストしたいルーティング名をしっかり指定しているか確認することが必要。

POSTリクエスト

spec/requests/posts_api_spec.rb
require 'rails_helper'

describe "Posts API", type: :request do
  .
  .
  
  it "creates a post" do
    user = FactoryBot.create(:user)

    post_attributes = FactoryBot.attributes_for(:post)

    # postリクエストを送り、postが1件作成されることを検証
    expect {
      post api_posts_path, params: {
        user_email: user.email,
        user_token: user.authentication_token,
        # プロジェクトの属性も含める
        post: post_attributes
      }
    }.to change(user.posts, :count).by(1)

    expect(response).to have_http_status(:success)
  end
end

コントローラスペックをリクエストスペックに置き換え

リクエストスペックが推奨されているので置き換えてみます。

spec/requests/posts_spec.rb
.
before do
  @user = FactoryBot.create(:user)
end

# 有効な属性値を渡し、post作成を検証
it "adds a post" do
  post_params = FactoryBot.attributes_for(:post)
  sign_in @user
  expect {
    post posts_path, params: { post: post_params }
  }.to change(@user.posts, :count).by(1)
end

# 無効な属性値を渡し、postが作成されないことを検証
it "does not add a post" do
  post_params = FactoryBot.create(:post, :invalid)
  sign_in @user
  expect {
    post posts_path, params: { post: post_params }
  }.to_not change(@user.posts, :count)
end
  • コントローラスペックとの違いは、Postコントローラのcreateアクションに直接依存するのではなく、具体的なルーティング名を指定してPOSTリクエストを送信する点。

  • また、Deviseのsign_inヘルパーを使用するには、下記のように、リクエストスペックで使用することを明記します。

まずspec/support/request_spec_helper.rbを作成

spec/support/request_spec_helper.rb
module RequestSpecHelper
  include Warden::Test::Helpers

  def self.included(base)
    base.before(:each) { Warden.test_mode! }
    base.after(:each) { Warden.test_reset! }
  end

  def sign_in(resource)
    login_as(resource, scope: warden_scope(resource))
  end

  def sign_out(resource)
    logout(warden_scope(resource))
  end

  private

  def warden_scope(resource)
    resource.class.name.underscore.to_sym
  end
end
spec/rails_helper.rb
.
.
RSpec.configure do |config|
  .
  .
  config.include Devise::Test::ControllerHelpers, type: :controller
  # 追加
  config.include RequestSpecHelper, type: :request
end

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?