subject
はトップレベルグループスコープで定義され、使われることができる
A subject can be defined and used in the top level group scope
RSpec.describe Array, "with some elements" do
subject { [1, 2, 3] }
it "has the prescribed elements" do
expect(subject).to eq([1, 2, 3])
end
end
Run options: include {:full_description=>/Array\ with\ some\ elements/}
1 example, 0 failures, 1 passed
外部グループで定義されたsubject
は内部グループが使用可能である
The subject defined in an outer group is available to inner groups
RSpec.describe Array do
subject { [1, 2, 3] }
describe "has some elements" do
it "which are the prescribed elements" do
expect(subject).to eq([1, 2, 3])
end
end
end
Run options: include {:full_description=>/Array/}
1 example, 0 failures, 1 passed
subject
はexample内でメモされるしかしexample間はできない
The subject is memoized within an example but not across examples
このシナリオはsubject
定義ブロックで実行される突然変異を示す。この振る舞いは、仕様理解をより難しくとして振る舞いとして一般的に推奨されていないさせる。これはどれくらいメモライゼーションを起こさせるかを説明するためのツールとして簡単に使える技術です。
Note: This scenario shows mutation being performed in a subject definition block. This behavior is generally discouraged as it makes it more difficult to understand the specs. This is technique is used simply as a tool to demonstrate how the memoization occurs.
RSpec.describe Array do
# This uses a context local variable. As you can see from the
# specs, it can mutate across examples. Use with caution.
element_list = [1, 2, 3]
subject { element_list.pop }
it "is memoized across calls (i.e. the block is invoked once)" do
expect {
3.times { subject }
}.to change{ element_list }.from([1, 2, 3]).to([1, 2])
expect(subject).to eq(3)
end
it "is not memoized across examples" do
expect{ subject }.to change{ element_list }.from([1, 2]).to([1])
expect(subject).to eq(2)
end
end
Run options: include {:full_description=>/Array/}
2 examples, 0 failures, 2 passed
changeマッチャーによって確認されているらしい
subject
はbefore
フックの前に利用可能である
The subject is available in before hooks
RSpec.describe Array, "with some elements" do
subject { [] }
before { subject.push(1, 2, 3) }
it "has the prescribed elements" do
expect(subject).to eq([1, 2, 3])
end
end
ヘルパーメソッドはsubject
定義ブロックから呼び出ことができる
Helper methods can be invoked from a subject definition block
RSpec.describe Array, "with some elements" do
def prepared_array
[1, 2, 3]
end
subject { prepared_array }
it "has the prescribed elements" do
expect(subject).to eq([1, 2, 3])
end
end
Run options: include {:full_description=>/Array\ with\ some\ elements/}
1 example, 0 failures, 1 passed
We recommend using the named helper method over subject in examples.
なのでヘルパーメソッドを使う方がいい
subject!
破壊メソッドを使ってexampleの前に定義ブロックを呼び出す
Use the subject! bang method to call the definition block before the example
RSpec.describe "eager loading with subject!" do
subject! { element_list.push(99) } # 破壊的メソッドを使うことで定義ブロックを呼び出してる
let(:element_list) { [1, 2, 3] }
it "calls the definition block before the example" do
element_list.push(5)
expect(element_list).to eq([1, 2, 3, 99, 5])
end
end
Run options: include {:full_description=>/eager\ loading\ with\ subject!/}
1 example, 0 failures, 1 passed
- 破壊的メソッドを使うことで定義ブロックより上でも呼び出せる
グループ全体でテストしないと変数が初期化される
subject(:name)
を使って、ヘルパーメソッドを定義する
Use subject(:name) to define a memoized helper method
グローバル変数はexample下で使われる間、この振る舞いは、強硬に現在の仕様で推奨されていない。ここでは単に複数回の呼び出しにわたるキャッシュされる値を表示するしかしexampleをまたいだキャッシュされない。
Note: While a global variable is used in the examples below, this behavior is strongly discouraged in actual specs. It is used here simply to demonstrate the value will be cached across multiple calls in the same example but not across examples.
Given a file named “namedsubjectspec.rb” with:
$count = 0
RSpec.describe "named subject" do
subject(:global_count) { $count += 1 }
it "is memoized across calls (i.e. the block is invoked once)" do
expect {
2.times { global_count }
}.not_to change{ global_count }.from(1) # 1から変わっているか?
end
it "is not cached across examples" do
expect(global_count).to eq(2)
end
it "is still available using the subject method" do
expect(subject).to eq(3)
end
it "works with the one-liner syntax" do
is_expected.to eq(4)
end
it "the subject and named helpers return the same object" do
expect(global_count).to be(subject)
end
it "is set to the block return value (i.e. the global $count)" do
expect(global_count).to be($count)
end
end
Run options: include {:full_description=>/named\ subject/}
6 examples, 0 failures, 6 passed
is_expected
subject!(:name)を使ってexampleの前に呼ばれるヘルパーメソッドを定義する
Use subject!(:name) to define a helper method called before the example
RSpec.describe "eager loading using a named subject!" do
subject!(:updated_list) { element_list.push(99) }
let(:element_list) { [1, 2, 3] }
it "calls the definition block before the example" do
element_list.push(5)
expect(element_list).to eq([1, 2, 3, 99, 5])
expect(updated_list).to be(element_list)
end
end
Run options: include {:full_description=>/eager\ loading\ using\ a\ named\ subject!/}
1 example, 0 failures, 1 passed
be
equalとの違いはあるのか?
RSpec.describe "eager loading using a named subject!" do
subject!(:updated_list) { element_list.push(99) }
let(:element_list) { [1, 2, 3] }
it "calls the definition block before the example" do
element_list.push(5)
expect(element_list).to eq([1, 2, 3, 99, 5])
expect(updated_list).to be([1, 2, 3, 99, 5])
end
end
同一のインスタンスでないと失敗する