Check Method Call on Model Using Minitest

How to test a method is invoked after other in an object?

You're running into a peculiarity of Minitest here: when call is defined on a method, it appears that something in the minitest stack attempts to call it when an object is defined.

Given this test setup:

require 'minitest/autorun'

class Foo
def call
end
end

class Bar
def test
Foo.new.call
end
end

describe "Bar" do
it "invokes foo.call" do
mock = Minitest::Mock.new(Foo)
mock.expect(:call, nil)

Foo.stub :new, mock do |args|
Bar.new.test
mock.verify
end
end
end

It fails as described. But if you rename call to my_call, it passes:

require 'minitest/autorun'

class Foo
def my_call
end
end

class Bar
def test
Foo.new.my_call
end
end

describe "Bar" do
it "invokes foo.my_call" do
mock = Minitest::Mock.new(Foo)
mock.expect(:my_call, nil)

Foo.stub :new, mock do |args|
Bar.new.test
mock.verify
end
end
end

The same test passes under RSpec:

class Foo
def call
end
end

class Bar
def test
Foo.new.call
end
end

describe "Bar" do
it "invokes foo.call" do
mock = double
expect(mock).to receive(:call)
expect(Foo).to receive(:new).and_return(mock)
Bar.new.test
end
end

How to assert certain method is called with Ruby minitest framework?

With minitest you use expect method to set the expectation for a method to be called on a mock object like so

obj = MiniTest::Mock.new
obj.expect :right

If you want to set expectation with parameters and return values then:

obj.expect :right, return_value, parameters

And for the concrete object like so:

obj = SomeClass.new
assert_send([obj, :right, *parameters])

Minitest - How do you test a method isn't invoked?

Since you're open to adding a gem, mocha works well for this. Add the gem, then use mocha's Expectation#never. Your test can then look like:

test 'Foo.new.bar is not invoked' do
model = Model.new
Foo.expects(:call).never
model.update!(not_the_title: 'value')
end

how do I test a private method on controller, using minitest?

You don't.

Testing private methods in general is frowned upon*.

In Rails you mainly test controllers through integration tests. These are tests that send real HTTP requests to your application and then you write assertions about the response or the side effects of sending the request.

"[...] You can test what cookies are set, what HTTP code is returned, how the view looks, or what mutations happened to the DB, but testing the innards of the controller is just not a good idea."

- David Heinemeier Hansson

An example of an integration test is:

require "test_helper"

class PokerControllerTest < ActionDispatch::IntegrationTest
test "can see the welcome page" do
get "/path/to/somewhere"
assert_select "h1", "Welcome to my awesome poker app"
end
end

If you have a method that you want to test in isolation it does not belong in your controller in the first place and arguably it should not be a private method either.

You should test the public API of your objects. The public API of a controller is the methods that respond to HTTP requests via the router.

The use of ActionController::TestCase is discouraged outside of legacy applications.

New Rails applications no longer generate functional style controller
tests and they should only be used for backward compatibility.
Integration style controller tests perform actual requests, whereas
functional style controller tests merely simulate a request. Besides,
integration tests are as fast as functional tests and provide lot of
helpers such as as, parsed_body for effective testing of controller
actions including even API endpoints.

Besides that the issues with your test is that you're calling the test method on the instance of ActionController::TestCase and not your controller. You're then shadowing the method by defining an instance variable with the same name which is rarely a good thing.

Even if you did call the method on the controller Ruby would still raise an error since you're calling a private method from outside the object - thats the whole point of private methods.

While you could violate encapsulation by calling @controller.send(:test) this is just a bad idea on so many levels.

How to test a method is called with Minitest?

You can do something like this:

describe 'Post#save' do
it "calls Mailer::notify!" do
mock = MiniTest::Mock.new
mock.expect(:call, nil, ['bla'])

Mailer.stub(:notify!, mock) do
post.save
end

mock.verify
end
end

And yes, that is easier and more intuitive in RSpec...

Unit testing a model method in Rails using MiniTest - How to use stubs or mock objects?

I started to practice the assumption that a unit test should test a model/function isolated from external factors.
So I decided to stub external behavior not inherent to the model. The unit test for the method of the Participant model could look like this.

def test_paid_expense_items
item1 = MiniTest::Mock.new
items = MiniTest::Mock.new
items.expect :where, [item1], [Hash]
@participant.stub :items, items do
assert_includes @participant.paid_expense_items, item1, "Participant's paid expenses should include items paid by himself/herself"
end
end

So far I'm happy with that solution. However feel free to comment if you think you have a better solution or complementary information or any other contribution.



Related Topics



Leave a reply



Submit