How can I check memoization with RSpec?

I have the following code in one of my classes:

class Settings < ActiveRecord::Base
  def self.current
    @settings ||= Settings.where({ environment: Rails.env }).first_or_create!
  end
  # Other methods
end

The main behavior:

  • It creates a new record on the first call.
  • It returns the same result with subsequent calls.
  • Resets ivar after each update, returning the first (and unique) record associated with the current environment in subsequent calls.

For this method, I have the following test:

describe Settings do
  describe ".current" do
    it "gets all settings for current environment" do
      expect(Settings.current).to eq(Settings.where({ environment: 'test' }).first)
    end
  end
end

I don't like this because I don't really test memoization, so I stick to the approach of this question and I tried something like this:

describe ".current" do
  it "gets all settings for current environment" do
    expect(Settings).to receive(:where).with({ environment: 'test' }).once
    2.times { Settings.current }
  end
end

But this test returns the following error:

NoMethodError:
  undefined method `first_or_create!' for nil:NilClass

So my question is: how can I test memoization for this method with RSpec?

UPDATE:

Finally, my approach is as follows:

describe Settings do
  describe ".current" do
    it "gets all settings for current environment" do
      expect(described_class.current).to eq(described_class.where(environment: 'test').first)
    end
    it "memoizes the settings for current environment in subsequent calls" do
      expect(described_class).to receive(:where).once.with(environment: 'test').and_call_original
      2.times { described_class.current }
    end
  end
end
+4
2

.and_call_original :

expect(Settings).to receive(:where).with({ environment: 'test' }).once.and_call_original
+7

Settings.current , , , . , .

describe ".current" do
  it "gets all settings for current environment" do
    relation = double("relation")
    expect(relation).to receive(:first_or_create!).once
    expect(Settings).to receive(:where).with({ environment: 'test' }).once.and_return(relation)
    settings = Settings.current
    expect(Settings.current).to eq settings
  end
end
0

All Articles