まえおき
改めてRSpecについてRelishを流し読みして学びのあったところをチェックしてみました。
使いやすそうだなと思った一部のみ抜粋しているため、網羅しているわけではないです。
また、量が多いのでとりあえずRspec Coreだけ見ました。
https://relishapp.com/rspec/rspec-core/v/3-5/docs
$ rspec -v
3.5.4
shared_examples
テスト項目を共有して再利用できます。
shared_examples "共有したいテスト項目" do
it { expect(1).to eq(1) }
end
describe "テスト" do
it_behaves_like "共有したいテスト項目"
end
$ bundle exec rspec spec/sample/shared_examples_spec.rb --format doc
テスト
behaves like 共有したいテスト項目
should eq 1
shared_examplesは引数をとることができます。
class Food; end
class Sushi < Food; end
class Drink; end
class Tea < Drink; end
shared_examples "親クラス確認" do |child, parent|
it "#{child}の親クラスは#{parent}である" do
expect(child.superclass).to eq(parent)
end
end
describe "それぞれの親クラスを確認" do
it_behaves_like "親クラス確認", Sushi, Food
it_behaves_like "親クラス確認", Tea, Drink
end
$ bundle exec rspec spec/sample/shared_examples_spec.rb --format doc
それぞれの親クラスを確認
Sushiの親クラスはFoodである
Teaの親クラスはDrinkである
Finished in 0.00301 seconds (files took 0.2289 seconds to load)
2 examples, 0 failures
shared_context
条件を共有することができます。
shared_context "共有する条件" do
let(:shared_let) { "hello" }
before do
shared_let.concat("world")
end
end
describe "shared_contextのテスト" do
include_context "共有する条件"
it { expect(shared_let).to eq("helloworld") }
end
$ bundle exec rspec spec/sample/shared_context_spec.rb --format doc
shared_contextのテスト
should eq "helloworld"
configに宣言を定義しておくと、include_context
を使わずに共有できます。
build_sushi
という定義をしてみます。
class Sushi
attr_accessor :name
def initialize(name=nil)
@name = name
end
end
shared_context "寿司を握る" do
let(:sushi) { Sushi.new("トロ") }
end
RSpec.configure do |rspec|
rspec.include_context "寿司を握る", build_sushi: true
end
require "spec_helper_for_shared_context.rb"
describe "宣言で共有する", build_sushi: true do
it { expect(sushi.name).to eq("トロ") }
end
describe "個別に共有する" do
let(:sushi) { Sushi.new("あなご") }
it { expect(sushi.name).to eq("あなご") }
it "トロ定義済み", build_sushi: true do
expect(sushi.name).to eq("トロ")
end
end
Command line
RSpec実行時にオプションを指定することで特定のテストを実行、除外などが可能です。
--example, --tag
describe "Command Line テスト ひとつめ" do
it "ログインに関するテスト その1" do; end
it "ログインに関するテスト その2", required: false do; end
it "ログインに関するテスト その3", required: true, type: 'important' do; end
it "ログアウトに関するテスト" do; end
end
describe "Command Line テスト ふたつめ" do
it "レコード作成に関するテスト その1", type: 'heavy' do; end
it "レコード作成に関するテスト その2" do; end
it "レコード作成に関するテスト その3", required: true do; end
it "レコード削除に関するテスト", type: 'heavy' do; end
end
describe "Command Line テスト みっつめ" do
it "関係のない名前" do; end
end
--example 'テスト'
を指定すると説明にテスト
を含むものを実行する。
itの命名に使われていなくても、describeに含めばそれも実行される。
$ bundle exec rspec spec/sample/command_line --example 'テスト' --format doc
Run options: include {:full_description=>/テスト/}
Command Line テスト ひとつめ
ログインに関するテスト その1
ログインに関するテスト その2
ログインに関するテスト その3
ログアウトに関するテスト
Command Line テスト ふたつめ
レコード作成に関するテスト その1
レコード作成に関するテスト その2
レコード作成に関するテスト その3
レコード削除に関するテスト
Command Line テスト みっつめ
関係のない名前
Finished in 0.0036 seconds (files took 0.35518 seconds to load)
9 examples, 0 failures
--example 'その2'
を指定すると説明にその2
を含むものしか実行しない。
$ bundle exec rspec spec/sample/command_line --example 'その2' --format doc
Run options: include {:full_description=>/その2/}
Command Line テスト ひとつめ
ログインに関するテスト その2
Command Line テスト ふたつめ
レコード作成に関するテスト その2
Finished in 0.00192 seconds (files took 0.19095 seconds to load)
2 examples, 0 failures
--tag required
を指定すると required: true
がついているものしか実行しない。
$ bundle exec rspec spec/sample/command_line --tag required --format doc
Run options: include {:required=>true}
Command Line テスト ひとつめ
ログインに関するテスト その3
Command Line テスト ふたつめ
レコード作成に関するテスト その3
Finished in 0.00132 seconds (files took 0.28193 seconds to load)
2 examples, 0 failures
--tag ~type:heavy
を指定すると、type:heavy
ではないものを実行する。
$ bundle exec rspec spec/sample/command_line --tag ~type:heavy --format doc
Run options: exclude {:type=>"heavy"}
Command Line テスト ひとつめ
ログインに関するテスト その1
ログインに関するテスト その2
ログインに関するテスト その3
ログアウトに関するテスト
Command Line テスト ふたつめ
レコード作成に関するテスト その2
レコード作成に関するテスト その3
Command Line テスト みっつめ
関係のない名前
Finished in 0.00321 seconds (files took 0.20338 seconds to load)
7 examples, 0 failures
行数指定
describe "行数指定の確認" do
it "2行目" do; end
it "4行目" do
expect(1).to eq(1)
end
it "8行目" do; end
end
ファイル名のあとに:行番号
で特定のテストを実行できます。
$ bundle exec rspec spec/sample/command_line/line_number_spec.rb:2 --format doc
Run options: include {:locations=>{"./spec/sample/command_line/line_number_spec.rb"=>[2]}}
行数指定の確認
2行目
Finished in 0.00093 seconds (files took 0.30578 seconds to load)
1 example, 0 failures
--only-failures
設定をすると失敗したテストのみ再実行するようにできます。
RSpec.configure do |config|
config.example_status_persistence_file_path = "spec/examples.txt"
end
require "spec_helper_for_failure"
describe "only failures 確認" do
it "成功1" do
expect("文字列".class).to eq(String)
end
it "失敗1" do
expect(1.class).to eq(String)
end
it "成功2" do
expect(1.class).to eq(Fixnum)
end
it "失敗2" do
expect([1].class).to eq(Fixnum)
end
end
これで $ bundle exec rspec spec/sample/command_line/failure_spec.rb
を実行すると、2つテストが失敗し、txtファイルが生成される。
example_id | status | run_time |
----------------------------------------------- | ------ | --------------- |
./spec/sample/command_line/failure_spec.rb[1:1] | passed | 0.00211 seconds |
./spec/sample/command_line/failure_spec.rb[1:2] | failed | 0.09153 seconds |
./spec/sample/command_line/failure_spec.rb[1:3] | passed | 0.00024 seconds |
./spec/sample/command_line/failure_spec.rb[1:4] | failed | 0.00239 seconds |
その後、 --only-failure
オプションをつけて実行すると、失敗したテストのみ再実行できる。
$ bundle exec rspec spec/sample/command_line/failure_spec.rb --only-failure 2.3.1
Run options: include {:last_run_status=>"failed"}
FF
Failures:
1) only failures 確認 失敗1
Failure/Error: expect(1.class).to eq(String)
expected: String
got: Fixnum
(compared using ==)
Diff:
@@ -1,2 +1,2 @@
-String
+Fixnum
# ./spec/sample/command_line/failure_spec.rb:9:in `block (2 levels) in <top (required)>'
2) only failures 確認 失敗2
Failure/Error: expect([1].class).to eq(Fixnum)
expected: Fixnum
got: Array
(compared using ==)
Diff:
@@ -1,2 +1,2 @@
-Fixnum
+Array
# ./spec/sample/command_line/failure_spec.rb:17:in `block (2 levels) in <top (required)>'
Finished in 0.10357 seconds (files took 0.64171 seconds to load)
2 examples, 2 failures
Failed examples:
rspec ./spec/sample/command_line/failure_spec.rb:8 # only failures 確認 失敗1
rspec ./spec/sample/command_line/failure_spec.rb:16 # only failures 確認 失敗2
また、 --next-failure
オプションを活用することで少しずつ修正を進めていける様子。(まだ実際にこれはあまり活用したことがない...。)
詳細はRelishを参照してください!
https://relishapp.com/rspec/rspec-core/v/3-5/docs/command-line/only-failures
around hooks
around
でテスト実行前後に処理することができます。
https://relishapp.com/rspec/rspec-core/v/3-5/docs/hooks/around-hooks
describe "around hook" do
around(:example) do |example|
puts "実行前"
example.run
puts "実行後"
end
it { puts "テスト実行" }
end
$ bundle exec rspec spec/sample/around_hooks.rb
実行前
テスト実行
実行後
.
Finished in 0.00125 seconds (files took 0.18901 seconds to load)
1 example, 0 failures
Metadata
Current example
it
の説明の内容を検証に含むことができる。
shared_example
とかと組み合わせたらもっと有用な感じに書けそう?
https://relishapp.com/rspec/rspec-core/v/3-5/docs/metadata/current-example
class Sushi
attr_accessor :name
def initialize(name)
@name = name
end
end
describe "description取得の確認" do
let(:sushi) {|example| Sushi.new(example.description) }
it "甘エビ" do
expect(sushi.name).to eq("甘エビ")
end
it "いくら" do
expect(sushi.name).to eq("いくら")
end
end
described_class
describe
にセットされている値を扱うことができる。
これが存在を知ってから一番活用しているやつです。便利。
https://relishapp.com/rspec/rspec-core/v/3-5/docs/metadata/described-class
class Food; end
class Sushi < Food; end
class Sweet < Food; end
shared_examples "Foodを継承している" do
it { expect(described_class.superclass).to eq(Food) }
end
describe Sushi do
it_behaves_like "Foodを継承している"
end
describe Sweet do
it_behaves_like "Foodを継承している"
end
Conditional Filters
各項目にif: false
, もしくはunless: true
をつけると実行時に無視されます。
https://relishapp.com/rspec/rspec-core/v/3-5/docs/filtering/conditional-filters
describe "if: true の describe", if: true do
it("if: true なので含まれる", if: true) {}
it("if: false なので除外", if: false) {}
it("unless: true なので除外", unless: true) {}
it("unless: false なので含まれる", unless: false) {}
end
describe "if: false の describe", if: false do
it("if: true なので含まれる", if: true) {}
it("if: false なので除外", if: false) {}
it("unless: true なので除外", unless: true) {}
it("unless: false なので含まれる", unless: false) {}
end
$ bundle exec rspec spec/sample/conditional_filters_spec.rb --format doc
if: true の describe
if: true なので含まれる
unless: false なので含まれる
if: false の describe
if: true なので含まれる
Finished in 0.00162 seconds (files took 0.23367 seconds to load)
3 examples, 0 failures
ちなみに --tag
でわざと指定しても filterd out と言われてしまいました。
$ bundle exec rspec spec/sample/conditional_filters_spec.rb --tag if:false --format doc
Run options: include {:if=>false}
All examples were filtered out
Finished in 0.00079 seconds (files took 0.47536 seconds to load)
0 examples, 0 failures
Pending and Skipped
pending
(未決定、保留中)とskip
(とばす)を設定しておくことができます。
pending
は現時点で失敗してしまうテスト項目に付与するものなので、成功するテストがpendingしていると失敗扱いとなります。
一方skip
は単純にskipしたいテスト項目に対して付与します。
pending
、skip
ともに理由をつけておくと実行時に表示してくれます。
英語力が足りなかったので下記の記事を参考にしました!
Rspec の pending と skip って何が違うの? - Qiita
# pending specs
describe "Pendingなテスト" do
it "pendingです" do
pending "まだです"
fail
end
it "エラーが無いけどpending" do
pending "エラーが無いときはpendingはFailed"
expect(1).to eq(1) # エラーが無いpendingは不適とみなされてFAILED
end
end
# skip specs
describe "テストをskipする" do
it("skipします その1") { skip("skipする理由") }
it("skipします その2", skip: "skipする理由") {}
xit("skipします その3") { } # 一時的なskipという意味があるので理由不要
end
$ bundle exec rspec spec/sample/pending_and_skipped_spec.rb --format doc
Pendingなテスト
pendingです (PENDING: まだです)
エラーが無いけどpending (FAILED - 1)
テストをskipする
skipします その1 (PENDING: skipする理由)
skipします その2 (PENDING: skipする理由)
skipします その3 (PENDING: Temporarily skipped with xit)
Pending: (Failures listed here are expected and do not affect your suite's status)
1) Pendingなテスト pendingです
# まだです
Failure/Error: fail
RuntimeError:
# ./spec/sample/pending_and_skipped_spec.rb:5:in `block (2 levels) in <top (required)>'
2) テストをskipする skipします その1
# skipする理由
# ./spec/sample/pending_and_skipped_spec.rb:15
3) テストをskipする skipします その2
# skipする理由
# ./spec/sample/pending_and_skipped_spec.rb:16
4) テストをskipする skipします その3
# Temporarily skipped with xit
# ./spec/sample/pending_and_skipped_spec.rb:17
Failures:
1) Pendingなテスト エラーが無いけどpending FIXED
Expected pending 'エラーが無いときはpendingはFailed' to fail. No Error was raised.
# ./spec/sample/pending_and_skipped_spec.rb:7
Finished in 0.00531 seconds (files took 0.27895 seconds to load)
5 examples, 1 failure, 4 pending
Failed examples:
rspec ./spec/sample/pending_and_skipped_spec.rb:7 # Pendingなテスト エラーが無いけどpending
テストリポジトリ
ごにょごにょしたファイル群を置いておきます。
https://github.com/betachelsea/sample_rspec