RailsチュートリアルのMinitestをRSpecに翻訳するvol.1

## 投稿に寄せて
本記事は、Railsエンジニアの登竜門である `Railsチュートリアル` にて紹介されている 
テストフレームワークである `Minitest` を `RSpec` に書き直していきます。 
おそらく、Railsエンジニアを目指す方々が実際の業務で扱うテストフレームワークは 
`Minitest` に比べて、 `RSpec`の方が断然多いのではないでしょうか！ 
このことからもRSpecを学ぶメリットは十分にあると思います。 
本記事が、Rails勉強中の方の参考になれば幸いです。

## 概要
本記事はRailsチュートリアル(Rails 5.1)の第3章以降の本格的アプリ開発の部分を対象として、
MinitestからRSpecへのリファクタリングをおこなっていきます。

## 前提
Railsチュートリアル第3章と同じ条件で `rails new` したアプリの土台がすでにあること
https://railstutorial.jp/chapters/static_pages?version=5.1#sec-slightly_dynamic_pages

## RSpecで開発をする準備

ここでRSpecでテストを書いていく前にRSpecのセットアップをしていきましょう！

#### 必要Gemリスト

```
  gem 'rspec-rails'
  gem 'spring-commands-rspec'
  gem 'factory_bot_rails'
  gem 'faker'
  gem 'capybara'
  gem 'database_cleaner'
  gem 'launchy'
  gem "selenium-webdriver"
```

#### 各Gemの解説

`rspec-rails` 
RSpecを使用するために必要なgem。

`spring-commands-rspec`　
bin/rspecのコマンドを実行するときに必要になるもの。
rspecはbin/コマンドをつけることでSpringというRailsに組み込まれているアプリを起動させて処理を高速化出来る。 
rspec自体はrspecと打ち込むだけでも起動可能。  

`factory_bot_rails`　
テストの際に使用するデータを作成するためのもの。

`faker`　
実在しそうな名前でダミーデータを作成するためのもの。

`capybara` 
アプリケーション操作をRubyで設定して、ユーザがアプリケーションを使っているかのようにページを遷移させて、不具合検証するためのもの。主に画面に関わる結合テストで使用する。

`database_cleaner` 
逐一テストデータを削除するためのもの。

`launchy`　
Capybaraでテスト中に、現在どのページを開いているのか確認するためのもの。

`selenium-webdriver`　
複数テストの並行実行用。


## 実際のGemfile

```ruby
source 'https://rubygems.org'

gem 'rails',        '5.1.4'
gem 'puma',         '3.9.1'
gem 'sass-rails',   '5.0.6'
gem 'uglifier',     '3.2.0'
gem 'coffee-rails', '4.2.2'
gem 'jquery-rails', '4.3.1'
gem 'turbolinks',   '5.0.1'
gem 'jbuilder',     '2.7.0'

group :development, :test do
  gem 'sqlite3', '1.3.13'
  gem 'byebug',  '9.0.6', platform: :mri
  gem 'spring-commands-rspec'
end

group :development do
  gem 'web-console',           '3.5.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.0.2'
  gem 'spring-watcher-listen', '2.0.1'
  gem 'pry-rails'
  gem 'pry-byebug'
  gem 'pry-doc'
end

group :test do
  gem 'rails-controller-testing', '1.0.2'
  gem 'minitest-reporters',       '1.1.14'
  gem 'guard',                    '2.13.0'
  gem 'guard-minitest',           '2.4.4'
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'faker'
  gem 'capybara', '~> 2.13'
  gem 'database_cleaner'
  gem 'launchy'
  gem 'selenium-webdriver'
end

group :production do
  gem 'pg', '0.20.0'
end

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
```

## 下準備

- とりあえず bundle install

```ruby
bundle install --path vendor/bundle
```

- bin/rspecファイルを生成

```ruby
bundle exec spring binstub rspec
```

- RSpecの設定ファイル群を生成

```ruby
rails generate rspec:install
```

- .rspecに以下の項目を追記して、テスト結果出力をドキュメント形式に変更する

```ruby
<中略>
--format documentation
```

 - application.rbを編集して、無駄なファイルが生成されないようにする

```ruby
require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module RailsTutorial514
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.1

    config.generators do |g|
      g.test_framework :rspec,
                       fixtures: true,
                       view_specs: false,
                       helper_specs: false,
                       routing_specs: false,
                       controller_specs: true,
                       request_specs: false
      g.fixture_replacement :factory_bot, dir: "spec/factories"
    end
  end
end
```
- RSpecを実行出来るかコマンドで確認

```ruby
$ bin/rspec

No examples found.

Finished in 0.00018 seconds (files took 0.06751 seconds to load)
0 examples, 0 failures
```

## 準備が出来たら実際にやっていこう 

第3章の実装は下記のRailsチュートリアルの通り実装していく前提で進めます。
ここではテスト以外の実装には触れません。 
https://railstutorial.jp/chapters/static_pages?version=5.1#cha-static_pages

## 3.3.1 最初のテスト
※ Railsチュートリアルの項目3.3.1を参照

#### テストすべき項目
- Home画面が正常に表示されること
- Help画面が正常に表示されること

### Minitestで書いた場合

````test/controllers/static_pages_controller_test.rb
require 'test_helper'

class StaticPagesControllerTest < ActionDispatch::IntegrationTest

  def setup
    @base_title = "Ruby on Rails Tutorial Sample App"
  end

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

  test "should get help" do
    get static_pages_help_url
    assert_response :success
  end
end

```

### RSpecで書いた場合(画面表示周りのRSpecであるFeatureSpecで書く)

#### FeatureSpec(フィーチャースペック)の説明
FeatureSpecとはRailsのテストフレームワークのRSpecで行われるテストの種類の1つで、ブラウザ上の操作をシミュレートして実行結果を検証するテストのことです。
一般的には「統合テスト」や「エンドツーエンドテスト（E2Eテスト）」といった名称で呼ばれています。

ざっくりイメージで言うと、モデルとコントローラーがうまく一緒に動作することで表示も正しいものであることを検証するテストという理解で良いと思います！

以下は、今回Minitestから書き直したファイルです。

```/spec/features/static_pages_spec.rb
require 'rails_helper'

describe 'Home' do
  specify '画面の表示' do
    visit '/static_pages/home'
    expect(page).to have_css('h1', text: 'Sample App')
  end
end

describe 'Help' do
  specify '画面の表示' do
    visit '/static_pages/help'
    expect(page).to have_css('h1', text: 'Help')
  end
end
```

#### テストコードの補足
>`describe '〇〇' do` の〇〇の部分にテストしたい対象を記載する

>`specify '〇〇' do` の部分にテストしたい内容の詳細を記載する

>`visit 'URL'` 'URL'の部分に検証したい画面のURLを記載する

>`expect(page).to` はvisitしたカレントページを対象とするという意味

>`have_css` はテストの検証したい内容を計るためのメソッドで「マッチャ」(マッチを計るため)と呼ばれるものの1つです。 

>`have_css('h1', text: 'Help')` で HTMLのh1要素の内容が `Help` かどうかを検証している

>`expect(page).to have_css('h1', text: 'Help')` でカレントページはh1要素のテキストとしてHelpを持っているかを検証している


#### 実行コマンド（このコマンドでRSpecのテストファイルに記載したテストを実行出来る）

```ruby
bundle exec rspec spec/features/static_pages_spec.rb
```

#### 実行結果

![image.png](https://qiita-image-store.s3.amazonaws.com/0/222024/40d9f094-5e68-700a-e6d1-889dce94a100.png)


## 3.3.2 RED 〜 3.3.3 GREENまで

#### テストすべき項目
- Home画面が正常に表示されること
- Help画面が正常に表示されること
- About画面が正常に表示されること

#### Minitestで書いた場合

````test/controllers/static_pages_controller_test.rb
require 'test_helper'

class StaticPagesControllerTest < ActionDispatch::IntegrationTest

  def setup
    @base_title = "Ruby on Rails Tutorial Sample App"
  end

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

  test "should get help" do
    get static_pages_help_url
    assert_response :success
  end
  
  test "should get about" do
    get static_pages_about_url
    assert_response :success
  end
end

```

#### RSpecで書いた場合(画面表示周りのRSpecであるFeatureSpecで書く)

```/spec/features/static_pages_spec.rb
require 'rails_helper'

describe 'home' do
  specify '画面の表示' do
    visit '/static_pages/home'
    expect(page).to have_css('h1', text: 'Sample App')
  end
end

describe 'help' do
  specify '画面の表示' do
    visit '/static_pages/help'
    expect(page).to have_css('h1', text: 'Help')
  end
end

describe 'about' do
  specify '画面の表示' do
    visit '/static_pages/about'
    expect(page).to have_css('h1', text: 'About')
  end
end
```

```routes.rb
Rails.application.routes.draw do
  get 'static_pages/home'
  get 'static_pages/help'
  get  'static_pages/about'
  root 'application#hello'
end
```

#### 実行結果

![image.png](https://qiita-image-store.s3.amazonaws.com/0/222024/4538df34-30fa-3cbe-4983-66750f5ec34f.png)

## 3.4 少しだけ動的なページ

レイアウトファイル(Railsアプリの基盤となるテンプレート)の役割をよりわかりやすく説明するために、最初に　レイアウトファイルの`application.html.erb` の無効化をする

```ruby
 mv app/views/layouts/application.html.erb layout_file
```

## 3.4.1 タイトルをテストする(Red) 〜 3.4.2 タイトルを追加する(Green)

#### テストすべき項目
- Home画面タイトル "Home | Ruby on Rails Tutorial Sample App" が正常に表示されること
- Help画面タイトル "Help | Ruby on Rails Tutorial Sample App" が正常に表示されること
- About画面タイトル "About | Ruby on Rails Tutorial Sample App" が正常に表示されること

#### Minitestで書いた場合

```test/controllers/static_pages_controller_test.rb
require 'test_helper'

class StaticPagesControllerTest < ActionDispatch::IntegrationTest

  test "should get home" do
    get static_pages_home_url
    assert_response :success
    assert_select "title", "Home | Ruby on Rails Tutorial Sample App"
  end

  test "should get help" do
    get static_pages_help_url
    assert_response :success
    assert_select "title", "Help | Ruby on Rails Tutorial Sample App"
  end

  test "should get about" do
    get static_pages_about_url
    assert_response :success
    assert_select "title", "About | Ruby on Rails Tutorial Sample App"
  end
end
```

#### RSpecで書いた場合(画面表示周りのRSpecであるFeatureSpecで書く)

- ここでテストの内容もタイトルの内容を検証するように変更

```/spec/features/static_pages_spec.rb
require 'rails_helper'

describe 'Home' do
  specify 'タイトル内容の表示' do
    visit '/static_pages/home'
    expect(page).to have_title 'Home | Ruby on Rails Tutorial Sample App'
  end
end

describe 'Help' do
  specify 'タイトル内容の表示' do
    visit '/static_pages/help'
    expect(page).to have_title 'Help | Ruby on Rails Tutorial Sample App'
  end
end

describe 'About' do
  specify 'タイトル内容の表示' do
    visit '/static_pages/about'
    expect(page).to have_title 'About | Ruby on Rails Tutorial Sample App'
  end
end
```

#### テストコードの補足

>`have_title` もテストの検証したい内容を計るためのメソッドで「マッチャ」(マッチを計るため)と呼ばれるものの1つです。

## 実行結果

![image.png](https://qiita-image-store.s3.amazonaws.com/0/222024/cc869c04-855e-0390-1531-8fdeefd5f9b1.png)

## 3.4.4 ルーティングの設定

### ルーティングの変更

`static_pages#home`を root( `/` )のルーティングに変更

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

#### Minitestで書いた場合

```test/controllers/static_pages_controller_test.rb
require 'test_helper'

# rootのURLにアクセスするテストを追加
class StaticPagesControllerTest < ActionDispatch::IntegrationTest
  test "should get root" do
    get FILL_IN
    assert_response FILL_IN
  end

  <中略>
end
```

#### RSpecで書いた場合(FeatureSpec)

```/spec/features/static_pages_spec.rb
require 'rails_helper'

# Home画面をrootとしてそこにアクセスするテストに修正する
describe 'Home' do
  specify 'タイトル内容の表示' do
    visit '/' #　ここを変更
    expect(page).to have_title 'Home | Ruby on Rails Tutorial Sample App'
  end
end

<中略>
```

## 実行結果

- Home画面をrootに変わったテストも通り、既存のテストも通ることを確認

![image.png](https://qiita-image-store.s3.amazonaws.com/0/222024/7833a938-1980-90f0-54e9-a1e072f6fd43.png)

## 最後に

いかがだったでしょうか!
自分の周りでRSpecを慣れていきたい！もっと知りたい！！という声をよく聞きます。

実を言うと自分も普段Railsエンジニアをしているものの、RSpecをイチからセットアップしたり、なにも無いところからテストをゴリゴリ書いていくことってあんまりないんですよね...
(既存のコードを参考に追加のテストを書いてしまうこともある...)
そういった背景もあり、イチからテストのセットアップをしてテストコードを書く練習として、RailsチュートリアルのMinitestをRSpecへ翻訳させて頂きました。

時間を見つけて、第3章以降も翻訳を試みてみます。
初心者でRSpecをこれから練習したい！という方の良い回答サンプルになるような続編を頑張って書こうと思うので、続きも購読のほど、宜しくお願いします。 

## 追記
※ Rails5.1以降ではFeatureSpecに代わって、SystemSpecというFeatureSpecのテストが推奨されるようになっています。普段の業務で使っていることから、当たり前のようにFeatureSpecで書いてしまいましたが、書いた後で気づきました...