1
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?

【Rails】テストファイルで使用するコマンドとメソッド

Last updated at Posted at 2025-05-03

記事概要

Ruby on Railsのテストについて、まとめる

RSpec

Capybara

結合テストコードを記述するための仕組みであり、デフォルトでRailsに搭載されている

まとめ(コマンド)

rspecコマンド

specディレクトリ以下に書かれたRSpecのテストコードを実行するコマンド

まとめ(テスト整理のメソッド)

describeメソッド

テストコードのグループ分けを行うメソッド
「どの機能に対してのテストを行うか」をdescribeでグループ分けし、その中に各テストコードを記述する

contextメソッド

テストコードのグループ分けを行うメソッド
使用方法はdescribeと同じだが、contextには特定の条件を指定する

itメソッド

テストコードのグループ分けを行うメソッド
describeメソッドに記述した機能において、どのような状況のテストを行うか」を明記する

example

itで分けたグループのこと

まとめ(メソッド)

expectation

検証で得られた挙動が想定通りなのかを確認する構文のこと
雛形は、expect().to matcher()

expectの引数

検証で得られた実際の挙動を指定する

matcher

「expectの引数」と「想定した挙動」が一致しているかどうかを判断する
どのような挙動を想定しているかを記述する

include

「expectの引数」に「includeの引数」が含まれていることを確認するマッチャ

# 配列の中に'メロン'が含まれていることを想定
expect(['りんご', 'バナナ', 'ぶどう', 'メロン']).to include('メロン')

eq

「expectの引数」と「eqの引数」が等しいことを確認するマッチャ

# 1 + 1という計算の結果が、2と等しいことを想定
expect(1 + 1).to eq(2)

be_valid

valid?メソッドの返り値が、trueであることを期待するマッチャ

expectの引数に指定されたインスタンスがバリデーションでエラーにならない場合、valid?の返り値はtrueとなる

@user = FactoryBot.build(:user)
expect(@user).to be_valid
#=> 「@user.valid?」の結果が"true"の場合、正常完了
#=> 「@user.valid?」の結果が"false"の場合、エラー発生

have_content

expect(page).to have_content('X')と記述することで、pageの中に、Xという文字列があるかどうかを判断するマッチャ

# 新規登録ボタンがあることを確認する
expect(page).to have_content('新規登録')

have_no_content

have_contentの逆で、文字列が存在しないことを確かめるマッチャ

change

モデルのレコードの数がいくつ変動するのかを確認できるマッチャ

expect{ [動作] }.to change { [モデル名].count }.by([変動する数])
# サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する
expect{
  find('input[name="commit"]').click
  sleep 1
}.to change { User.count }.by(1)

expect()ではなくexpect{}

have_current_path

pageのURLを確認するマッチャ

# トップページへ遷移することを確認する
expect(page).to have_current_path(root_path)

have_selector

指定したセレクタが存在するかどうかを判断するマッチャ

# 要素「<div class="content_post" style="background-image: url(画像のURL);">」を取得
have_selector ".content_post[style='background-image: url(#{@tweet_image});']"

# トップページには先ほど投稿した内容のツイートが存在することを確認する(画像)
expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet_image});']"

have_link

要素の中に当てはまるリンクがあることを確認するマッチャ
a要素に対して使用する

# 要素の中に当てはまるリンクがあることを確認できる
expect('[要素]').to have_link '[ボタンの文字列]', href: '[リンク先のパス]'

have_no_link

have_linkの逆で、要素の中に当てはまるリンクがないことを確認するマッチャ

# 要素の中に当てはまるリンクがないことを確認できる
expect('[要素]').to have_no_link '[ボタンの文字列]', href: '[リンク先のパス]'

have_field

inputやtextareaなどのフォーム要素が存在するかを確認するマッチャ
引数には、CSSで使用している#などは不要

# 「tweet_image」というidを持ったフォームが存在するかを確認
have_field('tweet_image')

# 「tweet_image」というidを持ったフォームが存在し、そのフォームにHelloと入力されていることを確認
have_field('tweet_image', with: "Hello")

build

ActiveRecordのnewメソッドと同様の意味を持つ
テスト用DBへアクセスしないため、データが保存されない

# FactoryBotを利用しない場合
user = User.new(nickname: 'test', email: 'test@example', password: '000000', password_confirmation: '00000000')

# FactoryBotを利用する場合
user = FactoryBot.build(:user)

create

ActiveRecordのcreateメソッドと同様の意味を持つ

buildとほぼ同じ働きをするが、createの場合はテスト用DBに値が保存される
注意すべき点は、1回のテストが実行され、終了する毎にテスト用DBの内容がロールバックされる(テスト実行時に保存された値がすべて消去されてしまう)

# FactoryBotを利用しない場合
user = User.create(nickname: 'test', email: 'test@example', password: '000000', password_confirmation: '00000000')

# FactoryBotを利用する場合
user = FactoryBot.create(:user)

before

それぞれのテストコードを実行する前に、セットアップを行うこと

モデル名_spec.rb
require 'rails_helper'
RSpec.describe モデル名, type: :model do
  before do
    # 処理(変数を受け渡す場合、インスタンス変数にする必要がある)
  end

  describe 'X' do
    it 'Y' do
      # before内の処理が完了してから実行される
    end
    it 'Z' do
      # before内の処理が完了してから実行される
    end
  end
end

after

任意の処理後に指定の処理を実行する

Rails.root

Railsアプリケーションのトップ階層のディレクトリまでの絶対パスを取得できる

# /Users/ユーザー名/projects/に「sample-app」というRailsアプリがある場合
[1] pry(main)> Rails.root
=> #<Pathname:/Users/ユーザー名/projects/sample-app>

Rails.root.join

引数として渡した文字列でのパス情報を、Rails.rootのパスの情報につけることができる

# /Users/ユーザー名/projects/に「sample-app」というRailsアプリがある場合
[1] pry(main)> Rails.root.join('public/images/test_image.png')
=> #<Pathname:/Users/ユーザー名/projects/sample-app/public/images/test_image.png>

create_list

FactoryBotの設定ファイルに存在しているレコードを、複数作成したい場合に使用するメソッド
create_listを用いることで、一度に複数のテストデータを生成可能

# hogesテーブルのレコードを3つ作成
FactoryBot.create_list(:hoge, 3)

# hogesテーブルのレコードを3つ作成し、titleカラムの情報を「Hello world」とする
FactoryBot.create_list(:hoge, 3, title: 'Hello world')

# hogesテーブルのレコードを3つ作成し、外部キー(user_id)の情報を@user.idとする
FactoryBot.create_list(:hoge, 3, user_id: @user.id)

sleep

次の処理を待機する

# 1秒待機後、次の処理を行う
sleep 1

まとめ(単体テストのメソッド)

valid?メソッド

バリデーションを実行させて、エラーがあるかどうかを判断するメソッド

エラーがない場合はtrueを返す
エラーがある場合はfalseを返し、エラーの内容を示すエラーメッセージを生成する

user.valid?
# Userモデルにおいて、カラムnicknameには入力必須のバリデーションが設けられているケース

[1] pry(main)> user = User.new(nickname: '', email: 'test@example', password: '000000', password_confirmation: '000000')
[2] pry(main)> user.valid?
=> false

errors

インスタンスにエラーを示す情報がある場合、その内容を返すメソッド

# 変数userに新規データを格納
[1] pry(main)> user = User.new(nickname: '', email: 'test@example', password: '000000', password_confirmation: '000000')
=> #<User id: nil, email: "test@example", created_at: nil, updated_at: nil, nickname: "">

# 変数userにバリデーションを実行し、エラーがあるかを判断
[2] pry(main)> user.valid?
  User Exists? (0.4ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'test@example' LIMIT 1
=> false

# エラー情報を表示
[3] pry(main)> user.errors
=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=nickname, type=blank, options={}>]>

full_messages

エラーの内容から、エラーメッセージを配列として取り出すメソッド

# 変数userに新規データを格納
[1] pry(main)> user = User.new(nickname: '', email: 'test@example', password: '000000', password_confirmation: '000000')
=> #<User id: nil, email: "test@example", created_at: nil, updated_at: nil, nickname: "">

# 変数userにバリデーションを実行し、エラーがあるかを判断
[2] pry(main)> user.valid?
  User Exists? (0.5ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'test@example' LIMIT 1
=> false

# エラーメッセージを表示
[3] pry(main)> user.errors.full_messages
=> ["Nickname can't be blank"]
user_spec.rb
user.valid?
expect(user.errors.full_messages).to include("Nickname can't be blank")

get

「パスにリクエストすると」を行う

get Prefix名_path

# ルートパスにリクエストする
get root_path

# showアクションにリクエストする
get tweet_path(@tweet)

response

リクエストに対するレスポンスそのもの

レスポンスで取得できる情報に、想定する内容が含まれているかを確認することで、テストコードを書ける

[1] pry(#<RSpec::ExampleGroups::TweetsController::GETIndex>)> response
=> #<ActionDispatch::TestResponse:0x0000000117117278
 @cache_control={:max_age=>"0", :private=>true, :must_revalidate=>true},
 @committed=false,
 @cv=
  #<MonitorMixin::ConditionVariable:0x00000001171902b8
   @cond=#<Thread::ConditionVariable:0x0000000117190290>,
   @monitor=#<Monitor:0x0000000117190470>>,
 @header=
  {"X-Frame-Options"=>"SAMEORIGIN",
   "X-XSS-Protection"=>"0",
   "X-Content-Type-Options"=>"nosniff",
   "X-Download-Options"=>"noopen",
   "X-Permitted-Cross-Domain-Policies"=>"none",
   "Referrer-Policy"=>"strict-origin-when-cross-origin",
   "Link"=>
    "</assets/application-9b8e2e0d675fa110d5daab5bb63deb13a118ca78232c286410d1b5291170c08e.css>; rel=preload; as=style; nopush",
   "Content-Type"=>"text/html; charset=utf-8",
   "ETag"=>"W/\"77f2ac4a81f863be39016e9731728738\"",
   "Cache-Control"=>"max-age=0, private, must-revalidate",
   "X-Request-Id"=>"f1f6e0b0-b6fc-4f8e-81e1-127901377b68",
   "X-Runtime"=>"0.327373",
   "Content-Length"=>"3460"},
# 十字キーの↓を入力すると続きを確認でき、qを押下すると終了して次の入力画面に移行できる

HTTPステータスコード

HTTP通信において、どのような処理の結果となったのかを示すもの

ステータスコード 内容
100~ 処理の継続中
200~ 処理の成功
300~ リダイレクト
400~ クライアントのエラー
500~ サーバーのエラー

status

response.statusを実行することで、HTTPステータスコードを確認できる

# レスポンスのステータスコードが出力される
[1] pry(#<RSpec::ExampleGroups::TweetsController::GETIndex>)> response.status
=> 200
tweets_spec.rb
expect(response.status).to eq 200

body

response.bodyを実行することで、ブラウザに表示されるHTMLの情報を抜き出せる

# ブラウザに表示されるHTMLの情報が出力される
[1] pry(#<RSpec::ExampleGroups::TweetsController::GETIndex>)> response.body
=> "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Pictweet</title>\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    \n    \n\n    <link rel=\"stylesheet\" href=\"/assets/application-9b8e2e0d675fa110d5daab5bb63deb13a118ca78232c286410d1b5291170c08e.css\" data-turbo-track=\"reload\" />\n    <script type=\"importmap\" data-turbo-track=\"reload\">{\n  \"imports\": {\n    \"application\": \"/assets/application-37f365cbecf1fa2810a8303f4b6571676fa1f9c56c248528bc14ddb857531b95.js\",\n    \"@hotwired/turbo-rails\": \"/assets/turbo.min-f3765a09513ca1417099ce92257ef54b9d4cf3a7addc7dcbb1d6d848d307ee8a.js\",\n    \"@hotwired/stimulus\": \"/assets/stimulus.min-dd364f16ec9504dfb72672295637a1c8838773b01c0b441bd41008124c407894.js\",\n    \"@hotwired/stimulus-loading\": \"/assets/stimulus-loading-3576ce92b149ad5d6959438c6f291e2426c86df3b874c525b30faad51b0d96b3.js\",\n    \"controllers/application\": \"/assets/controllers/application-368d98631bccbf2349e0d4f8269afb3fe9625118341966de054759d96ea86c7e.js\",\n    \"controllers/hello_controller\": \"/assets/controllers/hello_controller-549135e8e7c683a538c3d6d517339ba470fcfb79d62f738a0a089ba41851a554.js\",\n    \"controllers\": \"/assets/controllers/index-31a9bee606cbc5cdb1593881f388bbf4c345bf693ea24e124f84b6d5c98ab648.js\"\n  }\n}</script>\n<link rel=\"modulepreload\" href=\"/assets/application-37f365cbecf1fa2810a8303f4b6571676fa1f9c56c248528bc14ddb857531b95.js\">\n<link rel=\"modulepreload\" href=\"/assets/turbo.min-f3765a09513ca1417099ce92257ef54b9d4cf3a7addc7dcbb1d6d848d307ee8a.js\">\n<link rel=\"modulepreload\" href=\"/assets/stimulus.min-dd364f16ec9504dfb72672295637a1c8838773b01c0b441bd41008124c407894.js\">\n<link rel=\"modulepreload\" href=\"/assets/stimulus-loading-3576ce92b149ad5d6959438c6f291e2426c86df3b874c525b30faad51b0d96b3.js\">\n<link rel
tweets_spec.rb
expect(response.body).to include(@tweet.text)

まとめ(結合テストのメソッド)

visit

指定したページへ遷移できる

visit Prefix名_path

# トップページに移動する
visit root_path

page

visitで訪れた先のページの見える分だけの情報が格納されている
カーソルを合わせてはじめて見ることができる文字列は含まれない

fill_in

fill_in 'フォームの名前', with: '入力する文字列'と記述することで、フォームへ入力できる

Image from Gyazo

# ユーザー情報を入力する
fill_in 'Nickname', with: @user.nickname
fill_in 'Email', with: @user.email
fill_in 'Password', with: @user.password
fill_in 'Password confirmation', with: @user.password_confirmation

all

指定した要素を全て取得する

# pageに存在する同名のクラスを持つ要素をまとめて取得
all('クラス名')

# 「0番目のmoreクラス」を取得 ※添字なので、0からスタートする
all('クラス名')[0]

find

指定した要素を取得する

find('要素')

find().click

find('クリックしたい要素').clickと記述することで、実際にクリックできる

# <input type="submit" name="commit" value="Sign up" data-disable-with="Sign up">をクリックする
find('input[name="commit"]').click

find_link().click

find_link('リンクの文字列', href: 'URL').clickと記述することで、a要素で表示されているリンクをクリックする

hover

特定の要素にカーソルをあわせたときの動作を再現する
※カーソルを合わせる要素を特定するために、親要素のクラス名を指定する

find('[ブラウザ上の要素]').hover

# 実際に使用する場合
find('.[親要素のクラス名]').find('[ブラウザ上の要素]').hover
# user_navクラスの中にあるspan要素がログアウトボタン
# カーソルを合わせるとログアウトボタンが表示されることを確認する
expect(
  find('.user_nav').find('span').hover
).to have_content('ログアウト')

click_onメソッド

引数に文字列を取り、一致するテキストなどを持った要素をクリックできるメソッド

※()をつけるかは自由

# 「<a href="/contents/1">詳しくはこちら</a>」の要素をクリックしたことにできる
click_on ('詳しくはこちら')

# 「<input type="submit" name="commit" value="ログイン">」の要素をクリックしたことにできる
click_on ('ログイン')

attach_fileメソッド

画像などのアップロード用のinput要素(タイプがfileのinput要素)に、テスト用画像を添付(アタッチ)できるメソッド

引数 内容
第一引数 画像をアップロードするinput要素のname属性の値
第二引数 アップロードする画像のパス
第三引数以降 オプション
attach_file('input要素のname属性の値', 画像のパス)
# 添付する画像を定義
image_path = Rails.root.join('public/images/test_image.png')

# 画像選択フォームに画像を添付する
attach_file('message[image]', image_path)

make_visible

CSSのdisplay: none;を設定していることで、非表示になっている画像選択フォームの場合に付与するオプション

# 非表示の画像選択フォームに画像を添付する
attach_file('message[image]', image_path, make_visible: true)

fixture_file_uploadメソッド

インスタンスに画像ファイルをセットするメソッド

current_path

現在いるページのパスを示す

# 今いるページが指定したURLであることを確認できる
expect(current_path).to eq [Prefix]_path

Ruby on Railsまとめ

1
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
1
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?