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

10,328

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.

Share:
10,328

Related videos on Youtube

Juergen
Author by

Juergen

Updated on September 16, 2022

Comments

  • Juergen
    Juergen over 1 year

    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.

  • Juergen
    Juergen almost 9 years
    Ok, that works, thanks! However, I wonder how I would approach an example where I would need a class method and an instance method. e.g. new_book = Book.new\ content = new_book.read_page(5)Do I first need to mock the object using book_object = double("Book")\ allow(book_object).to receive(:read_page).and_return(...) and then mock the class by book = class_double("Book")\ allow(book).to receive(:new).and_return(book_object) Is this the way to go?
  • Myron Marston
    Myron Marston almost 9 years
    It's hard to read your question due to the comment formatting. Can you send your question to the RSpec mailing list? groups.google.com/group/rspec