It looks like the only source of information for stubbing a chain of methods properly are 10+ years ago:
https://www.viget.com/articles/stubbing-method-chains-with-mocha/
I feel pretty frustrated that I can't find information of how to do this properly. I want to basically mock Rails.logger.error
.
UPDATE: I basically want to do something as simple as
def my_action
Rails.logger.error "My Error"
render json: { success: true }
end
And want to write a test like this:
it 'should call Rails.logger.error' do
post my_action_url
???
end
I think maybe you misunderstood the term chain of methods
in this case, they imply the chain of ActiveRecord::Relation
those be able to append another. In your case Rails.logger
is a ActiveSupport::Logger
and that's it. You can mock the logger and test your case like this:
mock = Minitest::Mock.new
mock.expect :error, nil, ["My Error"] # expect method error be called
Rails.stub :logger, mock do
post my_action_url
end
mock.verify
Beside that, I personally don't like the way they test by stub chain of method
, it's so detail, for example: i have a test case for query top ten users and i write a stub for chain of methods such as User.where().order()...
, it's ok at first, then suppose i need to refactor the query or create a database view top users for optimize performance purpose, as you see, i need to stub the chain of methods again. Why do we just treat the test case as black box, in my case, the test case should not know (and don't care) how i implement the query, it only check that the result should be the top ten users.
update
Since each request Rails internal call Logger.info
then if you want ignore it, you could stub
that function:
def mock.info; end
In case you want to test the number of method called or validate the error messages, you can use a spy
(i think you already know those unit test concepts)
mock = Minitest::Mock.new
def mock.info; end
spy = Hash.new { |hash, key| hash[key] = [] }
mock.expect(:error, nil) do |error|
spy[:error] << error
end
Rails.stub :logger, mock do
post my_action_url
end
assert spy[:error].size == 1 # call time
assert spy[:error] == ["My Error"] # error messages
It's better to create a test helper
method to reuse above code. You can improve that helper method behave like the mockImplementation
in Jest if you want.