0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

subject

Last updated at Posted at 2024-12-28

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マッチャーによって確認されているらしい

subjectbeforeフックの前に利用可能である

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

同一のインスタンスでないと失敗する

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?