【Rspec】初心者のための内容まとめ

## はじめに
私がRspecを勉強する中で学んだ内容を初心者の方を対象にまとめています。
皆さんのRspec習得へのきっかけになれば幸いです。

## 対象となるRubyとRspecのバージョン
- Ruby  : 2.7.6
- Rspec : 3.11.0

## 目次
1. Rspecとは
1. Rspecの特徴
1. テストを書く際に意識するべきこと
1. Rspecの基本的な書き方
1. テストの種類
1. モデルスペック
1. リクエストスペック
1. 良く使うマッチャー
1. 学習に役立つ本
1. まとめ

## Rspecとは
[RSpec](https://github.com/rspec/rspec-rails)とは、RubyやRuby on Railsで作ったクラスやメソッドをテストするためのドメイン特化言語 (DSL)を使ったフレームワークです。
Rspecにテストコードを記述しておき、ソフトウェアテストを実行すると、自動的にテストコードに沿ったバグチェックを行えるツールです。

## Rspecの特徴
階層的な記載方法をすることでテストファイルからテスト対象の仕様が理解できます。

```hoge_spec.rb
describe 'テストの対象' do
  context '特定条件の内容' do
    it '期待する結果' do
      ...テスト処理...
    end
end
```

一度書いたテストは追加機能・修正対応・リファクタリングなどを行った際に、影響してるか否かを自動でチェックしてくれます。

## テストを書く際に意識するべきこと
テストを書くときには以下を意識することを心がけましょう。
- テストは信頼できるものであること
- テストは簡単に書けること
- テストは簡単に理解できること
- テストは成功する場合、失敗する場合の両方を書くこと

## Rspecの基本的な書き方
最も単純な書き方は以下のようになります。
```sample_spec.rb
describe '四則演算' do
  it '1 + 1 は 2 になること' do
    expect(1 + 1).to eq 2
  end
end
```
`describe`はテスト対象の機能を宣言します。
ここでは「四則演算」のテストを書くことを宣言しています。
`it`は期待する動作の詳細を記載します。
ここが期待通りに動作するかどうかでテストの成功/失敗がカウントされます。
`expect(X).to eq Y`は「検証対象」と「期待する結果」を記載します。
ここでは「1+1の結果は2になること」を期待しています。
 `to` と `eq` の部分はマッチャと呼ばれる機能ですが、後ほど`良く使うマッチャー`で説明します。

## テストの種類
Rspecでは以下のようにテストの種類によって名称が異なります。

■システムテスト
- システムスペック
- フィーチャースペック

■統合テスト
- リクエストスペック

■機能テスト
- コントローラースペック(Rails5以降非推奨)

■部品テスト
- モデルスペック

■その他
- ルーティングスペック
- ビュースペック
- ヘルパースペック
- メーラースペック
- ジョブスペック

ここでは私が業務で良く使用する「モデルスペック」「リクエストスペック」についてまとめていきます。

## モデルスペック
モデルに対するテストです。モデルのバリデーションやクラスメソッド、インスタンスメソッドをテストします。

例としてUserモデルのバリデーションテストを書いていきましょう。

■成功する場合
```spec/models/user_spec.rb
repuire 'rails_helper'　　#ヘルパーの読み込み

RSpec.describe User, type: :model do
  describe '#User' do
    it 'name, email, passwordがあれば有効な状態であること' do
      user = User.new(
        name:     "テスト太郎",
        email:    "hoge@hoge.com",
        password: "hogehoge"
      )
      expect(user).to be_valid　　#userが有効(valid)ならパスする
    end
  end
end
```

name, email, passwordを持つuserを生成し、それが有効かどうかを確認するテストです。
`expect(user(テスト対象)).to(が) be_valid(有効であること)`を確認しています。

■失敗する場合
```spec/models/user_spec.rb
repuire 'rails_helper'　　#ヘルパーの読み込み

RSpec.describe User, type: :model do
  describe '#User' do
    it 'nameがなければ無効であること' do
      user = User.new(
        name:     nil,
        email:    "hoge@hoge.com",
        password: "hogehoge"
      )
      expect(user.errors[:name]).to include("can't be blank")
    end
  end
end
```

nameを持たないuserを生成し、それが無効であることを確認するテストです。
userのnameをnilにした状態でvalid?メソッドを使ってuserの有効性を検証し、最後にuser.errors[:name]に"can't be blank"という内容のものが含まれていればパスするという内容です。

## リクエストスペック
HTTPメソッドでのリクエストの際の挙動を確認するテストです。
「Webリクエストが成功したか」「正しいページにリダイレクトされたか」「レスポンスのテンプレートに正しいオブジェクトが保存されたか」などを確認します。

例としてUser IDを指定すると、そのUserの会員情報を取得するAPIのテストを書いていきましょう。

■成功する場合
```spec/requests/api/user_info_spec.rb
repuire 'rails_helper'　　#ヘルパーの読み込み

RSpec.describe Api::User, type: :request do
  describe 'GET /user' do
    it 'ステータスコード(200)が返却されること' do
      id = 1　　#User IDに1を指定
      get 'api/user/#{id}'
      expect(response).to have_http_status(200)
    end
  end
end
```

リクエストが成功し、ステータスコード(200)が返却されることを確認するテストです。

■失敗する場合
```spec/requests/api/user_info_spec.rb
repuire 'rails_helper'　　#ヘルパーの読み込み

RSpec.describe Api::User, type: :request do
  describe 'GET /user' do
    it 'ステータスコード(422)が返却されること' do
      id = 9999  #存在しないID 9999を指定
      get 'api/user/#{id}'
      expect(response).to have_http_status(422)
    end
  end
end
```

リクエストが失敗し、ステータスコード(422)が返却されることを確認するテストです。

HTTPステータスコードの一覧については[こちら](https://ja.wikipedia.org/wiki/HTTP%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89)をご覧ください。

## 良く使うマッチャー

マッチャは「期待するデータと実際のデータを比較して、一致したor一致しなかったという結果を返すメソッド」です。
ここでは私がよく使うマッチャーを紹介します。

- **to / not_to**
■to
これは「XがYであること」を期待しています。
```
expect(1 + 1).to eq 2
```

　■not_to
　これは反対に「XがYでないこと」を期待しています。
```
expect(1 + 1).not_to eq 3
```

- **eq**
これは「XがYと等しいこと」を期待しています。
```
expect('hoge').to eq 'hoge'
```

- **include**
これは「XがYを含んでいること」を期待しています。
```
expect('hoge123').to include('hoge')
```

- **change + from / to**
これは「XがAからBへ変化すること」を期待しています。
```
#popメソッドを呼ぶと配列の要素が減少することをテストする
x = [1, 2, 3]
expect{ x.pop }.to change{ x.size }.from(3).to(2)
```

## 学習に役立つ本
最後に私が学習に使用した本を紹介します。

**Everyday Rails - RSpecによるRailsテスト入門**
RSpecを使ってRailsアプリケーションに信頼性の高いテストを書く実践的なアドバイスが書かれた本です。
Aaron Sumnerさん著の本を伊藤 淳一さんが日本語訳されたものです。
そこまで難しい内容ではありませんし、テストの在り方、扱い方について触れられておりとても読みやすい内容だと思います。
https://leanpub.com/everydayrailsrspec-jp

## まとめ
今回はRspecとは何か、何ができるのかを初心者の方向けにまとめました。
本内容がRspecを書き始める方の足がかりになれば幸いです。
ここまで読んでくださりありがとうございました！