見出し画像

Ruby on Rails6習得編 Ruby on Rails 6 実践ガイド Chapter4 RSpec


こちらの書籍について学んだことです。

Chapter4 RSpec

自動テストの仕組みを導入する
テストフレームワークとしてRSpecを採用する

4.1 RSpecの基礎知識

じっくり読んで理解を深める

4.1.1 テストとは

テストは色んな意味で使われるが、
RSpecみたいに、専用のプログラムによってWebアプリケーションの動作を確認することを指すことにする

4.1.2 RSpecとは

railsの標準はminitestというテストフレームワークが標準で組み込まれている
どうやらMinitestにはない優れた特徴があるみたい

4.1.3 RSpecの初期設定

一度だけ下記をwebコンテナ上のbaukis2ディレクトリで実行する

bin/rails g rspec:install
----------------------------------------
bash-4.4$ bin/rails g rspec:install
Running via Spring preloader in process 74
     create  .rspec
     create  spec
     create  spec/spec_helper.rb
     create  spec/rails_helper.rb
bash-4.4$ 
----------------------------------------

specディレクトリが作られる
spec_helper.rbとrails_helper.rbというファイルが生成される

rails_helper.rbを開き、コメント行を削除し、シングルクォートをダブルに変換
この作業多いなぁ・・・。

spec/rails_helper.rb修正前
----------------------------------------
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
 ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
 puts e.to_s.strip
 exit 1
end
RSpec.configure do |config|
 # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
 config.fixture_path = "#{::Rails.root}/spec/fixtures"
 # If you're not using ActiveRecord, or you'd prefer not to run each of your
 # examples within a transaction, remove the following line or assign false
 # instead of true.
 config.use_transactional_fixtures = true
 # RSpec Rails can automatically mix in different behaviours to your tests
 # based on their file location, for example enabling you to call `get` and
 # `post` in specs under `spec/controllers`.
 #
 # You can disable this behaviour by removing the line below, and instead
 # explicitly tag your specs with their type, e.g.:
 #
 #     RSpec.describe UsersController, :type => :controller do
 #       # ...
 #     end
 #
 # The different available types are documented in the features, such as in
 # https://relishapp.com/rspec/rspec-rails/docs
 config.infer_spec_type_from_file_location!
 # Filter lines from Rails gems in backtraces.
 config.filter_rails_from_backtrace!
 # arbitrary gems may also be filtered via:
 # config.filter_gems_from_backtrace("gem name")
end
----------------------------------------
spec/rails_helper.rb修正後
----------------------------------------
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
begin
 ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
 puts e.to_s.strip
 exit 1
end
RSpec.configure do |config|
 config.fixture_path = "#{::Rails.root}/spec/fixtures"
 config.use_transactional_fixtures = true
 config.infer_spec_type_from_file_location!
 config.filter_rails_from_backtrace!
end
----------------------------------------

うーんわけわかめ

4.1.4 RSpec はじめの一歩

全然関係ないけどFinder使いにくい・・・
時間あったら下のやつで設定見直してみようかな
https://maccle.com/my-tips/9-tips-improvement-mac-finder/
https://ushigyu.net/2015/05/18/mac-finder-configuration/

specディレクトリの下にexperimentsというサブディレクトリを作り、string_spec.rbというファイルを作成する

そして次のようにする

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"D"
     expect(s.size).to eq(4)
   end
 end
end
----------------------------------------

rspecのテストコードは、通常specディレクトリの下に置く。
ファイル名は、_spec.rbで終わるようにする
これらのファイルをspecファイルと呼ぶ

ちょっとここで離脱して、Finderを使いやすくする

〜数十分後〜

戻ってきた!

どのサブディレクトリにどんなspecファイルを置くかは慣習的に決まってるらしい。
たとえばモデルクラスに関するspecファイルはspec/models
apiに関するspecファイルはspec/requests

とか
独自のサブディレクトリを用意しても大丈夫そう
本書独自のルールで、rubyやrailsの仕様に関する実験を行うspecファイルを置く場所としてexperimentsサブディレクトリを作成した。

s="ABC"
s<<"D"
expect(s.size).to eq(4)

文字列の長さが4かどうかをチェックしようとしてる

実行するには下記
rspec spec/experiments/string_spec.rb

あ、rubyのファイルなんだね〜

----------------------------------------
bash-4.4$ rspec spec/experiments/string_spec.rb
.
Finished in 0.00761 seconds (files took 0.19442 seconds to load)
1 example, 0 failures
bash-4.4$ 
----------------------------------------

rspec spec
rspec

だと全てのテスト実行してくれる

4.2 エグザンプル

4.2.1 エグザンプルとは

例えば、という意味で使われる訳ではなさそう

全然関係ないけど気になった記事があったのでこれあとで見よ〜
https://wayohoo.com/mac/apps/summary-of-handy-apps-for-mac.html

RSpecはビヘイビア駆動開発(Behaivor Driven Development)

Behaivorは動作という意味

エグザンプルはexampleと書く

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"D"
     expect(s.size).to eq(4)
   end
 end
end
----------------------------------------

あ、エグザンプルって書いてある
example
it
specify

これらは全て同じ意味・・・?
rspecはテストコードが仕様という考え

4.2.2 エグザンプルグループ

もっかい見てみよー

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"D"
     expect(s.size).to eq(4)
   end
 end
end
----------------------------------------

describeがあるね
これは記述するという意味

グループ化のために使う。
文字列に関するテストグループの中に、文字列連結に関するテストグループがあって、その中に実際に行われるテスト"文字の追加"があるという認識でよい。


ちなみにこれテストがミスってたらどうなるんや?

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"DE"
     expect(s.size).to eq(4)
   end
 end
end
----------------------------------------

やってみた

----------------------------------------
bash-4.4$ rspec
F
Failures:
 1) String#<< 文字の追加
    Failure/Error: expect(s.size).to eq(4)
    
      expected: 4
           got: 5
    
      (compared using ==)
    # ./spec/experiments/string_spec.rb:8:in `block (3 levels) in <top (required)>'
Finished in 0.02988 seconds (files took 0.15111 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/experiments/string_spec.rb:5 # String#<< 文字の追加
bash-4.4$ 
----------------------------------------

なるほど
expectedが4でgotが5だよーって言ってるな

テストでミスってた部分の場所も明示してくれている

更にもういっちょテストコード弄った

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
describe Stringauce do
 describe "#<<だったもの" do
   example "文字の追加だったもの" do
     s="ABC"
     s<<"DEFGHIJKL"
     expect(s.size).to eq(4)
   end
 end
end
----------------------------------------

もうめちゃくちゃにしてやった
こうするとどうなる?

----------------------------------------
bash-4.4$ rspec
An error occurred while loading ./spec/experiments/string_spec.rb.
Failure/Error:
 describe Stringauce do
   describe "#<<だったもの" do
     example "文字の追加だったもの" do
       s="ABC"
       s<<"DEFGHIJKL"
       expect(s.size).to eq(4)
     end
   end
 end
NameError:
 uninitialized constant Stringauce
# ./spec/experiments/string_spec.rb:3:in `<top (required)>'
No examples found.

Finished in 0.00004 seconds (files took 0.18686 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples
bash-4.4$ 
----------------------------------------

んー、Stringとしてたのはファイル名と一致しなければいけないか?
わからんけど。
そこだけ直して再度rspec実行

----------------------------------------
bash-4.4$ rspec
F
Failures:
 1) String#<<だったもの 文字の追加だったもの
    Failure/Error: expect(s.size).to eq(4)
    
      expected: 4
           got: 12
    
      (compared using ==)
    # ./spec/experiments/string_spec.rb:8:in `block (3 levels) in <top (required)>'
Finished in 0.01991 seconds (files took 0.15466 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/experiments/string_spec.rb:5 # String#<<だったもの 文字の追加だったもの
bash-4.4$ 
----------------------------------------

なるほどね
ちな#でコメントつけれるみたい

4.2.3 テスト結果の読み方

この章で意図的にエラー起こすようになってた笑

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
#コメントをつけてみた
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"D"
     expect(s.size).to eq(4)
   end
   
   example "nilの追加" do
     s="ABC"
     s<<nil
     expect(s.size).to eq(4)
   end
 end
end
----------------------------------------

これで実行してみた

----------------------------------------
bash-4.4$ rspec
.F
Failures:
 1) String#<< nilの追加
    Failure/Error: s<<nil
    
    TypeError:
      no implicit conversion of nil into String
    # ./spec/experiments/string_spec.rb:14:in `block (3 levels) in <top (required)>'
Finished in 0.00877 seconds (files took 0.15473 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/experiments/string_spec.rb:12 # String#<< nilの追加
bash-4.4$ 
----------------------------------------

教科書誤字ってるけどまあいいや
rspecの下にある.Fは成功失敗の意味!
実行順ランダムだからもしかしたらFの表示位置違うことがあるかもしれんらしい


4.2.4 pendingメソッド

pendingを使うと、すぐに直せないテストコードであることを明示することができる

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
#コメントをつけてみた
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"D"
     expect(s.size).to eq(4)
   end
   
   example "nilの追加" do
     pending("調査なう")
     s="ABC"
     s<<nil
     expect(s.size).to eq(4)
   end
 end
end
----------------------------------------

rspec実行!


----------------------------------------
bash-4.4$ rspec
.*
Pending: (Failures listed here are expected and do not affect your suite's status)
 1) String#<< nilの追加
    # 調査なう
    Failure/Error: s<<nil
    
    TypeError:
      no implicit conversion of nil into String
    # ./spec/experiments/string_spec.rb:15:in `block (3 levels) in <top (required)>'
Finished in 0.00897 seconds (files took 0.18069 seconds to load)
2 examples, 0 failures, 1 pending
bash-4.4$ 
----------------------------------------

なんか黄色い文字と青い文字が目立った
ログが色分けされてて見やすい。かも知れん。

4.2.5 xexampleメソッド

面倒ならexampleをxexampleに書き換え

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
#コメントをつけてみた
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"D"
     expect(s.size).to eq(4)
   end
   
   xexample "nilの追加" do
     s="ABC"
     s<<nil
     expect(s.size).to eq(4)
   end
 end
end
----------------------------------------

レッツゴー☆

----------------------------------------
bash-4.4$ rspec
.*
Pending: (Failures listed here are expected and do not affect your suite's status)
 1) String#<< nilの追加
    # Temporarily skipped with xexample
    # ./spec/experiments/string_spec.rb:12

Finished in 0.00934 seconds (files took 0.16162 seconds to load)
2 examples, 0 failures, 1 pending
bash-4.4$ 
----------------------------------------

ほー、これもpendingの扱いになるのか

4.3 expertメソッドとマッチャー

4.3.1 オブジェクトを対象にする場合

expect(T).to M
の意味

Tをターゲット
Mをマッチャー

マッチャーは、ターゲットに指定されたオブジェクトがある条件を満たすかどうかを調べるオブジェクト

eqは等しいかどうか

expect(T).not_to M
は、見ての通りマッチャーと合っていないことを確かめる

4.3.2 ブロックを対象にする場合

expect{...}.to M

の形
これは例外が発生する時とかに便利な書き方
というかこの書き方じゃないとできなさそうなノリ

具体的な書き方は下記の通り

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
#コメントをつけてみた
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"D"
     expect(s.size).to eq(4)
   end
   example "nilは追加できない" do
     s="ABC"
     expect{s<<nil}.to raise_error(TypeError)
   end
 end
end
----------------------------------------

実行してみるとgreenになった

4.4 エグザンプルの絞り込み

4.4.1 行番号による絞り込み

rspec spec/experiments/string_spec.rb:14
rspec spec/experiments/string_spec.rb:12
rspec spec/experiments/string_spec.rb:11
rspec spec/experiments/string_spec.rb:2
rspec spec/experiments/string_spec.rb:17
rspec spec/experiments/string_spec.rb:18
rspec spec/experiments/string_spec.rb:30

----------------------------------------
bash-4.4$ rspec spec/experiments/string_spec.rb:14
Run options: include {:locations=>{"./spec/experiments/string_spec.rb"=>[14]}}
.
Finished in 0.00184 seconds (files took 0.16734 seconds to load)
1 example, 0 failures
bash-4.4$ rspec spec/experiments/string_spec.rb:12
Run options: include {:locations=>{"./spec/experiments/string_spec.rb"=>[12]}}
.
Finished in 0.0027 seconds (files took 0.15349 seconds to load)
1 example, 0 failures
bash-4.4$ rspec spec/experiments/string_spec.rb:11
Run options: include {:locations=>{"./spec/experiments/string_spec.rb"=>[11]}}
.
Finished in 0.00127 seconds (files took 0.14957 seconds to load)
1 example, 0 failures
bash-4.4$ rspec spec/experiments/string_spec.rb:2
Run options: include {:locations=>{"./spec/experiments/string_spec.rb"=>[2]}}
All examples were filtered out

Finished in 0.00028 seconds (files took 0.16423 seconds to load)
0 examples, 0 failures
bash-4.4$ rspec spec/experiments/string_spec.rb:17
Run options: include {:locations=>{"./spec/experiments/string_spec.rb"=>[17]}}
.
Finished in 0.00175 seconds (files took 0.1681 seconds to load)
1 example, 0 failures
bash-4.4$ rspec spec/experiments/string_spec.rb:18
Run options: include {:locations=>{"./spec/experiments/string_spec.rb"=>[18]}}
.
Finished in 0.0028 seconds (files took 0.15248 seconds to load)
1 example, 0 failures
bash-4.4$ rspec spec/experiments/string_spec.rb:30
Run options: include {:locations=>{"./spec/experiments/string_spec.rb"=>[30]}}
.
Finished in 0.00263 seconds (files took 0.14998 seconds to load)
1 example, 0 failures
bash-4.4$ 
----------------------------------------


よくわからん
あんま使うことなさそー

4.4.2 タグによる絞り込み

下記に書き換えた

spec/experiments/string_spec.rb
----------------------------------------
require "spec_helper"
#コメントをつけてみた
describe String do
 describe "#<<" do
   example "文字の追加" do
     s="ABC"
     s<<"D"
     expect(s.size).to eq(4)
   end
   example "nilは追加できない",:exception do
     s="ABC"
     expect{s<<nil}.to raise_error(TypeError)
   end
 end
end
----------------------------------------

:exceptionをつけた
タグがついたっぽい
このタグがついたものだけをテストする場合

rspec spec --tag=exception

でいけるっぽい


教科書に載ってないけど、

rspec --tag=exception

でも行けた!

ここまででrspecの基本は終わり!

次は53ページかけてChapter5のビジュアルデザインについてやる!


この記事が気に入ったらサポートをしてみませんか?