Mocks and stubs are not new concepts that have been introduced in minitest. If you want to get detailed understanding about what are mocks and stubs you should read mocks aren’t stubs written by Martin Fowler. To achieve mocking and stubbing in minitest-rails you don’t need to include any separate gem like rspec-mocks in rspec. Let see how to do work with mocks and stubs in minitest then check what are gems available to add extra functionality.
Method stubbing
Stubbing a method means, set a pre-defined return value of a method in test-case. Let’s check how to do method stubbing in minitest:
# app/models/user.rb class User < ActiveRecord::Base def foo "Executes foo" end def bar "Executes bar" end def new_method foo bar end end
it "stubbing known method" do user = User.new user.stub(:foo, "Return from test foo") do user.new_method.must_equal "Executes bar" end end
In this example, I stub foo
and check what would be the output of new_method
. As matter of fact it won’t call actual foo
method instead it calls stub foo
method. Now that we have seen how to stub and instance method, let check how to stub on any instance.
Using this gem you can stub a method on any instance of a class. The below example demonstrates how to do that:
# app/models/user.rb class User < ActiveRecord::Base def foo "Executes foo" end def bar "Executes bar" end def new_method foo bar end end
it "stubbing on any instance" do User.stub_any_instance(:foo, "Return from test foo") do p User.new.foo User.new.foo.must_equal "Return from test foo" end p User.new.foo end
Output:
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
Run options: –seed 36985
# Running tests:
“Return from test foo”
“Executes foo”
Notice that the stub method on any instance of User is valid only in the stub_any_instance
block.
As name suggests you can stub constants of a class. Here is the example:
it "stubbing a constant" do User.stub_const(:CONSTANT, "Test constant") do User::CONSTANT.must_equal "Test constant" end end
Here CONSTANT
is a constant defined on User class. Using minitest-stub-const we can not only stub constants but also class methods. Here is the example:
# app/models/user.rb def self.add "User created on development" end def add_user User.add end
it "stub a class method" do m = MiniTest::Mock.new m.expect(:add, "Created user from testing") User.stub_const(:User, m) do User.new.add_user.must_equal "Created user from testing" end m.verify end
In above example, I stub User
with mock object m
. Now whenever User
is referred, it is replaced with the mock object m
. Now, we call the method add
on the Mock object but this method is already mocked using m.expect(:add, "Created user from testing")
.
Mocking:
Mocking an object is one of the ways to do unit testing. Mock objects are the objects that we use in a test environment instead of real objects. We can create mocks using double
in rspec to create dummy objects, where as in minitest you can do as Minitest::Mock.new
which creates mock object. On mock object we can set expected messages. Here is an example:
it "mocking a method" do user = Minitest::Mock.new user.expect(:another_method, "from test bar") user.another_method.must_equal "from test bar" user.verify end
You can also mock objects using minitest firemock as follows:
it "mocking a method" do user = Minitest::FireMock.new("User") user.expect(:another_method, "from test bar") user.another_method.must_equal "from test bar" user.verify end
The only difference between Minitest::Mock
and Minitest::FireMock
is, when you try to mock undefined method then Minitest::FireMock
raises an exception whereas Minitest::Mock
won’t.
Hope this helps while you writing mocks and stubs in minitest. Any suggestions, queries would be welcome.