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.
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.
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.
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.
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.
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)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With