Jumbalaya Wanton
Jumbalaya Wanton

Reputation: 1621

How to test a hard coded class using a fake in MiniTest

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

Answers (3)

Warren Le
Warren Le

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

Frederick Cheung
Frederick Cheung

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

mecampbellsoup
mecampbellsoup

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

Related Questions