Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test memoization with RSpec?

I have the next 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

Basic behaviour:

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

For this method I have the next 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 feel comfortable with that because I'm actually not testing memoization, so I have been following the approach on this question, and I have tried something like that:

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 on 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
like image 369
backpackerhh Avatar asked Dec 18 '13 18:12

backpackerhh


People also ask

How do I run a test in RSpec?

To run a single Rspec test file, you can do: rspec spec/models/your_spec. rb to run the tests in the your_spec. rb file.

Is RSpec used for unit testing?

RSpec is a unit test framework for the Ruby programming language. RSpec is different than traditional xUnit frameworks like JUnit because RSpec is a Behavior driven development tool. What this means is that, tests written in RSpec focus on the "behavior" of an application being tested.

Does RSpec clean database?

I use the database_cleaner gem to scrub my test database before each test runs, ensuring a clean slate and stable baseline every time. By default, RSpec will actually do this for you, running every test with a database transaction and then rolling back that transaction after it finishes.


1 Answers

Add an .and_call_original to your message expectation:

expect(Settings).to receive(:where).with({ environment: 'test' }).once.and_call_original
like image 50
Ash Wilson Avatar answered Oct 04 '22 01:10

Ash Wilson