RSpecテストの導入とコードのまとめ(初心者向け）

#概要
RSpecでテストするときに備忘録になるようにまとめました
間違いがある場合はご指摘ください。
#環境
- Ruby 2.6.5
- Ralis 6.0

```ruby:gem
pry-rails  #導入済みと仮定
RSpec
FactoryBot
Faker
```

#導入

```ruby:Gemfile
group :development, :test do
 #追記
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'faker'
end
```
```
$ bundle install
$ rails g rspec:install
```
```ruby:.rspec
--require spec_helper
--format documentation #追記（エラーをターミナルで確認しやすく
```
```ruby:spec/rails_helper.rb
 #中略
I18n.locale = "en"　　#追記（エラーを英語表記

RSpec.configure do |config|

#中略
```
[Faker](https://github.com/faker-ruby/faker)　
FactoryBot　の設定は省略

#各種テストファイルの作成
```
$ rails g rspec:model モデル名（単体テスト用）
$ rails g rspec:request コントローラー名（単体テスト用）
$ rails g rspec:system 機能名（結合テスト用）
```
##テストファイルの実行
```
$ bundle exec rspec    　　　　　　　　　　　　#テストファイル全部実行
$ bundle exec rspec spec/models　　　　　　　#modelなどのディレクトリ以下全部実行
$ bundle exec rspec spec/models/ファイル名　　#特定テストファイルのみ実行
```
他にもオプションあり

#全体構成

```ruby
require 'rails_helper'

RSpec.describe "機能名", type: :"テストカテゴリー" do  
#テストする機能名（大項目）とテストするカテゴリー（model / request / system )
  before do　　#テストする前の処理（事前に行う仮データ作成など）　　　　　　　　　　　　
    インスタンス変数[以下 @xxx と表現] = FactoryBot.build(:"仮データのモデル名")　
#作成する仮データの内容（ユーザー、投稿、コメント）
            　または、インスタンス変数 = FactoryBot.create(:"仮データのモデル名") 
#DBに保存され、テストごとにロールバック                       
  end
  describe '機能名' do　　　　　#機能名（小項目）（　〜の保存、〜の投稿、〜の登録）
    context "状況内容" do　　　#主に結合テストでテストする状況（〜できるとき：正常系、〜できないとき：異常系）
      it "テスト内容" do        #テスト内容（〜のときに〜できないorできる）
     -----[example]-----   　　　　　 #具体的なテスト処理のこと
     expect ( A ).to eq B   #Aした結果がBになるのを期待する　eq部分を[マッチャ]
     -------------------
      end
    end
  end
 end
```
##テスト実行時の見方
緑が成功、赤が失敗

```html:赤のときのターミナル表示
expected:   #こうなるはず
got:        #結果としてこうなった　
```

##[example] model基本形
```ruby:
1.保存するデータを作成 
#上の構成では、before do~endで作成している
2.テスト処理
#nameが空、emailが重複など
3.正しく保存されたかの確認
#正常系
expect(＠xxx).to be_valid   #正常系はここで終わり
#異常系
@xxx.valid?　　　　　　　　　
4.期待する異常、エラーメッセージ　#異常系の場合、下記エラーメッセージがでればOKということ
expect(@xxx.errors.full_messages).to include("エラーメッセージ")
```
```ruby:エラーメッセージ確認方法
#1. 処理をメッセージの知りたい直後（上記3.の後）に、
binding.pry  

#2.テストを実行すると、1.の部分でコンソール状態になるので、そこで
> @xxx.errors.full_messages
=> ["エラーメッセージ"]
```
```ruby:テスト処理例
#nameが空
@xxx.name = ""

#passwordが6文字以上
@xxx.password = "1234567"

#emailが重複
@xxx.save
zzz = FactoryBot.build(:"仮データのモデル名")
zzz.email = @xxx.email
```

##[example] request基本形
```ruby
1.対象アクションにリクエストする
#例：get root_path 

2.期待したレスポンスが返ってくる
#例：expect(response.status).to eq 200
```
```ruby:レスポンスの確認

#1. 処理を知りたい直後、上記1.のあと
binding.pry 

#2.テストを実行すると、1.の部分でコンソール状態になるので、そこで

 #HTTPステータスコード
> response.status
=> 200    #200なら処理の成功

 #ページの表示の確認
> response.body
=> （ブラウザ上のHTMLの情報） #省略
```
```ruby:テスト処理例
#root_pathへのリクエストでレスポンスが正常
get root_path
expect(response.status).to eq 200

#root_pathに、xxxが存在する
get root_path
expect(response.body).to include xxx # @xxx.text　や　"検索"といった言葉など
```

##[example] system（結合テスト）
```ruby
ユーザーが行う行動を分解
#ログイン
#ページ移動
#フォーム入力
#表示の確認....
```
```ruby:テスト処理--ページ移動/確認系
#特定ページへの移動
visit xxx_xxx_path

#visitで来たページにA（文字列など）があることの確認
expect(page).to have_content('A')

#visitで来たページにBがないことの確認
expect(page).to have_no_content('B')

#今いるページはトップページ（root_path）か確認
expect(current_path).to eq root_path

#ページに画像など特定のセレクタが存在することを確認
expect(page).to have_selector "要素を特定するもの"　# ".クラス名[style='〜〜〜']"など

#ページに画像など特定のセレクタが無いことを確認
expect(page).to have_no_selector "要素を特定するもの"

#特定のリンクがあることを確認(a要素に対して）
expect("要素").to have_link 'ボタンの文字列', href: "リンク先のパス"

#特定のリンクが無いことを確認(a要素に対して）
expect("要素").to have_no_link 'ボタンの文字列', href: "リンク先のパス"

```
```ruby:テスト処理--入力/動作系
#フォームへの入力
fill_in 'フォーム名', with: '入力する文字列'

#ブラウザ上の要素にカーソルを合わせた(クラス名なら　.クラス名　など）
find("ブラウザ上の要素").hover

#要素をクリックした
find("クリックしたい要素").click
#例:input要素のname属性 commitをクリックした
=> find('input[name="commit"]').click

#a要素で表示されたリンクをクリック
find_link("リンクの文字列", href: "URL").click

#ページに複数あるクラスの要素を取得
 all("クラス名")[添字]
#例：このページ内の3番目のbtnクラスにカーソルを合わせる
=> all(".btn")[2].hover

```
```ruby:テスト処理--モデル/データ変更系
#モデルのレコードが変更されたことの確認
expect{ "何かしらの動作" }.to change { モデル名.count }.by(数字)
#例:投稿ボタン（commit）をクリックしたら、投稿（postモデル）が1増えた
=> expect{find('input[name="commit"]').click
}.to change { Post.count }.by(1)
```

#特定のテスト処理をモジュール化する場合
spec/supportディレクトリを作成し、モジュール用のファイルを作成

```ruby:spec/support/xxxx.rb
module "モジュール名"
  def メソッド名（引数）
       #モジュール化する処理
  end
end
```
```spec/rails_helper.rb
#コメントを外す
23: Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }

34: RSpec.configure do |config|
       config.include "モジュール名" #追記
```


#あとがき
他にもテストコードの便利な書き方があるようですが、慣れてからにしたいと思います。
テストコードは仕様の理解にも重要なので、すらすらと記述できるよう、習熟していきます。