如何在 MiniTest 中使用假的来测试硬编码 class
How to test a hard coded class using a fake in MiniTest
我有一个 PlantTree
作业调用 PlantTree
服务对象。我想测试该作业以确定它使用 tree
参数实例化 PlantTree
服务并调用 call
方法。
我对服务的作用或结果不感兴趣。它有自己的测试,我不想为这份工作重复这些测试。
# 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
如您所见,PlantTree
class 被硬编码在作业的 perform
方法中。所以我不能伪造它并将其作为依赖项传递。有没有办法在执行方法的生命周期内伪造它?像...
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
我正在使用 Rails' 默认堆栈,它使用 MiniTest。我知道这可以用 Rspec 来完成,但我只对 MiniTest 感兴趣。如果仅使用 MiniTest 或默认的 Rails 堆栈无法做到这一点,我愿意使用外部库。
不确定如何在 Minitest 中编写此代码,但您可以使用模拟(此处为 RSpec 语法):
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
此测试将失败,除非您的 PlantTree
服务对象 (1) 通过 #new
实例化,并且 (2) 得到 #call
编辑。
免责声明:这可能不是 100% 的功能,但这应该是正确的想法,假设我已经正确阅读了 OP 的 Q。
你应该可以做类似的事情
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
这会在 PlantTree 上存根新方法,检查树的参数,然后 returns 模拟而不是 PlantTree 实例。该模拟进一步验证调用。
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
我有一个 PlantTree
作业调用 PlantTree
服务对象。我想测试该作业以确定它使用 tree
参数实例化 PlantTree
服务并调用 call
方法。
我对服务的作用或结果不感兴趣。它有自己的测试,我不想为这份工作重复这些测试。
# 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
如您所见,PlantTree
class 被硬编码在作业的 perform
方法中。所以我不能伪造它并将其作为依赖项传递。有没有办法在执行方法的生命周期内伪造它?像...
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
我正在使用 Rails' 默认堆栈,它使用 MiniTest。我知道这可以用 Rspec 来完成,但我只对 MiniTest 感兴趣。如果仅使用 MiniTest 或默认的 Rails 堆栈无法做到这一点,我愿意使用外部库。
不确定如何在 Minitest 中编写此代码,但您可以使用模拟(此处为 RSpec 语法):
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
此测试将失败,除非您的 PlantTree
服务对象 (1) 通过 #new
实例化,并且 (2) 得到 #call
编辑。
免责声明:这可能不是 100% 的功能,但这应该是正确的想法,假设我已经正确阅读了 OP 的 Q。
你应该可以做类似的事情
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
这会在 PlantTree 上存根新方法,检查树的参数,然后 returns 模拟而不是 PlantTree 实例。该模拟进一步验证调用。
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