When I started using MiniTest one of the things I struggled with was how to mock and stub objects when constructing my tests. Here are a few patterns I’ve collected over time and now find helpful when stubbing and mocking in MiniTest.
Basic MiniTest Stub
MiniTest comes with a way to stub a value for a test. Here is a classic example stubbing time:
Time.stub :now, Time.new(2012, 11, 14).utc do MyObject.new.are_we_there_yet? end
In this example, the
:now method on the
Time object is stubbed so that it
always returns the same time while testing
#stub takes a few arguments, the first is the method you wish to stub and the
second is the value it should return when it’s called. This second value can
also be an object that responds to
#call and if it does the return value is
the result of calling the object. Which leads us to a second pattern.
Turn Your Stub Into a Spy
Since we can supply an object that responds to call for our stub, let’s turn our stub into a “spy”. Spies not only return a canned answer but are concerned with how they are called. To do this, we can use a Ruby lambda. Inside of our lambda we’ll make some assertions about the arguments the stubbed method is called with.
test "Disabling Alerts" do command = AlertsPause.new(user) response = nil args_checker = lambda do |args| assert_equal "1234qwfp", args[:user_id] assert_equal "team1234", args[:group_id] end Connection.stub :disable_notifications, args_checker do response = command.response end assert_match(/paused alerts/, response[:text]) end
In this example we are using a lambda to ensure that
gets called with the expected arguments. When the
is called on
args_checker will be called with the arguments
passed to that method. Inside the lambda we use assertions to “spy” on the
caller of this method and make sure it passes us the arguments we are expecting.
One other thing to note here, if you are using this approach and want to make
assertions on the result of something called within the
stub block, you have
to declare the local variable before the block for it to be available in the
scope outside of the block. In this example
response is used in this way; it
is declared and set to
nil at the beginning of the spec, assigned in the
block, and the used in an assertion at the end.
MiniTest::Mocks are object doubles you can use as a stand-in for some other object. Their use is really simple:
status = MiniTest::Mock.new status.expect :fine?, true checker = EverythingsFineChecker.new(status) checker.is_everything_fine? # => true status.verify
We can use
#expect to stub methods and set return values on our mock object.
If we want it to spy for us, we can call
verify at the end to make sure that
the stub was actually called. The first argument of expect is the method to
stub, the second is the value to return, and the third is optional and can be an
array of arguments. If we want to stub a method
set_speed that accepts an
argument of speed, we might do it like this for a speed of 55:
car = MiniTest::Mock.new car.expect :set_speed, car, 
Return a MiniTest::Mock from a Stub
You can combine stubs and mocks! In this example, a client is stubbed so that
it returns a mock client when it’s initialized. The
mock_client is stubbing a
list_all to always return an empty array.
mock_client = MiniTest::Mock.new mock_client.expect :list_all,  Client::Request.stub :new, mock_client do # do something using the client end
Using Standard Ruby Objects as Test Doubles
We can also use plain old Ruby in our test! One example of this is using a test
object created in the test as a stand-in for something else. Here we create a
mock client object that responds to
list with some pre-canned answers and then
use dependency injection to pass it into our job instead of one that might make
real requests to an API
class ProcessingTest < MiniTest::Unit::TestCase class MockClient def list(page:) case page when 1 ["item 1", "item 2"] when 2 ["item 3", "item 4"] else  end end end test "Process Items" do ProcessingJob.new(client: MockClient.new).perform # assert something end end
Redefine Methods on Ruby Objects
Here is a cool trick you might remember from another one of my recent
using plain Ruby you can redefine a method on any object. We can use that in a
test to stub methods! Here is an example of stubbing some methods on an instance
team, one of which raises an exception.
team = Team.create(name: "Engineering") def team.active?; true; end def team.add_member(*args); raise TeamError::NotAuthorized; end TeamFinder.stub :engineering_team, team do # Do something end # assert something
In that example, team is stubbed so that it always returned
and so that it will raise an exception when calling
The syntax is a little weird at first, but you’re likely familiar with defining class methods like:
def self.say_hello puts "hello" end
This is the same thing!
self in this case refers to the instance of class
representing the object and you are defining the “say_hello” method on it.
Using this technique is pretty powerful because it lets you quickly stub methods on any object and is especially handy when you have several methods to stub. The cool part is, it’s just Ruby!
Use The Tests, Luke
There are some patterns I’ve found helpful when writing tests in MiniTest. If you find that you are having to stub and mock a lot or that you are stubbing things to return other stubbed things that return other stubbed things it could mean that your class knows too much about other objects in your system. I’ve found it helpful to use this pain when I encounter it as an indicator that I should re-consider some closely coupled objects and find ways to make them less coupled.