1
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?

More than 1 year has passed since last update.

RSpecのsubject let beforeについて(小学生にもわかるように解説)

Last updated at Posted at 2021-07-29

subjectとは

RSpecでテストを書くときに使う便利なもので、テストをするときに、テストしたいもの
subjectとして指定するものです。

ex)
「猫が好き」という文をテストしたいとします。その場合、subjectはその文の「猫が好き」という部分です。
テストを書くとき、subjectを使うことで、そのテストがどの対象に対してテストするのかがわかりやすくなります。

また、subjectを使うと、同じテストで何度も同じ対象を指定する必要がなくなり、
subjectを使うとテストの中で同じ対象を何度も使い回すことができます。

subjectの使用例

Request spec(APIの使用例)

subjectは主題という意味で、APIを叩く部分を切り出すこと何を確認するためのテストなのかがわかります。

RSpec.describe "Users", type: :request do ... endでは、
Usersに関するrequest specを記述しています。
これは、HTTPリクエストとそのレスポンスをテストするためのものです。
:requestこのスペックがリクエストスペックであることを示しています

requests/user_spec.rb
require 'rails_helper'
RSpec.describe "Users", type: :request do
  describe "GET /users" do
    subject { get(users_path) }
    it "ユーザーの一覧が取得できる" do
      subject
    end
  end
  # 中略
end
  • subject { get(users_path) }
    テスト対象となるコードが記述されていて、get(users_path)がsubjectになります。
    これは、users_pathに対してGETリクエストを送信することを意味します。

  • it "ユーザーの一覧が取得できる" do ... end
    ここで実際のテストが行われ、itブロックは、1つのテストケースを定義します。
    このテストケースは、「ユーザーの一覧が取得できる」という仮定をテストしています。

  • subject
    ここでsubjectが呼ばれ、GET /usersエンドポイントに対するリクエストが実行されます。

このテストコード全体の流れは、GET /usersエンドポイントが正常に動作するかどうかをテストしてます。subjectを使い、テスト対象のコードを明確に定義し、itブロック内でテストを実行することができます。

他にも使い方を見ていきます。

  • 簡単なsubject
RSpec.describe Calculator do
  subject { Calculator.new }

  it "足し算" do
    result = subject.add(6,4)
    expect(result).to eq(10)
  end

  it "引き算" do
    result = subject.subtract(10, 6)
    expect(result).to eq(4)
  end
end

Calculatorクラスのインスタンスをsubjectとして定義し、それを使ってaddメソッドやsubtractメソッドをテストしてます。

  • 少し複雑な使い方
RSpec.describe BankAccount do
  let(:user) { create(:user) }
  subject { described_class.new(user) }

  it "最初は残高0" do
    expect(subject.balance).to eq(0)
  end

  it "入金可能" do
    subject.deposit(100)
    expect(subject.balance).to eq(100p)
  end

  it "引き落とし可能" do
    subject.deposit(1000)
    subject.withdraw(500)
    expect(subject.balance).to eq(500)
  end
end

⚫︎補足
Rubyでbalanceは引き落としたい金額と手数料が残高となる処理

ここでのsubjectの使い道についてです。

subject { described_class.new(user) }は、
テストで使用する「口座」を作成する部分で、
described_class現在のテストが対象としているクラス、つまりBankAccountクラスです。

つまり、この行は次のように解釈できます。「このテストでは、BankAccountクラスの新しい口座を作成します。その口座の所有者は、let(:user) { create(:user) }で定義されたユーザーです。」

この口座が作成された後、それを使ってテストを行います。たとえば、「初期残高が0であることを確認する」テストでは、この作成された口座の残高を検査して、それが0であることを確認します。

subjectは何回も使う必要ない?

以下の資料でsubjectを何回も使う必要ないという記事がありました。どういうことでしょうか。🤔

こちらの著者の主張は、subjectは効果的な時しか使わないという記事です。

subjectが向いているケース(subjectの使いどころ)

def greet
  'Hello!'
end

describe '#greet' do
  subject { greet }
  it { is_expected.to eq 'Hello' }
end

greetメソッドをテストしていて、このメソッドはHello!という文字列を返すメソッドです。
このように戻り値を返すメソッドをsubjectにするといいみたいです。違和感がない感じはします。

subjectが向いていないケース(ソッドの副作用をテストするケース)

class Counter
  attr_reader :count

  def initialize
    @count = 0
  end

  def increment
    @count += 1
  end
end

describe 'Counter#increment' do
  let(:counter) { Counter.new }
  subject { counter.increment }
  it do
    subject
    expect(counter.count).to eq 1
  end
end

これは、Counterクラスのincrementメソッドをテストしていて、
incrementメソッドは、Counterクラスのインスタンス内で保持しているカウント値を増やす副作用を持つメソッドです。
しかし、このテストではsubjectにcounter.incrementを設定しています。
これにより、テストはincrementメソッドの戻り値ではなく、
counter.countの値を検証しています。

before

テストをする前に事前に準備をするための場所です。
テストを始める前に、必要な準備をする場所とも言えそうですね。

小学生にわかりそうな説明をします。
ex)
ケーキを焼くテストをします。ケーキを焼く前にbeforeを使うと、ケーキを焼く前に事前準備ができます。例えば、材料を用意する、道具を揃える、オーブンで温めるなどケーキを焼く前にやることをbeforeの中に書きます。

spec.rb
RSpec.describe "お菓子を焼くテスト" do
  before do
    @ingredients = ["小麦粉", "砂糖", "卵", "バター"]
    @oven_temperature = 180
    @tools = ["ボウル", "泡立て器", "オーブン"]
  end

  it "お菓子が焼けること" do
    # お菓子を焼くテストの内容を書く
    # ここではお菓子が焼けるかどうかをチェックする
    expect(お菓子が焼けるかどうか).to be(true)
  end

  it "お菓子がおいしく焼けること" do
    # お菓子を焼いておいしく焼けるかどうかをチェックするテストの内容を書く
    # ここではお菓子がおいしく焼けるかどうかをチェックする
    expect(お菓子がおいしく焼けるかどうか).to be(true)
  end
end

実際のコードで見てみます。
この例では、beforeブロックの中でお菓子を焼く前の準備をしています。その後、それぞれのテストでその準備を使ってお菓子を焼いたり、おいしく焼けたかどうかを確認したりします。

つまり、beforeを使うと、テストをする前に必要な準備を書いておけば、それを使って簡単にテストができるということです。

requests/user_spec.rb
RSpec.describe "Users", type: :request do
  describe "GET /users" do
    subject { get(users_path) }

    it "ユーザーの一覧が取得できる" do
     3.times { create(:user) }
      subject

      binding.pry
    end
  end
# 中略
end


# before句で訂正

RSpec.describe "Users", type: :request do
  describe "GET /users" do
    subject { get(users_path) }
    before {  3.times { create(:user) } }

    it "ユーザーの一覧が取得できる" do
      subject

      binding.pry
    end
  end
# 中略
end

let

letとはイメージとして変数に値を入れているだけの簡単なもの
⚫︎使い方

let(:user) { create(:user) }
requests/user_spec.rb
require 'rails_helper'

RSpec.describe "Users", type: :request do
  # 中略

  describe "GET /users/:id" do
    subject { get(user_path(user_id)) }

    context "指定した id のユーザーが存在する場合" do
      let(:user) { create(:user) }
      let(:user_id) { user.id }

      it "そのユーザーのレコードが取得できる" do
        # 中略
      end
    end

    #省略
      end
    end
  end

RSpecをもっと学ぶなら

資料

ChatGPT

1
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
1
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?