Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I mock a Class with Ruby?

I'm using minitest/mock and would like to mock a class. I'm not trying to test the model class itself, but rather trying to test that a service (SomeService) interacts with the model (SomeModel).

I came up with this (Hack::ClassDelegate), but I'm not convinced it's a good idea:

require 'minitest/autorun'
require 'minitest/mock'

module Hack
  class ClassDelegate
    def self.set_delegate(delegate); @@delegate = delegate; end
    def self.method_missing(sym, *args, &block)
      @@delegate.method_missing(sym, *args, &block)
    end 
  end
end

class TestClassDelegation < MiniTest::Unit::TestCase

  class SomeModel < Hack::ClassDelegate ; end

  class SomeService
    def delete(id)
      SomeModel.delete(id)
    end
  end

  def test_delegation
    id = '123456789'
    mock = MiniTest::Mock.new
    mock.expect(:delete, nil, [id])

    SomeModel.set_delegate(mock)
    service = SomeService.new
    service.delete(id)

    assert mock.verify
  end
end

I'm pretty sure that mocking a class is not a great idea anyway, but I have a legacy system that I need to write some tests for and I don't want to change the system until I've wrapped some tests around it.

like image 636
darscan Avatar asked Mar 06 '12 14:03

darscan


People also ask

How does a mock work in Ruby?

Mocks are a handy tool for writing tests in Ruby. You can use them to fake an object and verify that the correct methods were called against it. Perfect for testing a method that integrates closely with another class or module.

What is mocking in Ruby on Rails?

Mocks. Mocks are “smart” stubs, their purpose is to verify that some method was called. They are created with some expectations (expected method calls) and can then be verified to ensure those methods were called.

What is the difference between stubs and mocks in Ruby testing?

Stub: A class or object that implements the methods of the class/object to be faked and returns always what you want. Mock: The same of stub, but it adds some logic that "verifies" when a method is called so you can be sure some implementation is calling that method.

What is stubbing in Ruby?

Stub. Definition of stub: A method stub is an instruction to an object (real or test double) to return a. known value in response to a message.


2 Answers

I think that's a little complicated. What about this:

mock = MiniTest::Mock.new
SomeService.send(:const_set, :SomeModel, mock)
mock.expect(:delete, nil, [1])
service = SomeService.new
service.delete(1)
mock.verify
SomeService.send(:remove_const, :SomeModel)
like image 120
Elliot Winkler Avatar answered Sep 22 '22 06:09

Elliot Winkler


After running into the same problem and thinking about it for quite a while, I found that temporarily changing the class constant is probably the best way to do it (just as Elliot suggests in his answer).

However, I found a nicer way to do it: https://github.com/adammck/minitest-stub-const

Using this gem, you could write your test like this:

def test_delegation
  id = '123456789'
  mock = MiniTest::Mock.new
  mock.expect(:delete, nil, [id])

  SomeService.stub_const 'SomeModel', mock do
    service = SomeService.new
    service.delete(id)
  end

  assert mock.verify
end
like image 29
severin Avatar answered Sep 22 '22 06:09

severin