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

# 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***
```rb
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メソッドをテストしてます。

- ***少し複雑な使い方***

```rb
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を何回も使う必要ないという記事がありました。どういうことでしょうか。🤔

https://blog.jnito.com/entry/2021/10/09/105651

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

①***subjectが向いているケース(subjectの使いどころ)***
```rb
def greet
  'Hello!'
end

describe '#greet' do
  subject { greet }
  it { is_expected.to eq 'Hello' }
end
```
greetメソッドをテストしていて、このメソッドはHello!という文字列を返すメソッドです。
このように戻り値を返すメソッドをsubjectにするといいみたいです。違和感がない感じはします。

②***subjectが向いていないケース(ソッドの副作用をテストするケース)***
```rb
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とはイメージとして変数に値を入れているだけの簡単なもの
⚫︎使い方
```rb
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をもっと学ぶなら

https://blog.jnito.com/entry/2023/05/12/070002#RSpec%E3%82%92%E3%82%82%E3%81%A3%E3%81%A8%E4%BD%BF%E3%81%84%E3%81%93%E3%81%AA%E3%81%97%E3%81%9F%E3%81%84

https://leanpub.com/everydayrailsrspec-jp?utm_campaign=everydayrailsrspec-jp&utm_medium=embed&utm_source=blog.jnito.com

# 資料

ChatGPT