Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rspec: Difference between allow and allow_any_instance_of

Tags:

ruby

rspec

I have a simple MySQL wrapper class which will run a query and return results.

class Rsql
  def initialize(db)
    @client = Mysql2::Client
    @db = db
  end

  def execute_query()
    client = @client.new(@db)
    client.query("select 1")
  end

end

I want to test some stuff involving the results of the query, but I don't want to actually connect to a database to get the results. I tried this test, but it doesn't work:

RSpec.describe Rsql do

  it "does it" do
    mock_database = double
    rsql = Rsql.new(mock_database)

    mock_mysql_client = double
    allow(mock_mysql_client).to receive(:query).and_return({"1" => 1})

    allow_any_instance_of(Mysql2::Client).to receive(:new).and_return(mock_mysql_client)
    expect(rsql.execute_query).to eq({"1" => 1})
  end

end

Replacing allow_any_instance_of() with allow() works. I was under the impression that allow_any_instance_of() was some kind of a global "pretend this class behaves in this way across the entire program" whereas allow() is for specific instances of a class.

Can someone explain this behavior to me? I'm new to Rspec, so I apologize if this answer is blatantly obvious. I tried searching for the answer, but I couldn't come up with the right search string to find one. Maybe I don't know enough to know when I've found it.

like image 530
Paul Springer Avatar asked Oct 01 '15 00:10

Paul Springer


1 Answers

As of RSpec 3.3 , any_instance is deprecated and not recommended to use in your tests.

From the docs:

any_instance is the old way to stub or mock any instance of a class but carries the baggage of a global monkey patch on all classes. Note that we generally recommend against using this feature.

You should only need to use allow(some_obj) going forward and the documentation has some great examples (see here).

Such as:

RSpec.describe "receive_messages" do
  it "configures return values for the provided messages" do
    dbl = double("Some Collaborator")
    allow(dbl).to receive_messages(:foo => 2, :bar => 3)
    expect(dbl.foo).to eq(2)
    expect(dbl.bar).to eq(3)
  end
end

Edit, if you really want to use any_instance, do so like this:

(Mysql2::Client).allow_any_instance.to receive(:something)

Edit2, your exact stub doesn't work because you're not stubbing an instance, you're stubbing before the object is initialized. In that case you would do allow(Mysql2::Client).to receive(:new).

like image 190
Anthony Avatar answered Oct 15 '22 22:10

Anthony