Rails RSpecの準備とテストコード基礎

# はじめに
+ RSpecを利用した基礎的なテストコードを書けるようになるために準備をすすめる。

# RSpecの準備
まずはRailsに 「rspec-rails」というGemをインストールし、RSpecを利用できるようにする。次にRSpecの設定をし、その後、実際にRSpecのコードを書いてRSpecの基礎文法を学習したいと思う。
# Gemのインストール
はじめにRSpecを利用するためのGem 「rspec-rails」をインストールする。

__● テキストエディタで、作成したアプリのディレクトリを開く__

#### RailsアプリのGemfileの下部を以下のように編集する
'gem 'rspec-rails'をいう部分を追記する。
また```web_console```というgemはtest環境で動かすと不具合が起きる可能性があるgemなのでdevelopment環境でのみ動くようにする。
新しく ```group :development do ~ end```というブロックを作成し、その間に移動させる。
※ group :development do ~ endの記述が既にある場合はその記述の間にweb_consoleを入れれば良い。

```ruby:Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'pry-rails'
  gem 'compass-rails'
  gem 'sprockets'
  gem 'kaminari'
  gem 'devise'
  gem 'rspec-rails'
end

group :development do
  gem 'web-console'
end
```
Gemfileを編集したら、 ```bundle install```をする。
# RSpecの設定をする
__● ターミナルから以下のコマンドを実行する__

```ruby:ターミナル
$ rails g rspec:install
# RSpec用設定ファイルの作成
```
下記のようにファイルが作成されれば成功

```rails:ターミナル
  create  .rspec
  create  spec
  create  spec/spec_helper.rb
  create  spec/rails_helper.rb
```
上記作業によって作成された２つのファイルについての説明をしておくと、、、
#### rails_helper.rb
RailsにおいてRSpecを利用する際に、共通の設定を書いておくファイルである。各テスト用ファイルでこのファイルを読み込むことで、共通の設定や、メソッドを適用する。
#### spec_helper.rb
rails_helper.rbと同じくRSpec用の共通の設定を書いておくファイルだが、このファイルはRSpecをRails無しで利用する際に利用する。
### .rspecに以下を追加する
```ruby:.spec
--format documentation
```
以上で準備は完了である。
# RSpecが正常に利用できるか確かめてみる
### ターミナルから以下のコマンドを実行する
```ruby:ターミナル
$ bundle exec rspec
```
以下のような結果になれば、RSpecを利用する準備はできている。

```ruby:ターミナル
No examples found.

Finished in 0.00031 seconds (files took 0.19956 seconds to load)
0 examples, 0 failures
```
# アプリの元のコードを編集する
テストコードを書く際に、バリデーションが正常に動くようにコードを修正する。
### app/views/devise/registrations/new.html.erbを以下のように編集する
nicknameを入力するホームを出力している```text_field```タグに付いている```maxlength: 6```をいう記述を削除する。

```ruby:devise/registrations/new.html.erb
  <h2>Sign up</h2>
  <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
    <%= devise_error_messages! %>

    <div class="field">
      <%= f.label :nickname %> <em>(6 characters maximum)</em><br>
      <%= f.text_field :nickname, autofocus: true %>
    </div>
 # 省略
```
続いてuser.rbを編集する。
### app/models/user.rbを以下のように編集する
Userクラス内に、```validates :nickname, ~```の記述を追加する。

```ruby:user.rb
  class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_many :tweets

  validates :nickname, presence: true, length: { maximum: 6 }

end
```
これで準備は完了。
# モデルクラスのテストコードを書く
まずはじめに、Railsにおいて基礎となる単体テストであるモデルのバリデーションに関するテストコードを書く。
「userモデル」が存在するアプリに対応するテストコードを書いていく。
#### specファイルの命名規則
__specファイルは```対応するクラス名_spec.rb```という名前になる。__今回はまず「user.rb」に関するspecファイルを作成するので、その場合の名前は「user_spec.rb」になる。
#### テストコードの基本
「1 + 1が2になることを確かめる」という簡単なテストコードを例として、以下に挙げる基本的なテストコードの文法について書いてみる。
## 【例】 1 + 1が2になることを確かめるテストコード
```ruby:sample_spec.rb
describe "hogehoge" do
  it "1 + 1は2になること" do
    expect(1 + 1).to eq 2
  end
end
```
上記のコードの概要を説明する。
1行目の```describe```というキーワードでテストをグループ化する。
続いて、テスト１つ(__example__)として評価される```it do ~ end```のブロックの中に、```expect(x).to eq Y```という形式の式を書いていく。これが、実際にテストが成功するかどうかチェックされる式(__エクスペクテーション__)になる。
以下に挙げるポイントについて説明。

+ __describe__
+ __itとexample__
+ __エクスペクテーション__
+ __expectメソッド__
+ __マッチャ__

#### ● describe
```describe```は、直後の```do ~ end```までのテストのまとまりを作る。```describe```の後に続く```""```の中にはそのexampleの説明を書く。
#### ● itとexample
2行目の```it```は__example__と呼ばれる実際に動作するテストコードのまとまりを表す。```it```の後に続く```""```の中にはそのexampleの説明を書く。
#### ● エクスペクテーション
実際に評価される式のことである。```it do ~ end```の間に書く。上記の式では```expect(1 + 1).to eq 2```の部分がエクスペクテーションである。
#### ● expect(x).to eq Y
エクスペクテーションの文法である。xの部分に入れた式の値がYの部分の値と等しければ、テストが成功する。```eq```の部分を、マッチャと言う。
#### ● マッチャ
エクスペクテーションの中で、テストが成功する条件を示す。例にあげると```eq```は「等しければ」という意味になる。他にも```include```(含んで入れば)、```valid```(バリデーションされれば)など複数のマッチャが存在する。
# 試しにバリデーションのテストを書いてみる
### rails アプリのディレクトリに、spec/modelsディレクトリを作成する
spec/models/user_spec.rbを作成する。
### user_spec.rbを以下のように編集する
```ruby:user_spec.rb
require 'rails_helper'
describe User do
  describe '#create' do
    it "is invalid without a nickname" do
    end
  end
end
```
```it ~ do```の間はそのexampleの説明を書くが、ここは日本語でも問題ない。
この状態でテストを実行する。テストを実行するには、ターミナルで```bundle exec rspec```というコマンドを実行する。
現状、user_spec.rbには中身のないテストコードが書かれている状態である。
#### ● bundle exec rspecコマンド
RSpecのテストコードを利用したテストを実行するためのコマンドである。
### ターミナルから、テストを実行する
```ruby:ターミナル
$ bundle exec rspec
```
ターミナルに以下のように表示されれば、テストを正常に実行できている。

```ruby:ターミナル
User
  #create
    is invalid without a nickname

Finished in 0.1379 seconds (files took 3.28 seconds to load)
1 example, 0 failures
```
この段階ではまだテストとして評価される式を書いていないので、テストは無条件でパスする。

1行目の```require 'rails_helper'```は、rails_helper.rb内の記述を読み込むことで共通の設定を有効にしている。この1行目の記述は、全てのspecファイルに書き込む。3,4行目に連続して```describe```が書いてあるが、```describe```は、ネスト(入れ子状)にすることができる。ここでは「userクラスにあるcreateメソッドをテストするまとまり」であることを示している。このように、```describe```と```do```の間にメソッド名を書く際は```#```をつけるのが習慣である。
# nicknameが空の場合登録できないことを確かめるテストコードを書く
「nicknameが空の場合登録できないことを確かめる」exampleを書いていく。
### ● user_spec.rbを以下のように編集する
```ruby:user_spec.rb
require 'rails_helper'
describe User do
  describe '#create' do
    it "is invalid without a nickname" do
     user = User.new(nickname: "", email: "kkk@gmail.com", password: "00000000", password_confirmation: "00000000")   # ⑤
     user.valid?  # ⑥
     expect(user.errors[:nickname]).to include("can't be blank")  # ⑦ 
    end
  end
end
```
上記のコードで追記した⑤,⑥,⑦の行の流れは以下である。

__⑤の行でテストしたいプロパティを持ったuserクラスのインスタンスを新規作成する__
スペックファイルの中では、そのRailsプロジェクトで作成しているモデルクラスを利用することができる。今回は「nicknameが空である場合登録できないこと」を確かめるテストコードを作成したいのでnicknameの値を空にし、それ以外は適当な値をセットした状態でuserクラスのインスタンスを作成している。
  
__⑥の行で作成したインスタンスがバリデーションによって保存できない状態がチェックする__
新規作成したuserクラスのインスタンスがバリデーションに引っかかるかどうかを確かめる```valid?```メソッドを利用する。
#### ● valid?メソッド

+ ```valid?```メソッドを利用すると、ActiveRecord::Baseを継承しているクラスのインスタンスを保存する際に「バリデーションにより保存できない状態であるか」を確かめることができる。

#### ● errorsメソッド
+ ```valid?```メソッドの返り値はtrue/falseだが、```valid?```メソッドを利用したインスタンスに対して```errors```メソッドを利用すると、バリデーションにより保存ができない状態である場合なぜできないのかを確認することができる。

### ターミナルから以下の指示の通りコマンドを実行する
```ruby:ターミナル
#コンソールを立ち上げる
$ rails c
#nicknameの値が空であるuserクラスのインスタンスを作成する
>user = User.new(nickname: "", email: "kkk@gmail.com", password: "00000000", password_confirmation: "00000000")
#valid?メソッドを利用する
>user.valid?
#errorsメソッドを利用する
>user.errors
=> #<ActiveModel::Errors:0x007ffa6ce07ef0
 @base=
  #<User:0x007ffa6d3430b8
#中略
 @messages={:nickname=>["can't be blank"]}>
```
下部の```@message=~```に注目する。この部分が、```valid?```によって追加された、そのインスタンスが保存できない理由である。上記の場合、「nicknameカラムに値が入っていなければ保存できない」と言われていることになる。

※ここで記述されている```"can't be blank"```は、元々Rails Gemで用意されているエラーメッセージである。このエラーメッセージは、RailsのGem本体に記述されている。
#### ⑦の行はチェックした結果インスタンスが持つエラー文が期待したものであるか確かめる
⑦の行は、以下のようなコードになっている。

```ruby:user_spec.rb
expect(user.errors[:nickname]).to include("can't be blank")
```
#### ● includeマッチャ
includeマッチャは、引数にとった値がexpectの引数である配列に含まれているかをチェックすることができるマッチャである。

今回の場合、「nicknameが空の場合は__can't be blank__というエラーが出るはずだ」ということがわかっているため、```include("can't be blank")```のように書くことができる。
実際にその通りになればこちらのエクスペクテーションはパスし、このコードは意図した動作をすると保証できる。
### ターミナルから以下のようにテストを実行する
```ruby:ターミナル
$ bundle exec rspec
```
以下のような結果になれば成功である。

```ruby:ターミナル
$ bundle exec rspec

User
  #create
    is invalid without a nickname

Finished in 0.53132 seconds (files took 3.64 seconds to load)
1 example, 0 failures
```
以上を参考にメールアドレスが存在しなければ登録できないことを確かめるテストコードを書いてみて欲しいです。
user.rbにまずはemailのバリデーションをかけるのを忘れずに⭐️