ChefSpec不应该测试包含的食谱

时间:2013-12-12 15:37:17

标签: ruby rspec chef chefspec

我已经建立了一个用于安装Jenkins CI的食谱。它使用key食谱中的repositoryyum资源,因此我最终得到以下食谱:

yum_key "RPM-GPG-KEY-jenkins" do
  url "http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key"
  action :add
end

yum_repository "jenkins" do
  description "Jenkins-CI 3rd party repository"
  url "http://pkg.jenkins-ci.org/redhat"
  key "RPM-GPG-KEY-jenkins"
  action :add
end

当我将这个食谱包含在另一个食谱中时:

include_recipe 'sp_jenkins::default'

我用以下ChefSpec测试

测试
it 'includes the `sp_jenkins::default` recipe' do
  expect(chef_run).to include_recipe('sp_jenkins::install')
end

我的ChefSpec测试失败,输出如下:

NameError:
  Cannot find a resource for yum_key on chefspec version 0.6.1

(我不确定为什么会说版本0.6.1,gem list告诉我它使用的是3.0.2)

sp_jenkins食谱确实取决于yum食谱(metadata.rb),并且运行正常,但是,我目前正在撰写的食谱不依赖于{{1} } cookbook因此没有yumyum_key方法可用。

有没有办法阻止ChefSpec下降'纳入食谱/食谱,只测试当前的食谱?

3 个答案:

答案 0 :(得分:3)

Ohai! Julian是正确的 - ChefSpec实际上在本地机器上运行Chef Solo。它将提供者操作重写为noop,但会创建所有操作的注册表(包括执行通知时将采取的操作)。

因此,就像您需要yum食谱将此食谱汇总到一个真实节点上一样,您需要在使用ChefSpec进行单元测试时将其汇总。完成此任务的最简单方法是使用Berkshelf或Librarian解析器。要在require 'chefspec/berkshelf'之后使用Berkshelf resolver,只需chefspec

# spec_helper.rb
require 'chefspec'
require 'chefspec/berkshelf'

如果您的系统上安装了Berkshelf,它会将所有cookbook拉入临时目​​录并为您运行ChefSpec。

您可能还想看一下旨在解决类似问题的Strainer


在某种程度上不相关的说明中,我正在研究一种可能更适合您需求的large refactor to the Jenkins cookbook


来源:

  • 我写了......

答案 1 :(得分:2)

不,没有办法阻止它下降,因为它试图将整个Chef运行在内存中。

但是,如果你在ChefSpec中使用B​​erkshelf功能,那么Berkshelf依赖解析器会将所有依赖的cookbook提供给内存中的Chef运行,你就会变得很好。

答案 2 :(得分:0)

期望单独测试您的食谱 绝对正确,并且不要将其他项目的代码包括在测试范围内。不幸的是,我发现似乎没有受支持的“干净”方法来执行此操作。我能够做到这一点,但这是有代价的。

要使用此技术,请不要require 'chefspec/berkshelf' chefspec本身在测试代码中的任何位置,因为您有意收集其他食谱源代码。这是我的工作测试模块的模板(不是我的完整测试代码,因为我省略了RSpec配置选项):

describe 'mycookbook::recipe' do
  let(:chef_run) do
    ChefSpec::SoloRunner.new(platform: 'x', version: 'x') {
      # ...
    }.converge(described_recipe)
  end

  before :each do
    allow_any_instance_of(Chef::RunContext::CookbookCompiler).to receive(:cookbook_order) do
      Chef::Log.debug 'Attempt to source external cookbooks blocked'
      [described_cookbook]
    end
    allow_any_instance_of(Chef::Recipe).to receive(:include_recipe) do |recipe|
      Chef::Log.debug "Attempt to include #{recipe} blocked"
    end
  end

  it 'works' do
    # ...
  end
end

您的before中都需要这两个。我必须为之工作的是拦截:cookbook_order方法。我必须深入研究Chef的内部知识才能发现这一点。请记住,这对于使用Chef 14的我来说是有效的,但不能保证这将是未来安全的。如果CookbookCompiler的实现发生变化,则升级Chef之后,您可能必须找到其他解决方案。 (但是Chef::Recipe.include_recipe的拦截是受支持的API,因此至少在将来会更安全。)

而且,我提到这是有代价的。 (除了使用不受支持的黑客!),除非您自己的食谱内,否则您将无法对食谱或属性包含的任何expect做任何操作。像这样的测试用例将失败,因为实际上不能包含配方,因为您正在防止这种情况:

it 'includes othercookbook::recipe' do
  expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('othercookbook::recipe')
end

此外,您现在必须在before块中满足所有属性和其他先决条件,否则运行列表中的其他配方可能会满足这些条件和其他先决条件。因此,您可能会为此付出巨大的痛苦。但是,一旦完成,您的脆性测试就会少得多。 (尽管要获得100%的外部依赖关系纯度,您还必须交出fauxhai,这会更加痛苦。)