How to call subject several times with RSpec

Viewed 6233

I create RSpec test below. I try to call subject several times. But I cannot get expected result. I call subject three times, doesn't it? So, I expect three Book records. Does subject cannot call one time?

require 'rails_helper'

RSpec.describe Book, type: :model do
  context 'some context' do
    subject { Book.create }

    before do
      subject
    end

    it 'something happen' do
      subject
      expect { subject }.to change{ Book.count }.from(0).to(3)
    end
  end
end
2 Answers

No. let and subject are memoized (and lazy-loaded).

You can change it like this

subject { 3.times { Book.create } }

it 'something happen' do
  expect { subject }.to change{ Book.count }.from(0).to(3)
end

Or if you (for whatever reason) want to call something 3 times - define a method:

subject { create_book }

def create_book
   Book.create
end

before do
  create_book
end

it 'something happen' do
  create_book
  expect { subject }.to change{ Book.count }.from(2).to(3)
end

Then it will be called 3 times: once in before block, one in it before expectation and once inside the expectation (but the change will be from 2 not from 0, because those 2 times were called before)

If you don't want to memoize a result you can also use a Proc

describe MyClass do
  let(:param_1) { 'some memoized string' }
  let(:described_method) { Proc.new { described_class.do_something(param_1) } }

  it 'can run the method twice without memoization' do
    expect(described_method.call).to have_key(:success) # => {success: true}
    expect(described_method.call).to have_key(:error)   # => {error: 'cant call do_something with same param twice'}
  end
end
Related