我有一个Puppet类,它使用自定义Puppet函数的结果。为了确保在对类进行单元测试时只测试类中的逻辑,而不测试函数中的逻辑,我想模拟该函数。
但是,我似乎无法将模拟函数完全隔离到单个上下文中。我真正的测试代码比下面的示例大,但我将其简化为:
class break_tests {
$result = my_mocked_function('foo', 'bar', 'baz')
file { 'under_test':
content => $result,
}
}
require 'spec_helper'
def mock_mmf(return_value)
Puppet::Parser::Functions.newfunction(:'my_mocked_function', type: :rvalue) do |_args|
return return_value
end
end
# rubocop:disable Metrics/BlockLength
describe 'break_tests' do
context 'numero uno' do
before { mock_mmf('foo') }
it { should contain_file('under_test').with_content('foo') }
end
context 'numero duo' do
before { mock_mmf('bar') }
it { should contain_file('under_test').with_content('bar') }
end
end
Failures:
1) break_tests numero duo should contain File[under_test] with content supplied string
Failure/Error: it { should contain_file('under_test').with_content('bar') }
expected that the catalogue would contain File[under_test] with content set to supplied string
# ./spec/classes/break_tests_spec.rb:17:in `block (3 levels) in <top (required)>'
我尝试将其拆分为两个describe
甚至两个单独的文件,结果始终是相同的:一个上下文从另一个上下文接收输出。
在我的更大的测试用例中,大约有20个测试,它甚至更加复杂,似乎受某些上下文是否分配了事实的影响。上下文的顺序似乎无关紧要。
我在这里想念什么?
答案 0 :(得分:3)
在撰写本文时(Puppet 6.6.0,Rspec-puppet 2.7.5),不幸的是,模拟Puppet函数的整个过程仍然有些混乱。 rspec-puppet的docs仍然引用旧版Ruby API的功能并没有帮助。
您所面对的问题就是约翰·博林格(John Bollinger)在评论中所说的,您有一个编译器实例,该实例在加载Rspec文件时运行,然后在it
块中的断言稍后运行。 / p>
请记住,Rspec(Rspec本身,与Puppet无关)运行分为两个阶段:
describe
和context
块在加载Rspec文件时均进行评估。it
块(示例本身)将在以后进行缓存和评估。Rspec的作者在Stack Overflow here上对此有一个答案,我建议看看。
因此,为了避免为每个示例编译目录(这会使Rspec-puppet太慢),编译将在执行it
示例之前进行缓存。
那你能做什么?
这具有现成解决方案的优点,该解决方案可以通过众所周知的界面来模拟您的Puppet函数,并使用Tom实现的expected
功能,还可以使目录重新编译到其中。不同的例子。
缺点可能是它使用Mocha而不是Rspec-mocks,它使用传统的Ruby API-但是Rspec-puppet的文档也是如此! -自2017年以来一直没有承诺。
因此,您可以通过以下方式重写测试:
require 'spec_helper'
require 'rspec-puppet-utils'
def mock_mmf(return_value)
MockFunction.new('my_mocked_function').expected.returns(return_value)
end
describe 'test' do
context 'numero uno' do
before { mock_mmf('foo') }
it { should contain_file('under_test').with_content('foo') }
end
context 'numero duo' do
before { mock_mmf('bar') }
it { should contain_file('under_test').with_content('bar') }
end
end
然而,在幕后,Tom的代码只是对Rspec-puppet进行了猴子补丁,您可以窃取一些代码,然后像这样重构您的示例:
require 'spec_helper'
require 'rspec-puppet/cache'
module RSpec::Puppet ## Add this block
module Support
def self.clear_cache
@@cache = RSpec::Puppet::Cache.new
end
end
end
def mock_mmf(return_value)
RSpec::Puppet::Support.clear_cache ## ... and this line
Puppet::Parser::Functions.newfunction(:'my_mocked_function', type: :rvalue) do |_args|
return return_value
end
end
describe 'test' do
context 'numero uno' do
before { mock_mmf('foo') }
it { should contain_file('under_test').with_content('foo') }
end
context 'numero duo' do
before { mock_mmf('bar') }
it { should contain_file('under_test').with_content('bar') }
end
end
如果您在其他Puppet模块中搜索足够长的时间,则可能会找到更好的解决方案-甚至是使用Puppet 4 Function API的解决方案。就是说,我认为对于您的测试而言,只要假函数返回您期望的响应就没关系。