Will Tomlins
Will Tomlins

Reputation: 1474

Using Rails and Rspec, how do you test that the database is not touched by a method

So I'm writing a test for a method which for performance reasons should achieve what it needs to achieve without using SQL queries. I'm thinking all I need to know is what to stub:

describe SomeModel do
  describe 'a_getter_method' do
    it 'should not touch the database' do
      thing = SomeModel.create

      something_inside_rails.should_not_receive(:a_method_querying_the_database)

      thing.a_getter_method
    end
  end
end

EDIT: to provide a more specific example:

class Publication << ActiveRecord::Base
end
class Book << Publication
end
class Magazine << Publication
end

class Student << ActiveRecord::Base
  has_many :publications

  def publications_of_type(type)
    #this is the method I am trying to test.  
    #The test should show that when I do the following, the database is queried.

    self.publications.find_all_by_type(type)
  end
end


describe Student do
  describe "publications_of_type" do
    it 'should not touch the database' do
       Student.create()
       student = Student.first(:include => :publications)
       #the publications relationship is already loaded, so no need to touch the DB

       lambda {
         student.publications_of_type(:magazine)
       }.should_not touch_the_database
    end
  end
end

So the test should fail in this example, because the rails 'find_all_by' method relies on SQL.

Upvotes: 4

Views: 5363

Answers (5)

urmurmur
urmurmur

Reputation: 232

Call

ActiveRecord::Base.connection.disconnect!

before calling the method, and check if there was no error raised.

Upvotes: -1

Ben Callaway
Ben Callaway

Reputation: 36

I don't have enough points yet to comment, but following up on the answer left by @histocrat, and the comment left by @will-tomlins. If you stub ActiveRecord::Base.connection in place of the SomeModel.connection you will avoid getting the 'stack level too deep' message, and instead get an error from the matcher itself.

Upvotes: 0

Alex Ives
Alex Ives

Reputation: 121

I know this question is super old now, but I stumbled across it trying to do the same thing. After looking around some more I've found there's a great and simple gem for this.

https://github.com/brigade/db-query-matchers

It adds an rspec matcher that takes a block so you can just use:

expect { subject.make_no_queries }.to_not make_database_queries

The configuration is super simple too, and it means you can have some calls that do make queries mixed in with a subset of calls that do connect to the database.

Upvotes: 3

nathanvda
nathanvda

Reputation: 50057

SomeModel.create creates the an instance of SomeModel and saves it to the database, and returns the created object.

Do you want to make sure that any getter method does not acces the database? In rails only retrieving associations will hit the database, and only the first time they are called.

If you have a specific implementation of a getter method, which you "suspect" would hit the database, I would assume it would be easy to test, or are you protecting against future implementations?

Maybe the easiest way would be to do SomeModel.build, and then perform your test. The build creates an instance, without saving it to the database. If the getter works, it sure didn't hit the database (since nothing should be there ---depending on your code --a bit hard to say without any clue what getter you are actually testing).

If you are testing a specific getter method, and want more relevant answer, please provide some code.

Upvotes: 0

histocrat
histocrat

Reputation: 2381

SomeModel.should_not_receive(:connection) should do it.

Upvotes: 7

Related Questions