###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.まだ苦手で全然書けないので参考にしないで欲しいけど書きます(恐怖)
プログラムは作っていないけど体感してみたいという人は②まで、実際になんかプログラムを作っているよ!という人は③以降も出来るはず・・・!
出来なくても教えられるほどの力量は私にはありません・・・(´;ω;`)
###①テストの準備
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に必要なヘルパーとかを作ってくれます。
RSpec.configure do |config| #の中に
config.include Devise::Test::IntegrationHelpers, type: :system #これを追加
end
↑これはdeviseを使っている人が記述するとRSpec内でsign_in @user
のような記述を使う事が出来るようになります。
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
というファイルを作って、
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
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
に記述してテストデータをつくる記述を書きます。
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に下記みたいに記述します
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の記述も書きたいけどまた次回続き書きます!(多分)