【Ralis】単体テストについて

## 最初に

Rspcを用いてモデルのバリデーションの単体テストを行った際の手順を備忘録として残します。

## Rspecとは
RubyやRailsの代表的なテストツールのことで、クラスやメソッド単位でテストするために使用するためのgem。

##環境
・Rails 5.0.7.2
・Ruby 2.5.1
・rspec-rails 3.9.0
・factory_bot_rails 5.1.1

## 事前準備

### 1.rspecとFactory_bot gemの導入

~~~
#gemfile

group :development, :test test do
#開発環境とテスト環境で使用したいのでこの中に記述。
gem 'rspec_rails'
gem 'factory_bot_gem' 

end
~~~
gemを記述できたらbundle install

###　2.rspecファイルの作成と動作確認

ターミナルで、

~~~
$ rails g rspec:install
~~~
コマンド実行後下記のようにファイルが作成されればok。
~~~
create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb
~~~

ファイルが作成されたのでターミナルで、

~~~.spec
--require spec_helper
--format documentation
~~~

この2文を.specファイルに追加してターミナルでrspec実行コマンドを行い動作確認。

~~~
$ bundle exec rspec
~~~

~~~
#ターミナル

No examples found.

Finished in 0.00031 seconds (files took 0.19956 seconds to load)
0 examples, 0 failures
~~~
このような記述がターミナルで出れば正常にRSpecが起動しているので、この後rspec/下のファイルにテストコードの記述を追加していく。

## テスト手順

### 1.rspecディレクトリ直下にfactoriesフォルダを追加して、その中にusers.rbファイルを作成

~~~users.rb
FactoryBot.define do

  factory :user do
    nickname        {"test"}
    email           {"test@gmail.com"}
    password        {"00000000"}
    family_name      {"test"}
    first_name      {"test"}
    family_name_kana    {"test"}
    first_name_kana    {"test"}
    birthday          {"2000-01-01"}
  end

end
~~~

factolesにモデルを先に作成して値を設定しておくことで、specファイルの中で特定のメソッドにより簡単にインスタンスを生成したり、DBに保存したりできるようになります。（１回１回テストを行うための値を入れて、データを作成しないで良くなる。）

~~~
user = FactoryBot.build(:user)
~~~
facutoryBotに対して、buildメソッドまたはcreateメソッドを使い、引数にfacutoriesフォルダ内で追加したモデルを指定することで~s.rbファイルで設定した値となる。

~~~
user = FactoryBot.build(:user,  email: "")
~~~
このように、モデルの後にカラム名の値を追加することにより、値を上書きできる。

### 2テストの記述を追加

specディレクトリにmodelsフォルダを生成。その中に各モデルのファイルを生成し実際のテストを行う。

~~~user_spec.rb
require 'rails_helper'
describe User do
  describe 'registrations#create' do

    it "nicknameが空欄の場合,登録できないこと" do
      user = FactoryBot.build(:user, nickname: "" )  
      user.valid?
      expect(user.errors[:nickname]).to include("を入力してください") #include("can't be blank")が正しい。日本語に変換されているため左のような形にしている。
  end
end
~~~

これで1つのテストのまとまりとなる。

1行目で、 spec/rails_helper.rb を読み込むよう設定。
2,3行目、describe直後のdo~endのまとまりが式を表す。ユーザーモデルのregistrations#createアクションのテスト式ということ。
it直後は動作するテストコードのまとまりを表し、itの後に続く""の中にはその説明を書きます。(exampleと呼ぶ）
次行でハッシュを生成して値をuser変数に代入。
ここでFactoryBotメソッドを使用。buildの引数にモデル名を指定して、nicknameカラムのみ値を上書き。
FactoryBotメソッドを使用すると下記は同じ値となる。

~~~usre_spec.rb

it "nicknameが空欄の場合,登録できないこと" do
      user = User.new(nickname: "", email: "test@gmail.com", password: "00000000", family_name: "test", first_name: "test", famliy_name_kana: "test", first_name_kana: "test", birthday: "2020-01-01") #gem未使用

      user = FactoryBot.build(:user, nickname: "" )　#FactoryBot使用時
~~~

毎回値をハッシュを生成しないようにFactoryBotmを使用する。
値を代入したuser変数に.valid?メソッドを使用。
ActiveRecord::Baseを継承しているクラスのインスタンスを保存する際に「バリデーションにより保存ができないか？」を確認。
Userモデルには今回全てのカラムに対して、presence: trueがバリデーションされています。

expect(X).to eq Yをしようして、xの部分に入れた式の値がYの部分の値と等しいかを確認。
ここではerrorsメソッドを利用。valid？メソッドの返り値はtrue/falseだが、valid?メソッドを利用したインスタンスvalid?メソッドを利用したインスタンスに対してerrorsメソッドを利用すると、バリデーションにより保存ができない状態である場合なぜできないのかを確認することができる。

今回であれば、nicknameカラムに値が入っていないので、”cant be blank” と出る。
includeマッチャを使用して、引数にとった値がexpectの引数である配列に含まれているかをチェック。
今回の場合、「nicknameが空の場合はcan't be blankというエラーが出るということがわかっているため、include("can't be blank")のように書く。実際にその通りになればこちらのエクスペクテーションは通過し、
"nicknameが空欄の場合,登録できないこと"というテストがこれで完了。

あとはひたすら単体テストの記述を追加して、1つ1つテストを行なっていく。

~~~user_rspec.rb
require 'rails_helper'
describe User do
  describe 'registrations#create' do

    it "nicknameが空欄の場合,登録できないこと" do
      user = FactoryBot.build(:user, nickname: "" )  
      user.valid?
      expect(user.errors[:nickname]).to include("を入力してください") #include("can't be blank")が正しい。日本語に変換されているため右のような形にしている。
    end

    it "emailが空欄の場合、登録できないこと" do
      user = FactoryBot.build(:user, email: "")
      user.valid?
      expect(user.errors[:email]).to include("を入力してください")
    end

    it "emailに「＠」がない場合、登録できないこと" do
      user = FactoryBot.build(:user, email: "test.gmail.com")
      user.valid?
      expect(user.errors[:email]).to include("は不正な値です")
    end   

    it "重複したメールアドレスの場合、無効である" do
      user1 = FactoryBot.create(:user)
      user2 = FactoryBot.build(:user)
      user2.valid?
      expect(user2.errors[:email]).to include("はすでに存在します")
    end

    it "passwordが空欄の場合、登録ができないこと"  do
      user = FactoryBot.build(:user, password: "" )
      user.valid?
      expect(user.errors[:password]).to include("を入力してください")
    end


    it "passwordが6文字以下の場合、登録ができないこと"  do
      user = FactoryBot.build(:user, password: "00000")
      user.valid?
      expect(user.errors[:password]).to include("は6文字以上で入力してください")
    end


    it "family_nameが空欄の場合、登録できないこと"  do
      user = FactoryBot.build(:user, family_name: "" )  
      user.valid?
      expect(user.errors[:family_name]).to include("を入力してください")
    end

    it "first_nameが空欄の場合、登録できないこと"  do
      user = FactoryBot.build(:user, first_name: "" )  
      user.valid?
      expect(user.errors[:first_name]).to include("を入力してください")
    end

    it "family_name_kanaが空欄の場合、登録できないこと"  do
      user = FactoryBot.build(:user, family_name_kana: "" )  
      user.valid?
      expect(user.errors[:family_name_kana]).to include("を入力してください")
    end

    it "first_name_kanaが空欄の場合、登録できないこと"  do
      user = FactoryBot.build(:user, first_name_kana: "" )  
      user.valid?
      expect(user.errors[:first_name_kana]).to include("を入力してください")
    end

    it "birthdayが空欄の場合、登録できないこと"  do
      user = FactoryBot.build(:user, birthday: "" )  
      user.valid?
      expect(user.errors[:birthday]).to include("を入力してください")
    end

  end
end

~~~

上記でuserモデルのバリデーションに対するテストが完成。

## 参考文献
・https://programming-beginner-zeroichi.jp/articles/61
・https://www.sejuku.net/blog/47847
・https://note.com/syojikishindoi/n/n5019bc64c254