如何在不测试Foo.bar
方法(已在其他地方测试过)的行为的情况下测试以下示例中是否正在调用bar
?
# Code
class Alpha
def process
Foo.bar
end
end
以下规格是我到目前为止所做的。不幸的是,这种方法抛出了“已定义类”警告,因为Foo已经在我的项目的其他地方定义过。
# Spec
let(:alpha) { Alpha.new }
let(:klass) { MiniTest::Mock.new }
subject { alpha.process }
it "calls Foo.bar" do
klass.expect(:bar, '') # Define method call expectation
Foo = klass # Redefine Foo as a mock object
subject # Run method being tested
klass.verify # Confirm method was called
end
我不希望我的测试依赖于Foo
类,因为这是一个外部依赖项,我不想测试Foo.bar
的响应值,因为那可能随意改变。
答案 0 :(得分:6)
为了模拟这样的类,你必须插入一个像这样的注入点:
class Alpha
def initialize(opts = {})
@foo_class = opts[:foo_class] || Foo
end
def process
@foo_class.bar
end
end
这是有效的,因为类名在Ruby中只是一个常量,可以像任何其他值一样赋值。因此,不是在Foo
中对Alpha
类的调用进行硬编码,而是在新的@foo_class
实例变量指向的任何内容上调用该方法。大多数情况下,除非你传递其他内容,否则它仍然是Foo
类。我通常将这样的模拟参数隐藏为可选参数,就像我在这里做的那样。我也没有在最终用户目标文档中包含对它们的引用,因为它们在技术上不是我认为是公共API的一部分。
然后在测试中,您可以像这样初始化Alpha
对象:
fooClassMock = MiniTest::Mock.new
fooClassMock.expect(:bar, '')
alpha = Alpha.new(:foo_class => fooClassMock)
你应该得到你正在寻找的结果。
答案 1 :(得分:2)
我知道这是一个老问题,但我一直在寻找一个解决方案,不需要修改业务代码来简化测试,我想我想出了一个解决方案。
即使您使用的是Rails,也需要将gem "minitest"
添加到Gemfile
,然后将require "minitest/mock"
添加到test_helper.rb
。
it "calls Foo.bar" do
bar_is_called = false
bar_lambda = ->{
bar_is_called = true
}
Foo.stub(:bar, bar_lambda) do
Alpha.new.process
end
bar_is_called.must_equal true
end
.stub
可以传递一个返回值,或者当传递响应.call
的内容时,它会在其上调用.call
(Docs for the stub method)。在这个例子中。调用bar_lambda
,更改bar_is_called
的值。这会验证是否已调用Foo.bar
。
它在测试语法中的工作方式也类似:
test 'Foo.bar is called' do
bar_is_called = false
bar_lambda = ->{
bar_is_called = true
}
Foo.stub(:bar, bar_lambda) do
Alpha.new.process
end
assert bar_is_called
end