Edited at

chefspecチートシート

More than 5 years have passed since last update.

公私共にchefを使っていて、テストにchefspecを使っていますが、RSpec系になじみがないからか、なかなか覚えられないのでチートシートにまとめます。

詳細や正確な情報は本家で確認してください。

本家:https://github.com/acrmp/chefspec


specを書く上での準備


nodeの設定

cookbook, role, node等でattributesを定義していて、レシピの動作結果がこれに依存している場合は、convergeより先にnodeに値をセットする必要があります。


prepare1

let( :chef_run ) do

chef_run = ChefSpec::ChefRunner.new do |node|
node.set['my_attribute'] = 'bar'
node.set['my_other_attribute'] = {'hoge' => 'bar2'}
end.converge 'example::default'
end


chef_environmentの設定

chef-soloではサポートしていなかったりで、個人的にはあまり使いたくないところですが。Environmentはインスタンスなのでnodeと比べると面倒です。


prepare2

let( :chef_run ) do

ChefSpec::ChefRunner.new do |node|
env = Chef::Environment.new
env.name 'staging'
node.stub(:chef_environment).and_return env.name
Chef::Environment.stub(:load).and_return env
end.converge 'example::default'
end


cookbook_pathの追加

site-cookbookを使っていたりでcookbookpathが複数ある場合は、パスを追加する必要があります。


prepare3

let (:chef_run) { ChefSpec::ChefRunner.new(:cookbook_path => ['cookbooks', 'site-cookbooks']).converge 'example::default' }



specの書き方


パッケージ、サービス関連


package_and_service

# package

expect(chef_run).to install_package 'foo'
expect(chef_run).to install_package_at_version 'foo', '1.2.3'
expect(chef_run).to upgrade_package 'foo'
expect(chef_run).to install_yum_package 'yum-foo'

# service
expect(chef_run).to set_service_to_start_on_boot 'food'
expect(chef_run).to restart_service 'food'



ファイル、ディレクトリ操作関連


執筆時点(2013/02)の現行バージョンでは、cookbook_fileがcreate_fileで認識されないのでご注意を。(pullreqは承認されているので、じきに反映されると思いますが)


私の勘違いでした。0.9.0でもcookbook_fileはサポートされています。


file_and_directory

# directory

expect(chef_run).to create_directory '/var/lib/foo'
dir = chef_run.directory('/var/lib/foo')
expect(dir.mode).to eq "0700"
expect(dir).to be_owned_by('user', 'group')

# cookbook_file
expect(chef_run).to create_cookbook_file '/var/lib/hoge.txt'
file = chef_run.cookbook_file('/var/lib/hoge.txt')
expect(file.mode).to eq "0700"
expect(file).to be_owned_by('user', 'group')

# file (template)
expect(chef_run).to create_file '/var/log/bar.log'
file = chef_run.template('/var/log/bar.log')
expect(file.mode).to eq "0600"
expect(file).to be_owned_by('user', 'group')

# remote_file
expect(chef_run).to create_remote_file 'http://path/to/file'
file = chef_run.remote_file('/var/lib/hoge.txt')
expect(file.mode).to eq "0700"
expect(file).to be_owned_by('user', 'group')

# symlink
expect(chef_run).to create_link "/path/to/link"
chef_run.link("/path/to/link").to.should == "/path/from/link"



その他


others

# cron

expect(chef_run.cron('daily_job')).to be

# include recipe
expect(chef_run).to include_recipe 'foo::bar'

# if you want check xxx not, use shoud_not
expect(chef_run).not_to create_directory '/no/such/directory'



あとがき

テストの書き方やその度合いは、統一的な見解はないので人それぞれです。私の場合は、仕様として明らかにしたいもの、後続処理などに影響を与える重要なものに絞ってテストを書くようにしています。特に設計がまとまらない時はテストファーストをすることで、設計とテスト準備を同時にやっつけます。