Reputation: 1621
I have a PlantTree
job that calls a PlantTree
service object. I would like to test the job to ascertain that it instantiates the PlantTree
service with a tree
argument and calls the call
method.
I'm not interested in what the service does or the result. It has its own tests, and I don't want to repeat those tests for the job.
# app/jobs/plant_tree_job.rb
class PlantTreeJob < ActiveJob::Base
def perform(tree)
PlantTree.new(tree).call
end
end
# app/services/plant_tree.rb
class PlantTree
def initialize(tree)
@tree = tree
end
def call
# Do stuff that plants the tree
end
end
As you can see, the PlantTree
class is hard coded in the perform
method of the job. So I can't fake it and pass it in as a dependency. Is there a way I can fake it for the life time of the perform method? Something like...
class PlantTreeJobTest < ActiveJob::TestCase
setup do
@tree = create(:tree)
end
test "instantiates PlantTree service with `@tree` and calls `call`" do
# Expectation 1: PlantTree will receive `new` with `@tree`
# Expectation 2: PlatTree will receive `call`
PlantTreeJob.perform_now(@tree)
# Verify that expections 1 and 2 happened.
end
end
I'm using Rails' default stack, which uses MiniTest. I know this can be done with Rspec, but I'm only interested in MiniTest. If it's not possible to do this with MiniTest only, or the default Rails stack, I'm open to using an external library.
Upvotes: 0
Views: 1399
Reputation: 195
plant_tree_mock= MiniTest::Mock.new
dummy = Object.new
tree = Object.new
plant_tree_mock.expect(:call, dummy, [tree])
PlantTree.stub(:new, plant_tree_mock) do
PlantTreeJob.perform_now(tree)
end
assert plant_tree_mock.verify
Upvotes: 0
Reputation: 84114
You should be able to do something like
mock= MiniTest::Mock.new
mock.expect(:call, some_return_value)
PlantTree.stub(:new, -> (t) { assert_equal(tree,t); mock) do
PlantTreeJob.perform_now(@tree)
end
mock.verify
This stubs the new method on PlantTree, checks the argument to tree and then returns a mock instead of a PlantTree instance. That mock further verifies that call was called.
Upvotes: 1
Reputation: 1471
Not sure how to write this in Minitest, but you can use a mock (RSpec syntax here):
expect(PlantTree).to receive(:new).with(tree)
expect_any_instance_of(PlantTree).to receive(:call)
# NOTE: either of the above mocks (which are expectations)
# will fail if more than 1 instance receives the method you've mocked -
# that is, PlantTree#new and PlantTree#call
# In RSpec, you can also write this using `receive_message_chain`:
# expect(PlantTree).to receive_message_chain(:new, :call)
job = PlantTreeJob.new(@tree)
job.perform
This test will fail unless your PlantTree
service object (1) gets instantiated via #new
, and (2) gets #call
'ed.
Disclaimer: this might not be 100% functional but this should be the right idea, assuming I've read OP's Q correctly.
Upvotes: 0