単体テストコードの書き方(Rails)

## はじめに

昨日、テストコードを書く意味と導入手順の記事を書いたので
本日は、単体テストコードの書き方を記事にしたいと思います。

テストコードの記事多くなっていますが、新人エンジニアが任される
ことの多い仕事と聞いたので、オリジナルアプリを使用し入念に学習しています。

テストコードの理解が浅い方はこちらを見てからだとわかりやすいかもしれません。

[テストコードについて(Ruby on Rails)](https://qiita.com/tetsu1026/items/2bcec208647c6cdadc17)

Rspec、FactoryBot、Fakerは導入済みです。
導入の仕方は上記の記事に書いてあります。

## コードを書く前の準備

1.ファイルを作成しよう
今回は、Userモデルの単体テストコードを書きたいと思います。
そこで、コードを書くファイルをを作りましょう。

ファイルの作り方は、ターミナルに以下のコマンドを打ち込みましょう。

```
% rails g rspec:model user
```
すると、

```
create  spec/models/user_spec.rb
```

これで、ファイルの作成は完了です。

2.バリデーションを確認しよう。
モデルの単体テストコードは、バリデーションの確認です。
自分の記述した、バリデーションが上手く行っているかを確認します。
なので、書く前に一度バリデーションを見直すと書きやすくなります。

models/user.rb

```ruby
class User < ApplicationRecord

  PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i.freeze
  validates_format_of :password, on: :create, with: PASSWORD_REGEX, message: 'には半角の英字数字の両方を含めて設定してください'

  with_options presence: true do
    validates :nickname
    validates :profile
  end

end
```
今回私が組んだバリデーションは
・パスワードは半角英数混合
・ニックネームが空では登録できない
・プロフィールが空だと登録できない
上記3つです。

さらに、ユーザー管理機能にはdeviseを導入しています。
deviseのデフォルトのバリデーションが、
・パスワードは6字以上
・emailには@が必要
・同じemailは登録できない
・パスワードは確認用含め2回入力が必要

これらのバリデーションがしっかり組まれているかを記述します。

## テストコードを書いてみよう

### 作成したファイルの中身を確認しましょう。

spec/models/user_spec.rb

```ruby
require 'rails_helper'

RSpec.describe User, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end
```

1番上に記述されているrails_helperについて。
これは、Rspecを用いてRailsの機能をテストするときに、共通の設定を書いておくファイルです。各テスト用ファイルでspec/rails_helper.rbを読み込むことで、共通の設定やメソッドを適用します。
rails gコマンドでテストファイルを生成すると、rails_helperを読み込む記述が、自動的追加されます。

### FactoryBotを生成しインスタンス変数に代入しましょう。

spec/models/user_spec.rb


```ruby
require 'rails_helper'

RSpec.describe User, type: :model do
  before do
    @user = FactoryBot.build(:user)
  end
```
これで、@userの中にFactoryBotで作成したユーザー情報が入りました。


### exampleを整理しよう

exampleとは簡単に説明すると、確認すること整理しましょうということです。
先ほどバリデーションを確認したと思うのですがそのことですね。
・パスワードは半角英数混合
・ニックネームが空では登録できない
・プロフィールが空だと登録できない
・パスワードは6字以上
・emailには@が必要
・同じemailは登録できない
・パスワードは確認用含め2回入力が必要
これらが、exsampleとなります。

ここで、テストコードにでてくるメソッドを事前に説明させていただきます。

・describe
テストコードのグループ分けを行うメソッドです。
「どの機能に対してのテストを行うか」をdescribeでグループ分けし、その中に各テストコードを記述します。

・it
itメソッドは、describeメソッド同様に、グループ分けを行うメソッドです。
itの場合はより詳細に、「describeメソッドに記述した機能において、どのような状況のテストを行うか」を明記します。

・context
contextは、特定の条件を指定してグループを分けます。
使用方法はdescribeと同じですが、describeには何についてのテストなのかを指定するのに対し、contextには特定の条件を指定します。

### 正常系テストコード

それでは、正常系のテストコードから書いてみましょう

spec/models/user_spec.rb

```ruby

describe "新規登録" do
    context "登録できる時" do
      it "フォームに情報が正しく入力されていれば登録できる" do
        expect(@user).to be_valid
      end
```

新規登録の登録ができる時についてコードを書いていきたいと思います。

・expect
検証で得られた挙動が想定通りなのかを確認する構文のことです。
expect().to matcher()を雛形に、テストの内容に応じて引数やmatcherを変えて記述します。

・matcher（マッチャ）
matcherは、「expectの引数」と「想定した挙動」が一致しているかどうかを判断します。
expectの引数には検証で得られた実際の挙動を指定し、マッチャには、どのような挙動を想定しているかを記述します。


@userがexpextで、be_validがマッチャになります。
つまり、@userの中身が意図したものになっているかを確認するものです。

ここで、be_validとvalidメソッドについて説明します。

・be_valid
be_validとは、valid?メソッドの返り値が、trueであることを期待するマッチャです。

・valid?
valid?は、バリデーションを実行させて、エラーがあるかどうかを判断するメソッドです。
エラーがない場合はtrueを、ある場合はfalseを返します。
また、エラーがあると判断された場合は、エラーの内容を示すエラーメッセージを生成します。

この場合、エラーがないのでtrueを返します。なので、マッチャがbe_validとなります。
すごく簡単に説明すると正常系の時は、be_validを記述します。


### 異常系のテストコード

今回は「新規登録できない時」のemailの部分の一部を抜粋し載せたいと思います。

spec/models/user_spec.rb

```ruby
context "登録できない時" do
      it "emailの入力がないと登録できない" do
        @user.email = nil
        @user.valid?
        expect(@user.errors.full_messages).to include("Email can't be blank")
      end

```

まず、「emailがないと登録できない」について
実際にemailがないと登録できないのか確認してます。
なので、@userの中のemailを一度空にします。
そして、valid?メソッドを使用してエラーがあるかを確認します。

そして、

```ruby
 expect(@user.errors.full_messages).to include("Email can't be blank")
```

デフォルトでemailは空だと登録できないバリデーションが組まれています。
valid?メソッドを使用したとき、falseが返ってくるはずです。
ここでの挙動の確認ですが、@userのエラーメッセージの中に意図したものが入っていれば
確認できます。
ちなみに、includeは「expectの引数」に「includeの引数」が含まれていることを確認するマッチャです。
つまり、@user.errors.full_messagesの中に"Email can't be blank"
のが含まれていれば、バリデーションが正しいかわかります。

どのように確認するのか?
デバッグをして確認します。

railsでは、デバックを行うにはpry-railsとういGemを導入して、
binding.pryという機能を使うことで確認できます。

pry-railsの導入の説明は省かせていただきます。
興味あるかたは調べてみてください。


```ruby

 context "登録できない時" do
      it "emailが空だと登録できない" do
        @user.email = nil
        @user.valid?
        binding.pry
        expect(@user.errors.full_messages).to include("Email can't be blank")
      end
```
binding.pryの記述のところで、処理が一時停止します。

ターミナルより確認してみましょう。
以下のコマンドを実行します。

```
% bundle exec rspec spec/models/user_spec.rb 
```

[![Image from Gyazo](https://i.gyazo.com/cd858121eae3b5c4d0b09e405d64bce0.png)](https://gyazo.com/cd858121eae3b5c4d0b09e405d64bce0)

すると、処理が停止しコンソールが起動していることがわかります。
ここに、

```
@user.errors
```
こちらを打ち込むと、@userのエラーメッセージが確認できます。
しかし、これを全部打ち込むことはできないので、

```
@user.errors.full_messages
```
[![Image from Gyazo](https://i.gyazo.com/b8958c2d60eecb27d4581998ee056103.png)](https://gyazo.com/b8958c2d60eecb27d4581998ee056103)
これで、バリデーションを確認することができました。
ちなみにコンソールを終了する場合は、exitを入力します。

これで、ターミナルよりもう一度

```
% bundle exec rspec spec/models/user_spec.rb 
```

コマンドを入力し、確認します。
エラーがでなければ次のテストコードを書いていきます。

ここまでが、モデルの単体テストコードの書き方の説明です。
実際に書けても説明するのはとても難しかったです。

至らない点もあるかと思いますが、何かありましたらコメント
残してくだされば幸いです。