【问题标题】:Test an inner class exists with RSpec and PDK使用 RSpec 和 PDK 测试内部类是否存在
【发布时间】:2020-01-20 09:45:07
【问题描述】:

我有一个相当基本的 puppet 模块,用于运行 tomcat 的 web 服务。我想在 Tomcat 的 catalina.out 文件上设置 logrotate,我想首先编写一个测试,确认 logrotate 包含在模块中并使用正确的设置进行设置。

这是我的webservice.pp 的精简版,例如:

class my_module::webservice (
  ...
){
  include ::tomcat_server

  ...

  logrotate::rule { 'tomcat':
    path          => '/var/log/tomcat/catalina.out',
    rotate        => 1,
    rotate_every  => 'day',
    copytruncate  => true,
    missingok     => true,
    compress      => true,
    delaycompress => true,
  }
}

我已经在我的.fixtures.yml 中包含了 logrotate forge 模块,如下所示:

fixtures:
  forge_modules:
    logrotate:
      repo: 'puppet-logrotate'
      ref:  '3.2.1'
    ...

但我只能编写一个测试来确认logrotate 包含在模块中,如下所示:

require 'spec_helper'

describe 'my_module::webservice' do
  on_supported_os.each do |os, os_facts|
    context "on #{os}" do
      let(:facts) { os_facts }

      it { is_expected.to compile }

      it { is_expected.to contain_class('logrotate') }
    end
  end
end

这不起作用(如果我从 init.pp 中删除 logrotate 块,那么测试仍然通过):

it { is_expected.to contain_class('logrotate::conf') }

也不要求with

it { is_expected.to contain_class('logrotate') \
  .with('path'          => '/var/log/tomcat/catalina.out',
        'rotate'        => 1,
        'rotate_every'  => 'day',
        'copytruncate'  => true,
        'missingok'     => true,
        'compress'      => true,
        'delaycompress' => true,
  )
}

也没有单独/嵌套的describe 块:

describe 'logrotate::rule' do
  let(:title) { 'tomcat' }
  let(:params) do
    {
        'path'          => '/var/log/tomcat/catalina.out',
        'rotate'        => 1,
        'rotate_every'  => 'day',
        'copytruncate'  => true,
        'missingok'     => true,
        'compress'      => true,
        'delaycompress' => true,
    }
  end
end

我在 rspec 文档中找不到任何提及除了测试定义的类之外的任何内容。有没有可能做我想做的事?

这是我的目录布局:

puppet
  `- modules
        `- my_module
             |- data
             |- manifests
             |    |- init.pp
             |    `- webservice.pp
             |- spec
             |    |- classes
             |    |    `- webservice_spec.rb
             |    `- spec_helper.rb
             |- .fixtures.yml
             |- Gemfile
             |- hiera.yaml
             |- metadata.json
             `- Rakefile

【问题讨论】:

  • 如果您的测试通过而不管代码内容如何,​​这听起来像是代码组织/布局和/或 RSpec/PDK 使用问题。您的布局是什么样的?您是如何使用 PDK 的?
  • 我已经用目录布局更新了描述。我通过在puppet/modules/my_module 目录中运行pdk test unit 来使用PDK。

标签: ruby rspec puppet pdk


【解决方案1】:

我有一个相当基本的 puppet 模块,用于运行 tomcat 的 web 服务。我想在 Tomcat 的 catalina.out 文件上设置 logrotate,我想首先编写一个测试,确认 logrotate 包含在模块中并使用正确的设置进行设置。

这听起来很合理。不过这...

这是我的 init.pp 的精简版,例如:

class my_module::webservice (
  ...
){

... 充其量是不好的做法。如果它存在,那么模块my_moduleinit.pp 清单应该只定义类my_module。一个名为my_module::webservice 的类应该在模块my_module 中名为webservice.pp 的清单中定义。 Puppet 在线文档中对模块布局的期望是documented。尽管您可能能够摆脱与这些规范的某些差异,但这样做只有缺点。

在这一点上,我观察到“内部阶层”不是惯用的 Puppet 术语,它表明您对正在使用的内容存在误解。具体来说,这...

logrotate::rule { 'tomcat':

[...]

...根本没有声明一个class,而是声明了一个logrotate::rule类型的resource,这显然是puppet提供的一个定义类型/对数旋转模块。一般来说,声明资源并不意味着模块中的类(如果有的话)提供资源的类型。

此外,虽然完全有可能声明 logrotate::rule 资源确实会导致类 logrotate 也包含在目录中,但这将是 logrotate::rule 的实现细节,因此,您的规范测试应该不测试它。只有当 my_module::webservice 被期望自己声明类 logrotate 时,它的测试才应该检查它。

你接着说:

这不起作用(如果我从 init.pp 中删除 logrotate 块,那么 测试仍然通过):

it { is_expected.to contain_class('logrotate::conf') }

您没有为我们提供足够的代码来确定为什么测试包含在其中时通过,但是如果满足该期望,则有些事情是非常奇怪的。 logrotate::conf 也是一个定义的(资源)类型,而不是一个类,所以期望应该永远成功。按照我上面介绍的一个主题,如果 my_module::webservice 类没有直接声明任何 logrotate::conf 资源,那么它的测试不应该检查一个。

也不要求:

it { is_expected.to contain_class('logrotate') \
  .with('path'          => '/var/log/tomcat/catalina.out',
        'rotate'        => 1,
        'rotate_every'  => 'day',
        'copytruncate'  => true,
        'missingok'     => true,
        'compress'      => true,
        'delaycompress' => true,
  )
}

当然不会成功。它表达了对logrotate 类声明的期望,但您实际声明的是logrotate::rule 类型的资源。即使logrotate::rule 确实声明了logrotate,人们也不会期望它会传递自己的参数列表。

也没有单独的/嵌套的描述块:

describe 'logrotate::rule' do

[...]

同样,这并不奇怪。这样的describe 块告诉RSpec logrotate::rule 是被测试的类。它不仅不是被测试的类(当然是my_module::webservice),而且logrotate::rule 根本不是一个类。 RSpec 当然也可以测试定义的类型,但这不是你想要的。

要测试资源是否由被测类声明,可以使用contain_type(title形式的谓词em>),其中类型名称中的任何命名空间分隔符 (::) 都替换为双下划线。例如:

it do
  is_expected.to contain_logrotate__rule('tomcat')
end

允许但可选地包含一个或多个with 子句来指定对指定资源的声明参数的期望。那么,按照您似乎一直在尝试做的事情,也许这会更充分地表达您正在寻找的东西:

require 'spec_helper'

describe 'my_module::webservice' do
  on_supported_os.each do |os, os_facts|
    context "on #{os}" do
      let(:facts) { os_facts }

      it do
        is_expected.to compile
        is_expected.to contain_logrotate__rule('tomcat')
          .with(
            path: '/var/log/tomcat/catalina.out',
            rotate: 1,
            rotate_every: 'day',
            copytruncate: true,
            missingok: true,
            compress: true,
            delaycompress: true
          )
      end
    end
  end
end

请注意,顺便说一下,当您想针对同一个示例测试多个谓词时,将它们组合在同一个 it 块中比将每个谓词放在其拥有it 块。例如,即使将两个 it 块合并为一个,您也可能会注意到测试运行时间的差异。

此外,我上面的示例演示了接近避免来自pdk validate 的警告所需的编码风格,这给我们带来了另外一点:在尝试该单元之前验证pdk validate 是否在没有错误或警告的情况下完成总是有用的测试。您可能会发现它对 Puppet 和 Ruby 代码风格都过于挑剔,但它也会挑出一些导致神秘测试失败的问题。此外,它的运行速度比测试快得多,而且它可以检测 Puppet 和 Ruby 代码中的几乎所有语法错误。由于轻微的语法错误,您的测试需要很长时间才能失败,这令人沮丧。

【讨论】:

  • 感谢您的详细解释。我只是偶尔使用 ruby​​,我们有很多未经测试的遗留代码,我希望通过单元测试来改进它们。我肯定错过了我试图添加的是资源而不是课程的知识。我在 .pp 的名称上犯了一个错误——我的模块实际上并没有被称为 my_module,我想从手头的问题中提取敏感信息。
  • @Frangipanes,我知道模块名称实际上不是my_module。当然,您可以在此答案中的任何地方替换实际的模块名称,而不会影响其含义。特别是,如果被测类改为 some_other_name::webservice,那么它的定义仍然不属于其模块的 init.pp 清单。
  • 随着我的 webservice_spec.rb 文件更新以匹配您在那里的文件,我得到一个测试失败:expected that the catalogue would contain Logrotate_rule[tomcat]
  • 对不起,@Frangipanes,我凭记忆写了一点,目前我的 Puppet 基础设施已经离开。不是用单个下划线替换::,而是用两个下划线替换它(也就是说,每个冒号都转换为自己的下划线)。 The RSpec::Puppet docs 覆盖这个。我已经更新了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多