repl.info

PuppetのFacterでもrspecしたい!

そういうわけです。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行程度のものなので、テストがなくてもまぁ分かるのだけど、滅多に触らないのでテストがあると安心すると思います。