コントローラーの単体テストコード

本章では、コントローラーの単体テストコードを学びます。モデルとコントローラーでは、テストコードを書く方針が以下のように異なります。

テストコードの種類 テストコードを書く方針
モデル インスタンスを生成し、それがモデルに規定したどおりの挙動になるか(たとえば、バリデーションが正しく働くか)を確かめる
コントローラー あるアクションにリクエストを送ったとき、想定通りのレスポンスが生成されるかどうかを確かめる

Request Specリクエスト スペック

RSpecが提供している、コントローラーのテストコードを書くために特化した手法です。RSpecの導入が完了していれば使用できます。

 

exampleを整理しよう

まず、indexアクションにリクエストを送った際に確認すべきexampleについて整理しましょう。indexアクションにリクエストした際は、以下のようなことが考えられます。

  • indexアクションにリクエストすると正常にレスポンスが返ってくる
  • indexアクションにリクエストするとレスポンスに投稿済みのツイートのテキストが存在する
  • indexアクションにリクエストするとレスポンスに投稿済みのツイートの画像URLが存在する
  • indexアクションにリクエストするとレスポンスに投稿検索フォームが存在する

createクリエイト

ActiveRecordcreateメソッドと同様の意味を持ちます。
buildとほぼ同じ働きをしますが、createの場合はテスト用のDBに値が保存されます。
注意すべき点として、1回のテストが実行され、終了する毎にテスト用のDBの内容がロールバックされます。

FactoryBot.create(:tweet)というように「create」で保存をあらかじめしておきます。

spec/requests/tweets_spec.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
require 'rails_helper'
describe TweetsController, type: :request do

  before do
    @tweet = FactoryBot.create(:tweet)
  end

  describe 'GET #index' do
    it 'indexアクションにリクエストすると正常にレスポンスが返ってくる' do 
    end
    it 'indexアクションにリクエストするとレスポンスに投稿済みのツイートのテキストが存在する' do 
    end
    it 'indexアクションにリクエストするとレスポンスに投稿済みのツイートの画像URLが存在する' do 
    end
    it 'indexアクションにリクエストするとレスポンスに投稿検索フォームが存在する' do 
    end
  end
end

 

binding.pryで停止しているところに、responseと入力してエンターキーを押下しましょう。するとレスポンスとしてたくさんの情報が表示されるはずです。

ターミナル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[1] pry(#<RSpec::ExampleGroups::TweetsController::GETIndex>)> response

=> #<ActionDispatch::TestResponse:0x00007fe4ba5b45e0
 @cache_control={:max_age=>"0", :private=>true, :must_revalidate=>true},
 @committed=false,
 @cv=#<MonitorMixin::ConditionVariable:0x00007fe4ba5b44a0 @cond=#<Thread::ConditionVariable:0x00007fe4ba5b4478>, @monitor=#<ActionDispatch::TestResponse:0x00007fe4ba5b45e0 ...>>,
 @header=
  {"X-Frame-Options"=>"SAMEORIGIN",
   "X-XSS-Protection"=>"1; mode=block",
   "X-Content-Type-Options"=>"nosniff",
   "X-Download-Options"=>"noopen",
   "X-Permitted-Cross-Domain-Policies"=>"none",
   "Referrer-Policy"=>"strict-origin-when-cross-origin",
   "Content-Type"=>"text/html; charset=utf-8",
   "ETag"=>"W/\"07b471bc4b0f822d1d74886c0cd36998\"",
   "Cache-Control"=>"max-age=0, private, must-revalidate",
   "X-Request-Id"=>"e0aea9ae-5622-4a8a-9cfc-13fa1eaaddf3",
   "X-Runtime"=>"2.913438",
   "Content-Length"=>"154958"},
 @mon_count=0,
 @mon_mutex=#<Thread::Mutex:0x00007fe4ba5b4590>,
 @mon_mutex_owner_object_id=70310177907440,
 @mon_owner=nil,

#以下省略
長過ぎるため一度に表示されません。十字キーの↓を入力すると続きを確認できます。Qを押下すると終了して次の入力画面に移行できます。

ここから、「正常なレスポンスかどうか」を判断する必要があります。それを判断するためには、HTTPステータスコードで判別します。

HTTPエイチティーティーピーステータスコード

HTTP通信において、どのような処理の結果となったのかを示すものです。以下のような分類になっています。

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

今回は正常にレスポンスを得ることを確かめたいため、200というステータスコードを期待します。
レスポンスの中からステータスコードを確かめるためには、statusを利用します。

 

statusステータス

response.statusと実行することによって、そのレスポンスのステータスコードを出力できます。

 

レスポンスに投稿済みのツイートのテキストが存在する」を確かめるテストコードを記述します。
先ほど学んだresponseの中に、ブラウザ上に表示される記述があります。それはbodyと呼ばれます。

bodyボディー

response.bodyと記述すると、ブラウザに表示されるHTMLの情報を抜き出すことができます。

 

tweets_spec.rbを編集しましょう。

spec/requests/tweets_spec.rb
1
2
3
4
it 'indexアクションにリクエストするとレスポンスに投稿済みのツイートのテキストが存在する' do 
  get root_path
  binding.pry
end

続いて、以下のコマンドを実行します。

ターミナル
1
% bundle exec rspec spec/requests/tweets_spec.rb

binding.pryの部分で停止したら、response.bodyと入力してエンターキーを押下しましょう。すると、以下のようにHTMLの情報が表示されます。

ターミナル
1
2
3
4
5
[1] pry(#<RSpec::ExampleGroups::TweetsController::GETIndex>)> response.body

=> "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Pictweet</title>\n    \n    \n\n    <link rel=\"stylesheet\" media=\"all\" href=\"/assets/application-097b0ccf4679051ab3e4c471c917c119a4a2a4319479b9ef722b42d8f952764e.css\" data-turbolinks-track=\"reload\" />\n    <script src=\"/assets/application-0bd5c4f4779a1eb21cfe2a9620c6f659ef14237993955e2b0c155c65f436f675.js\"></script>\n    <script src=\"/packs-test/js/application-beb94e39e00d815806de.js\" data-turbolinks-track=\"reload\"></script>\n  </head>\n  <header class=\"header\">\n    <div class=\"header__bar row\">\n      <h1 class=\"grid-6\"><a href=\"/\">PicTweet</a></h1>\n          <div class=\"grid-6\">\n            <a class=\"post\" href=\"/users/sign_in\">ログイン</a>\n            <a class=\"post\" href=\"/users/sign_up\">新規登録</a>\n          </div>\n    </div>\n  </header>\n    <form class=\"search-form\" "

#以下省略

次に、この中に投稿済みのツイートのテキスト情報が含まれているか確認するテストコードを記述します。

 

exampleについて整理しましょう。showアクションにリクエストした際は、以下のようなことが考えられます。

  • showアクションにリクエストすると正常にレスポンスが返ってくる
  • showアクションにリクエストするとレスポンスに投稿済みのツイートのテキストが存在する
  • showアクションにリクエストするとレスポンスに投稿済みのツイートの画像URLが存在する
  • showアクションにリクエストするとレスポンスにコメント一覧表示部分が存在する

 

weets_spec.rbを以下のように編集しましょう。

spec/requests/tweets_spec.rb
1
2
3
4
it 'showアクションにリクエストすると正常にレスポンスが返ってくる' do 
  get tweet_path(@tweet)
  expect(response.status).to eq 200
end

テストコードを実行しましょう

以下のコマンドを実行して、テストコードが正しく実行されることを確認しましょう。

ターミナル
1
% bundle exec rspec spec/requests/tweets_spec.rb

投稿済みのツイートがレスポンスに含まれることを確認しよう

続いて、「レスポンスに投稿済みのツイートのテキスト/画像URLが存在する」をまとめて確認しましょう。こちらも、indexアクションの際に行ったことと同じです。

テストコードを編集しましょう

tweets_spec.rbを以下のように編集しましょう。

spec/requests/tweets_spec.rb
1
2
3
4
5
6
7
8
it 'showアクションにリクエストするとレスポンスに投稿済みのツイートのテキストが存在する' do 
  get tweet_path(@tweet)
  expect(response.body).to include(@tweet.text)
end
it 'showアクションにリクエストするとレスポンスに投稿済みのツイートの画像URLが存在する' do 
  get tweet_path(@tweet)
  expect(response.body).to include(@tweet.image)
end

テストコードを実行しましょう

以下のコマンドを実行して、テストコードが正しく実行されることを確認しましょう。

ターミナル
1
% bundle exec rspec spec/requests/tweets_spec.rb

レスポンスにコメント一覧表示部分が含まれることを確認しよう

最後に、「レスポンスにコメント一覧表示部分が存在する」ことを確かめましょう。こちらは「<コメント一覧>」という文字列がレスポンスに含まれているかどうかを確認します。

テストコードを編集しましょう

tweets_spec.rbを以下のように編集しましょう。

spec/requests/tweets_spec.rb
1
2
3
4
it 'showアクションにリクエストするとレスポンスにコメント一覧表示部分が存在する' do 
  get tweet_path(@tweet)
  expect(response.body).to include('<コメント一覧>')
end

テストコードを実行しましょう

以下のコマンドを実行して、テストコードが正しく実行されることを確認しましょう。

ターミナル
1
% bundle exec rspec spec/requests/tweets_spec.rb