Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #2 RSpec導入編

# こんな人におすすめ
* プログラミング初心者でポートフォリオの作り方が分からない
* Rails Tutorialをやってみたが理解することが難しい

前回：[#1 準備編](https://qiita.com/aokyo17/items/4df4cfd97891addc0452)
次回：[#3 Bootstrap4, jQueryプラグイン導入編](https://qiita.com/aokyo17/items/34ac84b75176d1df37b0)

# 今回の流れ
1. 今回のイメージを理解する
* トップページのダミーを作る
* RSpecを導入する
* ヘルパーテストを書く
* ブラウザテストの雰囲気を掴む

※ この記事は、ポートフォリオを作る理由を**web系自社開発企業に転職するため**とします。
※ 2020年3月28日、記事を大幅に更新しました。

# 今回のイメージを理解する
始めに、簡単なRailsの動作とRSpecを説明するためダミーページを作ります。
続いて、ページによってタイトルが変わるfull_titleメソッドを作ります。
続いて、RSpecを導入します。
最後に、full_titleとブラウザのテストを書きます。

以上です。

# トップページのダミーを作る
まずはRails Tutorialの3~4章にならってトップページのダミーを作ります。
これはRails TutorialのテストをMinitestからRSpecに変更する際、差分をわかりやすくするためです（実際のトップページは[#3](https://qiita.com/aokyo17/items/34ac84b75176d1df37b0)から作ります）。

ダミーページには以下の要素を含みます。

* ダミーページのルーティングとMVCを整える
* タイトルを動的に変更するヘルパーを作る

## ダミーページのルーティングとMVCを整える
ダミーページのルーティングとMVC（Model, View, Controller）を整えます。
各用語の説明はRails Tutorialに譲りますが、ざっくりと言うと以下の通りです。

* Model → データを格納する
* View → Web上に表示する
* Controller → モデルとビューを制御する
* ルーティング → リクエストに対するコントローラーの割り当てをする

それではこれらを作成します。

### ルーティング
まずはルーティングをします。
ルートにはstatic_pagesコントローラーのhomeアクションを割り当てます。
その他にはstatic_pagesコントローラーのaboutアクションを割り当てます。

```rb:config/routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'
  get '/about', to: 'static_pages#about'
end
```

### Controller
ジェネレーターでコントローラーを作成します。
この時、自動的にhome, aboutアクションに対応するビューも作成されます。

```shell:shell
$ rails g controller StaticPages home about
```

```app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController

  def home
  end

  def about
  end
end
```

### View
ビューのタイトルはRails Tutorialで作成したfull_titleメソッドを使っています。
こちらは後述します。

```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all',
                               'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application',
                               'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
```

<%= yield %>という部分で、home, aboutそれぞれのビューを可変します。
可変部は以下の通りです。

```erb:app/views/static_pages/home.html.erb
<% provide(:title, "Home") %>
<h1>Sample App</h1>
<p>
  This is the home page for the
  <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
  sample application.
</p>
```

```erb:app/views/static_pages/about.html.erb
<% provide(:title, "About") %>
<h1>About</h1>
<p>
  <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
  is a <a href="https://railstutorial.jp/#ebook">book</a> and
  <a href="https://railstutorial.jp/#screencast">screencast</a>
  to teach web development with
  <a href="http://rubyonrails.org/">Ruby on Rails</a>.
  This is the sample application for the tutorial.
</p>
```

タイトルを指定するには、以下のように書きます。

```erb:
<% provide(:title, "〇〇") %>
```

こうすることで、application.html.erbにあるyield(:title)に文字列〇〇が代入されます。

### Model
モデルは[#5](https://qiita.com/aokyo17/items/48137ccb982f936d035f)で使いますが、今のところは必要ありません。

```
#5まで不要
```

## タイトルを動的に変更するヘルパーを作る
各ページでタイトルが変わるように、full_titleという名前でヘルパーメソッドを作成します。
ヘルパーとは、使い回しができるメソッドの置き場といったところです。
ルート以外のタイトルには先にページ名がくるよう定義します。

```rb:app/helpers/application_helper.rb
module ApplicationHelper

  def full_title(page_title = '')
    base_title = "Lantern Lantern" # 自分のアプリ名
    if page_title.empty?
      base_title
    else
      page_title + " | " + base_title
    end
  end
end
```

こうすることで、ビューのこの部分が理解できます。

```erb:
<title><%= full_title(yield(:title)) %></title>
<% provide(:title, "〇〇") %>
```

full_titleの引数にyield(:title)、つまり〇〇が入ります。
空でなければ縦棒が入って、タイトルとして表示されます。

以上でMinitestからRSpecに変更するためのダミーページが完成しました。

# RSpecを導入する
テストフレームワークで、より使われているのはMinitestではなくRSpecです。
ここでは企業が使っている母数を考え、RSpecを採用します。

それでは早速、導入します。
導入の手順は以下の通りです。

* RSpecをインストールする
* 必要なファイルと設定を追加する

## RSpecをインストールする
Gemfileを使ってRSpecを導入します。
RailsでRSpecを使う際、rspec-railsというGemを使います。
\#2以降、Gemをたくさん追加するので手順と用語を覚えます。

* Gem → Ruby用のパッケージ管理
* Gemfile → Gemを管理するファイル
* Bundler → Gemの依存関係を整えるGem

Gemfileに以下を記述し、Bundlerを使ってインストールします。

```diff:Gemfile
group :development, :test do
+ gem 'rspec-rails'
end
```

```shell:shell
$ bundle install
```

# 必要なファイルと設定を追加する
次にファイルと設定を追加します。
ジェネレーターからRSpecのファイルを自動生成します。
設定の変更として、テストをドキュメント形式にします。

```shell:shell
$ rails g rspec:install
```

```:.rspec
--require spec_helper
--format documentation
```

これでRSpecが動作するようになりました。

参考になりました↓
[RSpecとMinitest、使うならどっち？](https://speakerdeck.com/jnchito/number-kanrk06)
[RailsでRSpecテストを実行するまで](https://qiita.com/tocomi/items/2dba0de52eefdcf33fd7)

# Rails TutorialのMinitestをRSpecで確認する
[Rails Tutorial3章](https://railstutorial.jp/chapters/static_pages?version=5.1#sec-getting_started_with_testing)を進めると、初めてのテストにたどり着きます。
そのテストをMinitestからRSpecに書き直します。
以下は、MinitestとRSpecの差分です。
（一部改変）

```rb:static_pages_controller_test.rb（Minitest：Tutorial）
require 'test_helper'

class StaticPagesControllerTest < ActionDispatch::IntegrationTest

  test "should get home" do
    get static_pages_home_url
    assert_response :success
  end

  test "should get about" do
    get static_pages_about_url
    assert_response :success
  end
end
```

```rb:static_pages_controller_spec.rb（RSpec：ポートフォリオ）
require 'rails_helper'

RSpec.describe StaticPagesController, type: :controller do

  describe "GET #home" do
    it "returns http success" do
      get :home
      expect(response).to have_http_status(:success)
    end
  end

  describe "GET #about" do
    it "returns http success" do
      get :about
      expect(response).to have_http_status(:success)
    end
  end
end
```

ここで各テストとRSpecの対応を簡単に理解します。

* 単体テスト → Model/Controller specなど
* 統合テスト → Request spec
* ブラウザテスト → System spec（Feature specは非推奨）

RSpecの理解はこちらを参考にします↓
[使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」](https://qiita.com/jnchito/items/42193d066bd61c740612)
[Ruby on Rails のテストフレームワーク RSpec 事始め](https://qiita.com/tatsurou313/items/c923338d2e3c07dfd9ee)

次回以降からテストをRSpecで書くので、その都度覚えるよう心がけます。

# ヘルパーテストを書く
full_titleヘルパーのテストを追加します。
[Rails Tutorial 4.1.2 カスタムヘルパー](https://railstutorial.jp/chapters/rails_flavored_ruby?version=5.1#sec-custom_helpers)では、このテストをコントローラーテスト内に書いています。
しかしヘルパーに依存するテストをController Specに書くことは好まれません。
よってHelper specを生成します。

```bash:bash
$ rails g rspec:helper application
```

```application_helper_spec.rb
require 'rails_helper'

RSpec.describe ApplicationHelper, type: :helper do
  
  describe "#full_title" do
    context "page_title is empty" do
      it "removes symbol" do
        expect(helper.full_title).to eq('Lantern Lantern')
      end
    end
    
    context "page_title is not empty" do
      it "returns title and application name where contains symbol" do
        expect(helper.full_title('hoge')).to eq('hoge | Lantern Lantern')
      end
    end
  end
end
```

参考になりました↓
[Rails5でコントローラのテストをController specからRequest specに移行する](https://qiita.com/t2kojima/items/ad7a8ade9e7a99fb4384)
[Ruby on Rails 5の上手な使い方 現場のエンジニアが教えるRailsアプリケーション開発の実践手法](https://books.google.co.jp/books?id=wlNHDwAAQBAJ&pg=PA256&lpg=PA256&dq=%3Ctitle%3E%23%7Bbase_title%7D%3C%5C/title%3E/i&source=bl&ots=0eA_hTfVrB&sig=ACfU3U3GoDxnjYxVK560I9pO5ikpy0ws7A&hl=ja&sa=X&ved=2ahUKEwihm4qZoOrjAhXRIqYKHVsPDL4Q6AEwCnoECAgQAQ#v=onepage&q&f=false)

# ブラウザテストの雰囲気を掴む
ホームページのリンクを踏んだ際、ブラウザが反応するかのテストを確認します。
ブラウザテストにはSystem specを使います。

System specを使うには[#4 System spec導入編](https://qiita.com/aokyo17/items/356b7628194937851848)で**セットアップが必要**です。
今回はなんとなく雰囲気を掴むために、セットアップ後のコードを載せます。

```spec/systems/site_layout_spec.rb
require 'rails_helper'

RSpec.describe "SiteLayouts", type: :system do
  
  describe "home layout" do
    it "returns title with 'Lantern Lantern'" do
      visit root_path
      expect(page).to have_title 'Lantern Lantern'
    end
  end
  
  describe "about layout" do
    it "returns title with 'About | Lantern Lantern'" do
      visit about_path
      expect(page).to have_title 'About | Lantern Lantern'
    end
  end
end
```

今はこんな感じか、と思うだけで十分です。
今回は以上です。

---
前回：[#1 準備編](https://qiita.com/aokyo17/items/4df4cfd97891addc0452)
次回：[#3 Bootstrap4, jQueryプラグイン導入編](https://qiita.com/aokyo17/items/34ac84b75176d1df37b0)