PuppetのFacterでもrspecしたい!

Pocket

そういうわけです。Rubyなので、Rubyのテストツールでテストできると複雑なCustomFacterの維持・管理がしやすいですよね。

今日は「あるFacterについてのテストを2つ以上書くときテスト毎にそのFacterの値をクリアする」ということをしたのでそれについて書きます。

まずはテストを1つ書いてみる

ホスト名からタグ(何のタグか、は気にしない)を生成するFacterのテストを書く、ということにします。最初のコードはこれ。雑…

require 'puppet'
require 'facter'

Facter.add(:tag) do
  setcode do
    if Facter.value(:hostname) =~ /(hoge)-\d.*/
      $1
    else
      ''
    end
  end
end

テストはこうしましょう。CustomFacter内で別のFacterを使っているので、allowとreceiveを使ってstub化します。

require_relative './custom_facter'
require 'rspec'


describe 'custom facter foo' do
  def tag
    Facter.fact(:tag).value
  end

  before :each do
    allow(Facter).to receive(:value).with(:hostname).and_return(hostname)
  end

  describe 'hostname の prefix が hoge の場合' do
    let(:hostname) { 'hoge-192-168-0-1' }

    it 'tag が hoge であること' do
      expect(tag).to eq 'hoge'
    end
  end
end

テストを実行します。いいですね。

% be rspec -f d ./custom_facter_spec.rb

custom facter foo
  hostname の prefix が hoge の場合
    tag が hoge であること

Finished in 0.00702 seconds (files took 0.84902 seconds to load)
1 example, 0 failures

2つ以上のテストがあると?

さて、次です。いろいろなホスト名に対応したいので、安直にこうしてみます。

require 'puppet'
require 'facter'

Facter.add(:tag) do
  setcode do
    if Facter.value(:hostname) =~ /(hoge|piyo)-\d.*/
      $1
    else
      ''
    end
  end
end

テストはこうなりますね。

require_relative './custom_facter'
require 'rspec'


describe 'custom facter foo' do
  def tag
    Facter.fact(:tag).value
  end

  before :each do
    allow(Facter).to receive(:value).with(:hostname).and_return(hostname)
  end

  describe 'hostname の prefix が hoge の場合' do
    let(:hostname) { 'hoge-192-168-0-1' }

    it 'tag が hoge であること' do
      expect(tag).to eq 'hoge'
    end
  end

  describe 'hostname の prefix が piyo の場合' do
    let(:hostname) { 'piyo-192-168-0-2' }

    it 'tag が piyo であること' do
      expect(tag).to eq 'piyo'
    end
  end
end

よさそうです。テストを実行してみましょう。

% be rspec -f d ./custom_facter_spec.rb

custom facter foo
  hostname の prefix が hoge の場合
    tag が hoge であること
  hostname の prefix が piyo の場合
    tag が piyo であること (FAILED - 1)

Failures:

  1) custom facter foo hostname の prefix が piyo の場合 tag が piyo であること
     Failure/Error: expect(tag).to eq 'piyo'

       expected: "piyo"
            got: "hoge"

       (compared using ==)
     # ./custom_facter_spec.rb:26:in `block (3 levels) in <top (required)>'

Finished in 0.0229 seconds (files took 0.85121 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./custom_facter_spec.rb:25 # custom facter foo hostname の prefix が piyo の場合 tag が piyo であること

うーむ、失敗してしまった。追加した「hostname の prefix が piyo の場合」がうまくいっていません。これは、1つ前のテストで一度tag facterが呼び出されたため、その値がメモリに保持されてしまっているためです。

解決策

解決策としては、テスト毎にtag facterの結果をクリアしてやればよい。ベストかどうかは自信が無いですが、今回はbefore(:each)で以下を実行して解決しました。

Facter.collection.instance_variable_get(:@facts)[:tag].flush

クリアしたいFacterをピンポイントでflushしてやります。Facter.clearというメソッドもあるのだけど、それを実行すると他のものも消えてしまって面倒だった。最終的なテストコードはこうなります。

require_relative './custom_facter'
require 'rspec'


describe 'custom facter foo' do
  def tag
    Facter.fact(:tag).value
  end

  before :each do
    Facter.collection.instance_variable_get(:@facts)[:tag].flush
    allow(Facter).to receive(:value).with(:hostname).and_return(hostname)
  end

  describe 'hostname の prefix が hoge の場合' do
    let(:hostname) { 'hoge-192-168-0-1' }

    it 'tag が hoge であること' do
      expect(tag).to eq 'hoge'
    end
  end

  describe 'hostname の prefix が piyo の場合' do
    let(:hostname) { 'piyo-192-168-0-2' }

    it 'tag が piyo であること' do
      expect(tag).to eq 'piyo'
    end
  end
end

実行すると、テストは2つとも通ることが確認できます。

% be rspec -f d ./custom_facter_spec.rb

custom facter foo
  hostname の prefix が hoge の場合
    tag が hoge であること
  hostname の prefix が piyo の場合
    tag が piyo であること

Finished in 0.00719 seconds (files took 0.81246 seconds to load)
2 examples, 0 failures

これで、少ないコードでCustomFacterのテストを書けるようになりました。実際のCustomFacterは30行程度のものなので、テストがなくてもまぁ分かるのだけど、滅多に触らないのでテストがあると安心すると思います。

 

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *