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