How to stub a class method using rspec/rspec-mocks

Juergen picture Juergen · Jun 15, 2015 · Viewed 10.3k times · Source

I am using rspec-mock for test-driven-development. I am starting implementing a single class and mocking/stubbing the other classes using rspec-mock. Mocking objects of classes yet to be implemented works well. However when I try to mock a class method of a class that does not exist yet, I haven't been successful. My class "Hashes" should have a class method "calculate_hashes" receiving a filename and returning a hash.

I tried

 allow(Hashes).to receive(:calculate_hash) do |file| 
      # looks up what to return
 end

which give the error "Hashes is not a class". I then implemented a class "Hashes"

class Hashes
end

and then only tried to stub the class method in the same way. This gives the error "Hashes does not implement: calculate_hash" When I then add the method to my class definition:

class Hashes
    def self.calculate_hash(filename)
    end
end

it finally works and my stubbing of this class method works using "allow(Hashes)" as seen in the example above. I just wonder if there is a way of accomplishing this without writing this class skeleton.

Or am I maybe trying to accomplish something in an inappropriate way? Or is rspec-mock maybe not the right tool to do this?

Any help is greatly appreciated.

Answer

Myron Marston picture Myron Marston · Jun 15, 2015

For your workflow, I think it's going to work better to use a class_double than than to stub the Hashes class directly. allow(Hashes) is always going to require that the Hashes constant is defined. It's simply how Ruby works and RSpec can't do anything about that. With a class double, you can instead do this:

class_double("Hashes", :calculate_hash => canned_return_value).as_stubbed_const

# or

hashes = class_double("Hashes").as_stubbed_const
allow(hashes).to receive(:calculate_hash) do |file|
  # look up what to return
end

class_double("Hashes") provides you with a test double that, when the Hashes constant is defined, will verify the mocked and stubbed methods against the Hashes class definition, but when it is not defined, will act just like a normal double that allows anything to be mocked or stubbed on it. The as_stubbed_const bit tells rspec-mocks to stub the Hashes constant for the duration of the example so that any references to Hashes get your class double rather than the real Hashes class, even if the Hashes class has never been defined.