shared exampleはクラスとモジュールの振る舞いを記述させる
Shared examples let you describe behaviour of classes or modules.
When declared, a shared group’s content is stored.
他のexample groupのコンテキストでのみ実行に必要な共有グループのいくつかのコンテキストを供給する
It is only realized in the context of another example group, which provides any context the shared group needs to run.
気になる単語
- Shared examples、shared group
- context、example group
このどれかを使ってshared_exampleを呼び出す
A shared group is included in another group using any of:
include_examples "name" # include the examples in the current context
it_behaves_like "name" # include the examples in a nested context
it_should_behave_like "name" # include the examples in a nested context
matching metadata # include the examples in the current context
shared_exampleを使ったテスト
RSpec.shared_examples "some example" do |parameter|
\# Same behavior is triggered also with either `def something; 'some value'; end`
\# or `define_method(:something) { 'some value' }`
let(:something) { parameter }
it "uses the given parameter" do
expect(something).to eq(parameter)
end
end
RSpec.describe SomeClass do
include_examples "some example", "parameter1"
include_examples "some example", "parameter2"
end
動かしてみた 変数の上書きされている エラー
RSpec.describe SomeClass do
include_examples "some example", "parameter1"
include_examples "some example", "parameter2"
end
expected: "parameter1"
got: "parameter2"
(compared using ==)
0) User uses the given parameter
Failure/Error: expect(something).to eq(parameter)
expected: "parameter1"
got: "parameter2"
(compared using ==)
最初のテストは失敗する
You’re actually doing this (notice that first example will fail):
RSpec.describe SomeClass do
\# Reordered code for better understanding of what is happening
let(:something) { "parameter1" }
let(:something) { "parameter2" }
it "uses the given parameter" do
\# This example will fail because last let "wins"
expect(something).to eq("parameter1")
end
it "uses the given parameter" do
expect(something).to eq("parameter2")
end
end
上のコードを動かしてみた結果
expected: "parameter1"
got: "parameter2"
(compared using ==)
0) User uses the given parameter
Failure/Error: expect(something).to eq("parameter1")
expected: "parameter1"
got: "parameter2"
(compared using ==)
変数が上書きされているからエラーが起こったと考える
エラーが起こる原因
shared groupを含んだファイルを前にロードするためには厳格な名前規則が必要だ
WARNING: Files containing shared groups must be loaded before the files that use them. While there are conventions to handle this, RSpec does not do anything special (like autoload). Doing so would require a strict naming convention for files that would break existing suites.
同じコンテキスト内で複数回呼ぶと最後に呼ばれたやつが勝つ
WARNING: When you include parameterized examples in the current context multiple times, you may override previous method definitions and last declaration wins. So if you have this kind of shared example (or shared context)
上のエラーを回避
同じcontextで同じ名前のメソッドを複数宣言すると警告される
To prevent this kind of subtle error a warning is emitted if you declare multiple methods with the same name in the same context.
上の警告を防ぐためにinclude_examples
をit_behaves_like
に置き換える
Should you get this warning the simplest solution is to replace include_examples with it_behaves_like, in this way method overriding is avoided because of the nested context created by it_behaves_like
どちらかを変えると両方通る
RSpec.describe User do
include_examples "some example", "parameter1" # 以下2行同じコンテキスト内だと考える
it_behaves_like "some example", "parameter2"
end
requireでファイルを要求する
The simplest approach is to require files with shared examples explicitly from the files that use them.
require ファイル名と書くと特定のパスから呼び出される
Keep in mind that RSpec adds the spec directory to the LOAD_PATH, so you can say require 'shared_examples_for_widgets' to require a file at #{PROJECT_ROOT}/spec/shared_examples_for_widgets.rb.
spec/support/にあるshared examplesを含むファイルを置き、...requireする
One convention is to put files containing shared examples in spec/support/ and require files in that directory from spec/spec_helper.rb:
Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
同じファイルに全てあるときはshared groupを宣言するだけで良い
When all of the groups that include the shared group reside in the same file, just declare the shared group in that file.
感想
shared examplesは関数で、shared groupはshared examplesで使われるcontextを言うのだろう。
-
requireを使ってみる
-
include_examples
をit_behaves_like
に置き換える -
Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
を使ってみる -
include_examples "name", it_behaves_like "name", it_should_behave_like "name", matching metadata の意味の使い分けを知りたい