RSpecのインストール方法と動かし方

# 記事の内容
RSpecをインストールして動作させるまでの手順

# 前提
- Ruby 2.3.3
- Rails 5.2.2
- RSpec 3.8

# 内容
## Rialsプロジェクトを作る
以下コマンドを順番に実行してRailsプロジェクトを作る

```console:console
mkdir rspec_test
cd rspec_test
rails new .
```

**注意**
※プロジェクト名をRSpecにするとうまく動かないので注意

## RailsプロジェクトにRSpecをインストールする
gemファイルの「test」にRSpecを追加する。

```gemfile:Gemfile
group :test do
 ...
 ...
 gem 'rspec-rails'
end
```

gemファイルの内容をインストールする

```console:console
bundle install
```

rspecの構成ファイル/構成フォルダを作成する。

```console:console
// 実行するとspecフォルダができる
rails g rspec:install
```

## テスト対象クラスを作る
今回はtitleカラムを持つTodoモデルをテスト対象とする。
以下のコマンドを実行してテスト対象クラスを作る。

```console:console
rails g model todo
```

マイグレーションファイルを下記のように編集する。

```ruby:20190202080103_create_todos.rb
class CreateTodos < ActiveRecord::Migration[5.2]
  def change
    create_table :todos do |t|
      t.text :title
      t.timestamps
    end
  end
end
```

下記のコマンドを実行してマイグレーションを行う。

```console:console
rails db:migrate
```

## テストコードを作成する
以下のコマンドを実行してテストコードの雛形を作成する。

```console:console
rails g rspec:model todo
```

テストコードを記述する。

```ruby:todo_spec.rb
require 'rails_helper'

RSpec.describe Todo, type: :model do
  describe "#title" do
    context "空の場合" do
      let(:todo){Todo.new(title:"")} #titleが空文字のTodoオブジェクトを生成

      it "エラーを返す" do
        todo.valid? #バリデーションを実行
        expect(todo.errors[:title]).to be_present #期待結果：エラーメッセージが存在する
      end
    end

    context "Nullの場合" do
      let(:todo){Todo.new(title:nil)} #titleがNullのTodoオブジェクトを生成

      it "エラーを返す" do
        todo.valid? #バリデーションを実行
        expect(todo.errors[:title]).to be_present #期待結果：エラーメッセージが存在する
      end
    end
    
    context "値が入っている場合" do
      let(:todo){Todo.new(title:"this is title")} #titleに値が入っているTodoオブジェクトを生成

      it "エラーを返さない" do
        todo.valid? #バリデーションを実行
        expect(todo.errors[:title]).to be_blank #期待結果：エラーメッセージが存在しない
      end
    end
  end
end
```

describeに「テスト概要」を記述して、contextに「テスト条件」を記述して、itに「期待結果」を書く感じ。
期待結果の書き方は、下記の通り。

```expect記法
expect(検証対象変数).to [期待結果]
```
より詳細なexpectの書き方は、[こちらの記事](https://qiita.com/jnchito/items/2e79a1abe7cd8214caa5)がわかりやすい。

## テストを実行する
下記のコマンドを実行してテストを実行する

```console:console
rspec
```

下記のような結果が返ってくる。

```console:console
  1) Todo#title 空の場合 エラーを返す
     Failure/Error: expect(todo.errors[:title]).to be_present
       expected `[].present?` to return true, got false
     # ./spec/models/todo_spec.rb:9:in `block (4 levels) in <top (required)>'

  2) Todo#title Nullの場合 エラーを返す
     Failure/Error: expect(todo.errors[:title]).to be_present
       expected `[].present?` to return true, got false
     # ./spec/models/todo_spec.rb:17:in `block (4 levels) in <top (required)>'

Finished in 0.04147 seconds (files took 1.89 seconds to load)
3 examples, 2 failures

Failed examples:

rspec ./spec/models/todo_spec.rb:7 # Todo#title 空の場合 エラーを返す
rspec ./spec/models/todo_spec.rb:15 # Todo#title Nullの場合 エラーを返す
```

「空の場合」と「Nullの場合」はテストに失敗していることがわかる。
「値が入っている場合」は期待通りの結果になっているので、テストが成功していることがわかる。

## テスト対象コードを修正する
「空の場合」と「Nullの場合」に、テストの期待結果通りバリデーションエラーが発生するようにコードを修正する。

```ruby:todo.rb
class Todo < ApplicationRecord
    validates :title, presence: true
end
```

## テストを実行する
以下のコマンドを実行してテストを行う。

```console:console
rspec
```

下記のような結果が返ってくるはず。

```console:console
Finished in 0.02788 seconds (files took 0.95229 seconds to load)
3 examples, 0 failures
```

テスト失敗がなくなっていることがわかる。

## (2019/2/2 追記) Subjectを使ったRSpecの書き方
Subjectメソッドを使うと、もっと簡潔に書ける。

```ruby:todo_spec.rb
require 'rails_helper'

RSpec.describe Todo, type: :model do
  describe "#title" do
    #テストする対象ロジック
    subject{ todo.valid?; todo.errors[:title] }

    context "空の場合" do
      # titleが空のオブジェクトを作る
      let(:todo){Todo.new(title:"")}

      # titleが空のときの期待結果を定義する
      it { should be_present }
    end

    context "Nullの場合" do
      let(:todo){Todo.new(title:nil)}
      it { should be_present }
    end
    
    context "値が入っている場合" do
      let(:todo){Todo.new(title:"this is title")}
      it { should be_blank }
    end

  end
end
```

subjectに「テスト対象の処理」を記述して、itに「その期待結果」を記述する。
subjectを使うとitの方の記述が簡単になる。

## 感想
JUnitしか書いたことがなかったが、RSpecは「振る舞い駆動」のテストケースを書きやすくて良いなと思いました。TDDもやりやすいんじゃないかな。

終わり。