rspec-puppet单元测试跨用例修改外层类全局参数的实现方法
问题根因
你遇到的报错和测试失效本质是两个认知和逻辑上的错位:
let(:params)是 rspec-puppet 框架专属的、用于给被测资源传自身入参的方法。你在外层的无maxheap测试块里根本没有定义let(:params),嵌套的带maxheap测试块里调用super()去找父级的params方法,自然找不到,直接抛出NoMethodError。$::mymod::maxheap是外层mymod类的类作用域变量,根本不属于被测试的mymod::as::myressource自定义资源的入参,把它塞到params里从逻辑上就无法生效。
不改动现有代码的测试修正方案
如果暂时保留当前跨作用域引用类变量的实现,只需要把测试里错用的params传参,改成在pre_condition里给mymod类传递maxheap参数即可,正确的用例写法如下:
require 'spec_helper' describe 'mymod::as::myressource' do let(:title) { 'as_myressource_test' } on_supported_os.each do |os, os_facts| context "no maxheap on #{os}" do let(:facts) { os_facts } # 无自定义maxheap时的pre_condition,和原实现保持一致 let(:pre_condition) do " class { 'mymod': } " end it { is_expected.to compile } it { is_expected.to contain_mymod__as__myressource2("myres_myres2-as_myressource_test").with({ :maxheap => /^[0-9]+M$/, })} # 带maxheap的测试用例,完全重写pre_condition完成传参 context "with maxheap on #{os}" do let(:pre_condition) do " class { 'mymod': maxheap => '3G', } " end it { is_expected.to compile } it { is_expected.to contain_mymod__as__myressource2("myres_myres2-as_myressource_test").with({ :maxheap => '3G' })} end end end end
这种写法的核心逻辑是:$::mymod::maxheap的值是在mymod类实例化时确定的,所以要修改这个值,只需要在类的声明里传入对应的参数即可,完全不需要用到params方法。
现有实现的规范问题
你当前自定义资源直接跨作用域引用外层类变量的写法,属于Puppet社区明确不推荐的反模式:
- 会直接让自定义资源丧失可复用性:
mymod::as::myressource不再是一个独立可调用的define,必须等mymod类提前加载完成才能拿到变量,单独调用时会直接抛出未声明变量的错误。 - 跨作用域的变量耦合会大幅提高后续排障、单元测试的成本,没法对这个define做独立的逻辑验证。
符合社区规范的实现就是你之前已经跑通的myressource3的模式:
- 把
maxheap作为mymod::as::myressource自定义资源本身的可选入参,给一个默认的undef值 - 在
mymod类实例化这个define的时候,把类自身拿到的maxheap参数传递给define
改写后的核心代码示例:mymod/manifests/as/myressource.pp
define mymod::as::myressource ( Optional[String] $maxheap = undef, ) { $effective_maxheap = ($maxheap =~ NotUndef) ? { true => $maxheap, default => "${::facts['memory']['system']['total_bytes'] * 3 / 4 / 1024 / 1024}M", } notice("effective_maxheap = ${effective_maxheap}") mymod::as::myressource2 {"myres_myres2-${title}": maxheap => $effective_maxheap, } }
mymod/manifests/init.pp
class mymod( Optional[String] $maxheap = undef, ) { notice("${title} start maxheap=${maxheap} ...") # 实例化define时传递类拿到的maxheap参数 mymod::as::myressource {'whatever': maxheap => $maxheap, } notice("${title} end ...") }
这种写法下,你的自定义资源完全独立,测试的时候可以直接通过params传不同的maxheap值,不需要依赖外层类的上下文,和你最开始跑通的测试逻辑完全一致。
内容的提问来源于stack exchange,提问作者Ralph Urban
相关产品推荐
相关产品推荐

