RSpecの書き方を1mm理解したので手順とかの忘備録を残す


RSpecが苦手だ!!!

と思い現実逃避していましたが、テストの大事さについて認識、RSpecについて1mmだけ理解したので忘備録を書きます。
超初心者で、理解が異なっていたらご指摘いただけると嬉しいです。

現在の私の状況:PFを作成していて、少しRuby、Railsについて分かってきた程度。現場の事は全然知らないです。

なんでRSpecを書くの?

A.コードを書き替えた時や、仕様変更などで正しい動きをするかを素早く正確に確認する為。

私は1つ目のPFを作った時は、テストは手動でいいじゃん!と思っていました。なぜなら確認する項目がめっちゃ少ないから。
2つ目のPFを作成していますがここでテストの重要性に気づきます。

「ここ同じような記述だからまとめちゃお~」
と思ってなんとなくまとめていたら、

  • こっちではparamsは[:id]じゃなくて[:item_id]で取得しててエラーが起きた!
  • バリデーションが間違ってかかってて保存できなくなった!
  • まとめた時に記述抜け落ちててバリデーション無くなっちゃった!
  • 眠くて見逃してたけどここ空白でも保存されちゃってるじゃん!

と次々に問題発生。
1つ直す→10チェックする→1つ間違いを見つける→直す→また10チェックする・・・
あれ、手動で同じ事何回もやってる・・・と気付きました。

  • RSpecに任せれば手動よりも簡単に同じテストが出来る!
  • ヒューマンエラーでテスト項目見逃すなんてことが無い!

RSpecはこの為に書くべきなんですね~(多分)

RSpecってどういうものなの?

A.実際のサイトの『スクリーンショット』とRSpecが示す値を比べるテストをするもの
今はこういう解釈でいます。もしかしたら間違っているかも。

例えばこういうログイン画面があったりします。

でパスワードが空欄だった時にエラーメッセージが出る仕様だったとします。(下の図)

手動で見たら、「あっ、エラー出てるな、よしよし。」となるのですが、RSpecでは

  • 「エラー」という言葉が画面に出ているか?
  • 遷移したページはnew_session_user_pathか?
  • 入力が無効(be_invalid)だったか?

みたいな感じでテストを書きます(多分)
RSpecくんが、出た結果(ここではページ)のスナップショットを見て、指定された部分が正しいか確認してくれるんですね。↓こんな感じで

でも、ここで注意なのが、

  • 「エラー」という言葉が画面に出ているか?

というテストを「ログイン」という言葉が画面に出ているか?という記述でテストを書いてしまうと、エラーが出ていなくても、画面にでかでかと「ログイン」の言葉があるので、エラーが出ても出なくてもテストOKという結果になってしまいます。注意です。
そんな書き方しないよ~と思うかもしれませんが、この考え方が結構大事で、意外と忘れがち。
例えば、税率計算だったとすると、

いちごオムレットの値段が正しく表示されているのかの確認では「sweet.priceに1.1かけたものと正しい」と書くか、「値段が1000円だったときに1,100円と表示される」と書くか悩みますよね。

ここで、気付いた人は気づいたと思うのですが、税込表示の値段を出す時にsweet.price * 1.1ではなく((sweet.price * 1.1).round(2)).ceilみたいな書き方をしないと1円の商品があった時に1.1円になってしまいます。(おいおい画像付きでミスリード誘ってたのかよ!と思った方すみません。画像作ってから思い出しました。)

数式で書くとこういうミスに気づきにくいので、RSpecのテストでは1,100円とベタ書きするのが良いのかなあと思います。
他にも『バリデーションのテストはOKだったけど、実は正しい時でもバリデーションかかってしまって本末転倒!』ということが発生したりするので、正しい場合は正しい動作をするかを書いておいたり、多方面から考えてテストとして機能しているのか確認するのは大事かなあと思います。

実際どうやって書くのか教えてよ!

A.まだ苦手で全然書けないので参考にしないで欲しいけど書きます(恐怖)

プログラムは作っていないけど体感してみたいという人は②まで、実際になんかプログラムを作っているよ!という人は③以降も出来るはず・・・!
出来なくても教えられるほどの力量は私にはありません・・・(´;ω;`)

①テストの準備

Gemfile
group :test do
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  gem 'webdrivers'
  gem 'rspec-rails'
  gem "factory_bot_rails"
  gem 'faker'
end

Gemファイルのtestの中をこんな感じにして

コンソール
$ bundle install
$ rails g rspec:install

をする。そうすると新しくspecというフォルダを作成し、RSpecに必要なヘルパーとかを作ってくれます。

spec/rails_helper.rb
RSpec.configure do |config| #の中に
  config.include Devise::Test::IntegrationHelpers, type: :system #これを追加
end

↑これはdeviseを使っている人が記述するとRSpec内でsign_in @userのような記述を使う事が出来るようになります。

spec/spec_helper.rb
require 'capybara/rspec' #追加
require 'devise' #追加
RSpec.configure do |config|
  config.before(:each, type: :system) do #この3行追加
    driven_by :rack_test
 end
:

↑これはcapybaraという補助機能(?)を有効にするための記述で、これを行うとvisit root_pathという書き方が出来ます。visit root_pathは「RSpecくん!rootパスまで移動してくれ!」という指示になります。

大事なのは、モデルのチェックはモデル!遷移などのチェックはsystemに書く!ということ。

上のコードに何気なくtype: :systemと書いてありますが、これは「タイプがシステムという所だけでこの補助機能を有効にするよ~」という意味です(多分)。後述しますが、type: :modelなどの所には使えません。ここで私はめっちゃ悩んだ。

②まずはコードを書かずに実行してみる

specというフォルダの中に、test_spec.rbというファイルを作って、

spec/test_spec.rb
require 'rails_helper'

RSpec.describe "RSpecを書いてみるよ!" do
  describe "ここが段落" do
    context "ここが小段落" do
      it "ここでテストを記述するよ" do
      end

      it "何も書かなければ緑になります" do
      end
    end
  end
end

って書いてみてください。そしたら、コンソールで、

コンソール
~/environment/test_app $ rspec spec/test_spec.rb --color --format doc

とやってみてください。

こんな感じになりましたか?公式をグーグル翻訳すると、緑の喜びを浴びるって書いてあります。私的にはこれが結構楽しいと感じています。なんかすごい出来る人になった感じしませんか。私だけか(笑)

③実際にコードを書いてみる

私はこの書き方しか知らないのでこの書き方で書きます!

こういうイメージ
(実際).to RSpecで求めている値

まず、さっきのtest_spec.rbは削除して、specフォルダの中にfactoriesフォルダとmodelsフォルダとsystemsフォルダを作ります。

modelフォルダの中に、バリデーションを付けてるテーブルなどのファイルを作りましょう。
イメージだとこんな感じ

specフォルダの中身
spec
L factories
 L user.rb
 L workbook.rb

L model
 L user.rb
 L workbook.rb

L system
 L user_spec.rb
 L workbook_spec.rb

factoriesはfactory botというgem機能(?)を使って、テスト用のデータを作ってくれるものです。さっきgemでインストールしたやつですね。なくても出来る。

今回はfactory/user.rbに記述してテストデータをつくる記述を書きます。

spec/factory/user.rb
FactoryBot.define do
  factory :user do
    sequence(:email) { |n| "test#{n}@email.com" }
    name {"nyanko"}
    password { '123456' }
  end
end

sequenceは重複をしてはいけない時に、こういう書き方をすると、被らないデータを作ってくれるよ~というfactory botの良い機能です(多分)

次に、model/user.rbに下記みたいに記述します

spec/model/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  let(:user) { FactoryBot.create(:user) }

  describe 'ユーザのバリデーション' do
    it '不備無しで保存されるか' do
      expect(user).to be_valid
    end

    context '名前' do
      it '空白だと保存されないか' do
        user.name = ""
        expect(user).to be_invalid
      end
    end

    context 'メール' do
      it '空白だと保存されないか' do
        user.email = ""
        expect(user).to be_invalid
      end

      it '重複すると保存されないか' do
        other_user = FactoryBot.build(:user, email: user.email)
        expect(other_user).to be_invalid
      end
    end

    context 'パスワード' do
      it '空白だと保存されないか' do
        user.password = ""
        expect(user).to be_invalid
      end
    end
  end
end

2行目で「Userのmodelについて書いていくよ~!」と宣言してます。3行目でspec/factory/user.rbで書いた記述をもとにテストデータを作ってます。「expect(user).to be_invalid」が「抽出した(user)は 無効 である」という感じで書いてます。「expect(user).not_to be_valid」と書くと、「抽出した(user)は 有効 ではない」みたいな感じになりますね。


次にsystemの記述も書きたいけどまた次回続き書きます!(多分)