LoginSignup
0
0

RSpecのletを使った遅延評価❗️(letとlet!の違い)

Last updated at Posted at 2021-09-07

概要

RSpecではletlet!を使いますが、この2つはどのように使い分けるのでしょうか。
そこにはRSpecの遅延評価のことを知っておく必要があります。

letとlet!の違い

  • let
    最初にメソッドを呼び出された時に使います***。使い方はbeforeと同じです。

  • let!
    itなどが実行される前に評価されます。beforeと実行タイミングが同じです。

ChatGPTに小学生にもわかるように説明してと聞いてみました。(子どもみたいな解説ご了承ください)

⚫︎let
おもちゃ箱におもちゃをしまっておくようなもので、おもちゃ箱(=変数)おもちゃ(=値) を入れておいて、必要なときに箱を開けて取り出します。
箱を開けるまでおもちゃは見えず、初めて箱を開けるときにおもちゃが出てくるようなものです。ここで言うと,let(:variable) { some_value } と書くと、
おもちゃ箱にvariableというおもちゃが入れられます。
でも、箱の中身は最初は見ずに、箱を開ける(=変数を使う)ときに初めておもちゃがでてきます。

RSpec.describe 'let' do
  let(:variable) { some_value }

  it 'opens the toy box and finds a toy' do
    # 変数を使って箱を開ける(初めて箱を開けるとおもちゃが見える)
    expect(variable).to eq(some_value)
  end
end

⚫︎let!
おもちゃ箱に最初からおもちゃが見えるようにしておくようなものです。
おもちゃ箱(=変数)におもちゃ(=値)を入れておいて、箱を開けなくても最初からおもちゃが見えます。
おもちゃが最初から見えているので、箱を開けるときに毎回同じおもちゃが見えます。

RSpec.describe 'let!' do
  let!(:variable) { some_value }

  it 'sees the toy without opening the box' do
    # 箱を開けなくてもおもちゃが見える(最初から見えている)
    expect(variable).to eq(some_value)
  end
end

遅延評価とは?

以下のようにletを使うと遅延評価されます。
{ create(:post,user: user) }はテストが実行されるまで実際には評価されません。

let(:post) { create(:post,user: user) }

let!にするとテストが始まる前に{ create(:post,user: user) }が実行され、
その結果がlet!の後の:postにセットされます。
let!にしたこの時点でcreateしてくれるのでレコードが作成してくれます。

let!(:post) { create(:post,user: user) }

なぜ遅延評価を使うのか

ChatGPTに聞いてみたら遅延評価を使うことで、テストの実行効率が向上し、不要な計算を避けることができます。特に、コストのかかる計算や初期化処理が必要な場合には、遅延評価が性能を向上させる助けになるみたいです。

letとlet!の使い分け

ここからは大人の説明をしていきます。

let(:user) { create(:user)}

の方は遅延評価でuserが参照された時に評価つまり、ユーザーがデータベースで作られて、
処理が実行された時に実行
されます。

let!(:user) { create(:user) }

let!が使われる時は最初からitの中(example)が評価されます。

何のこっちゃと思うので使用例を見ていきます。

⚫︎letの使用例
遅延評価を使う場合についてです。

ex)
あるテストケース内で変数を複数回使用するが、その値が変更されない時に便利です。

RSpec.describe 'letを使う' do
  let(:user) { create(:user) }

  it 'ユーザーネームがある' do
    expect(user.username).to eq('大谷')
  end

  it 'メールアドレスがある' do
    expect(user.email).to eq('ohtani@example.com')
  end
end

user変数が各テストケース内で使われていますが、letは遅延評価されるため、
テストケースごとにcreate(:user)が実行されます。

⚫︎let!の使用例
すぐに変数のセットアップが必要な時に使います。
ex)
あるテストケース内で変数の値が変更される可能性があり、その都度初期化が必要な時に便利です。

RSpec.describe 'let!を使う' do
  let!(:user) { create(:user) }

  it 'ユーザーネームを変える' do
    user.update(username: '大谷')
    expect(user.username).to eq('大谷')
  end

  it 'メールアドレスを変える' do
    user.update(email: 'ohtani@example.com')
    expect(user.email).to eq('ohtani@example.com')
  end
end

他にも使用例を出していきます。
⚫︎let

この例では、letを使用して初期のユーザー initial_user と新しいユーザー new_user を作成しています。letは遅延評価され、各テストケース内で同じインスタンスが再利用されます。

最初のテストでは、initial_userのユーザーネームを更新しています。
次のテストでは、letで作成した初期のユーザー initial_user のユーザーネームが初期状態のままであることを確認しています。
最後に、letを使用して新しいユーザー new_user を作成し、そのユーザーネームを確認しています。
letを使用することで、テストコード内で再利用可能な変数を定義し、コードの再利用性と可読性を向上させることができます。

# ユーザーモデル
class User
  attr_accessor :username

  def initialize(username)
    @username = username
  end
end

# RSpecテスト
RSpec.describe 'Using let' do
  # letを使って初期のユーザーを作成
  let(:initial_user) { User.new('initial_user') }

  it 'updates the username' do
    # 最初のユーザーのユーザーネームを変更
    initial_user.username = 'updated_user'
    
    # 初期のユーザーと更新後のユーザーを比較
    expect(initial_user.username).to eq('updated_user')
  end

  it 'has the correct initial username' do
    # letで作成した初期のユーザーのユーザーネームを確認
    expect(initial_user.username).to eq('initial_user')
  end

  # letを使って新しいユーザーを作成
  let(:new_user) { User.new('new_user') }

  it 'has the correct username for the new user' do
    # 新しいユーザーのユーザーネームを確認
    expect(new_user.username).to eq('new_user')
  end
end

⚫︎let!
この例では、let!を使用して初期のユーザー initial_user と新しいユーザー new_user を作成しています。let!は即座に初期化され、それぞれのテストケースで異なる状態を持つことが期待されます。

最初のテストでは、initial_userのユーザーネームを更新しています。
次のテストでは、let!で作成した初期のユーザー initial_user のユーザーネームが初期状態のままであることを確認しています。
最後に、let!を使用して新しいユーザー new_user を作成し、そのユーザーネームを確認しています。
これにより、let!が即座に実行され、各テストケースで異なる状態を持つことが示されています。

# ユーザーモデル
class User
  attr_accessor :username

  def initialize(username)
    @username = username
  end
end

# RSpecテスト
RSpec.describe 'Using let!' do
  # let!を使って初期のユーザーを作成
  let!(:initial_user) { User.new('initial_user') }

  it 'updates the username' do
    # 最初のユーザーのユーザーネームを変更
    initial_user.username = 'updated_user'
    
    # 初期のユーザーと更新後のユーザーを比較
    expect(initial_user.username).to eq('updated_user')
  end

  it 'has the correct initial username' do
    # let!で作成した初期のユーザーのユーザーネームを確認
    expect(initial_user.username).to eq('initial_user')
  end

  # let!を使って新しいユーザーを作成
  let!(:new_user) { User.new('new_user') }

  it 'has the correct username for the new user' do
    # 新しいユーザーのユーザーネームを確認
    expect(new_user.username).to eq('new_user')
  end
end

参考資料

ChatGPT参照

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