chefspec 7.2.1 → 7.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +21 -13
- data/CHANGELOG.md +4 -0
- data/Gemfile +14 -10
- data/README.md +322 -1025
- data/README_old.md +1291 -0
- data/Rakefile +8 -3
- data/examples/apt_package/spec/install_spec.rb +1 -1
- data/examples/apt_package/spec/lock_spec.rb +1 -1
- data/examples/apt_package/spec/purge_spec.rb +1 -1
- data/examples/apt_package/spec/reconfig_spec.rb +1 -1
- data/examples/apt_package/spec/remove_spec.rb +1 -1
- data/examples/apt_package/spec/unlock_spec.rb +1 -1
- data/examples/apt_package/spec/upgrade_spec.rb +1 -1
- data/examples/apt_repository/spec/add_spec.rb +1 -1
- data/examples/apt_repository/spec/remove_spec.rb +1 -1
- data/examples/apt_update/spec/periodic_spec.rb +1 -1
- data/examples/apt_update/spec/update_spec.rb +1 -1
- data/examples/attributes/spec/default_spec.rb +1 -1
- data/examples/batch/spec/run_spec.rb +1 -1
- data/examples/cab_package/spec/install_spec.rb +1 -1
- data/examples/cab_package/spec/remove_spec.rb +1 -1
- data/examples/cached/spec/default_spec.rb +1 -1
- data/examples/chef_gem/spec/install_spec.rb +1 -1
- data/examples/chef_gem/spec/purge_spec.rb +1 -1
- data/examples/chef_gem/spec/reconfig_spec.rb +1 -1
- data/examples/chef_gem/spec/remove_spec.rb +1 -1
- data/examples/chef_gem/spec/upgrade_spec.rb +1 -1
- data/examples/chocolatey_package/spec/install_spec.rb +1 -1
- data/examples/chocolatey_package/spec/remove_spec.rb +1 -1
- data/examples/chocolatey_package/spec/upgrade_spec.rb +1 -1
- data/examples/compile_time/spec/default_spec.rb +1 -1
- data/examples/cookbook_file/spec/create_if_missing_spec.rb +1 -1
- data/examples/cookbook_file/spec/create_spec.rb +1 -1
- data/examples/cookbook_file/spec/delete_spec.rb +1 -1
- data/examples/cookbook_file/spec/touch_spec.rb +1 -1
- data/examples/core/recipes/default.rb +1 -0
- data/examples/core/spec/default_spec.rb +7 -0
- data/examples/core/spec/non_recipe_spec.rb +5 -0
- data/examples/cron/spec/create_spec.rb +1 -1
- data/examples/cron/spec/delete_spec.rb +1 -1
- data/examples/custom_matcher/spec/install_spec.rb +1 -1
- data/examples/custom_matcher/spec/remove_spec.rb +1 -1
- data/examples/custom_resource/spec/default_spec.rb +1 -1
- data/examples/custom_resource_block/resources/default.rb +3 -0
- data/examples/custom_resource_block/spec/default_spec.rb +10 -0
- data/examples/directory/spec/create_spec.rb +1 -1
- data/examples/directory/spec/delete_spec.rb +1 -1
- data/examples/dnf_package/spec/install_spec.rb +1 -1
- data/examples/dnf_package/spec/purge_spec.rb +1 -1
- data/examples/dnf_package/spec/remove_spec.rb +1 -1
- data/examples/dnf_package/spec/upgrade_spec.rb +1 -1
- data/examples/do_nothing/spec/default_spec.rb +1 -1
- data/examples/dpkg_package/spec/install_spec.rb +1 -1
- data/examples/dpkg_package/spec/purge_spec.rb +1 -1
- data/examples/dpkg_package/spec/remove_spec.rb +1 -1
- data/examples/dsc_resource/spec/run_spec.rb +1 -1
- data/examples/dsc_script/spec/run_spec.rb +1 -1
- data/examples/env/spec/create_spec.rb +1 -1
- data/examples/env/spec/delete_spec.rb +1 -1
- data/examples/env/spec/modify_spec.rb +1 -1
- data/examples/execute/spec/run_spec.rb +1 -1
- data/examples/expect_exception/spec/compile_error_spec.rb +1 -1
- data/examples/expect_exception/spec/converge_error_spec.rb +1 -1
- data/examples/expect_exception/spec/no_error_spec.rb +1 -1
- data/examples/file/spec/create_if_missing_spec.rb +1 -1
- data/examples/file/spec/create_spec.rb +1 -1
- data/examples/file/spec/delete_spec.rb +1 -1
- data/examples/file/spec/touch_spec.rb +1 -1
- data/examples/freebsd_package/spec/install_spec.rb +1 -1
- data/examples/freebsd_package/spec/remove_spec.rb +1 -1
- data/examples/gem_package/spec/install_spec.rb +1 -1
- data/examples/gem_package/spec/purge_spec.rb +1 -1
- data/examples/gem_package/spec/reconfig_spec.rb +1 -1
- data/examples/gem_package/spec/remove_spec.rb +1 -1
- data/examples/gem_package/spec/upgrade_spec.rb +1 -1
- data/examples/git/spec/checkout_spec.rb +1 -1
- data/examples/git/spec/export_spec.rb +1 -1
- data/examples/git/spec/sync_spec.rb +1 -1
- data/examples/group/spec/create_spec.rb +1 -1
- data/examples/group/spec/manage_spec.rb +1 -1
- data/examples/group/spec/modify_spec.rb +1 -1
- data/examples/group/spec/remove_spec.rb +1 -1
- data/examples/guards/spec/default_spec.rb +1 -1
- data/examples/heavy_provider_light_resource/spec/provider_service_spec.rb +1 -1
- data/examples/homebrew_package/spec/install_spec.rb +1 -1
- data/examples/homebrew_package/spec/purge_spec.rb +1 -1
- data/examples/homebrew_package/spec/remove_spec.rb +1 -1
- data/examples/homebrew_package/spec/upgrade_spec.rb +1 -1
- data/examples/http_request/spec/delete_spec.rb +1 -1
- data/examples/http_request/spec/get_spec.rb +1 -1
- data/examples/http_request/spec/head_spec.rb +1 -1
- data/examples/http_request/spec/options_spec.rb +1 -1
- data/examples/http_request/spec/post_spec.rb +1 -1
- data/examples/http_request/spec/put_spec.rb +1 -1
- data/examples/ifconfig/spec/add_spec.rb +1 -1
- data/examples/ifconfig/spec/delete_spec.rb +1 -1
- data/examples/ifconfig/spec/disable_spec.rb +1 -1
- data/examples/ifconfig/spec/enable_spec.rb +1 -1
- data/examples/include_recipe/spec/default_spec.rb +1 -1
- data/examples/ips_package/spec/install_spec.rb +1 -1
- data/examples/ips_package/spec/remove_spec.rb +1 -1
- data/examples/ips_package/spec/upgrade_spec.rb +1 -1
- data/examples/launchd/spec/create_if_missing_spec.rb +1 -1
- data/examples/launchd/spec/create_spec.rb +1 -1
- data/examples/launchd/spec/delete_spec.rb +1 -1
- data/examples/launchd/spec/disable_spec.rb +1 -1
- data/examples/launchd/spec/enable_spec.rb +1 -1
- data/examples/library_patch/libraries/default.rb +5 -0
- data/examples/library_patch/recipes/default.rb +1 -0
- data/examples/library_patch/spec/default_spec.rb +14 -0
- data/examples/link/spec/create_spec.rb +1 -1
- data/examples/link/spec/delete_spec.rb +1 -1
- data/examples/link/spec/link_to_spec.rb +1 -1
- data/examples/log/spec/write_spec.rb +1 -1
- data/examples/macports_package/spec/install_spec.rb +1 -1
- data/examples/macports_package/spec/purge_spec.rb +1 -1
- data/examples/macports_package/spec/remove_spec.rb +1 -1
- data/examples/macports_package/spec/upgrade_spec.rb +1 -1
- data/examples/mdadm/spec/assemble_spec.rb +1 -1
- data/examples/mdadm/spec/create_spec.rb +1 -1
- data/examples/mdadm/spec/stop_spec.rb +1 -1
- data/examples/mount/spec/disable_spec.rb +1 -1
- data/examples/mount/spec/enable_spec.rb +1 -1
- data/examples/mount/spec/mount_spec.rb +1 -1
- data/examples/mount/spec/remount_spec.rb +1 -1
- data/examples/mount/spec/umount_spec.rb +1 -1
- data/examples/msu_package/spec/install_spec.rb +1 -1
- data/examples/msu_package/spec/remove_spec.rb +1 -1
- data/examples/multiple_actions/spec/default_spec.rb +1 -1
- data/examples/multiple_actions/spec/reversed_spec.rb +1 -1
- data/examples/multiple_actions/spec/sequential_spec.rb +1 -1
- data/examples/multiple_run_action/spec/default_spec.rb +1 -1
- data/examples/nothing_matcher/spec/default_spec.rb +1 -1
- data/examples/notifications/spec/before_spec.rb +1 -1
- data/examples/notifications/spec/chained_spec.rb +1 -1
- data/examples/notifications/spec/default_spec.rb +1 -1
- data/examples/notifications/spec/delayed_spec.rb +1 -1
- data/examples/notifications/spec/immediately_spec.rb +1 -1
- data/examples/ohai/spec/reload_spec.rb +1 -1
- data/examples/osx_profile/spec/install_spec.rb +1 -1
- data/examples/osx_profile/spec/remove_spec.rb +1 -1
- data/examples/package/spec/install_spec.rb +1 -1
- data/examples/package/spec/lock_spec.rb +1 -1
- data/examples/package/spec/purge_spec.rb +1 -1
- data/examples/package/spec/reconfig_spec.rb +1 -1
- data/examples/package/spec/remove_spec.rb +1 -1
- data/examples/package/spec/unlock_spec.rb +1 -1
- data/examples/package/spec/upgrade_spec.rb +1 -1
- data/examples/pacman_package/spec/install_spec.rb +1 -1
- data/examples/pacman_package/spec/purge_spec.rb +1 -1
- data/examples/pacman_package/spec/remove_spec.rb +1 -1
- data/examples/pacman_package/spec/upgrade_spec.rb +1 -1
- data/examples/paludis_package/spec/install_spec.rb +1 -1
- data/examples/paludis_package/spec/purge_spec.rb +1 -1
- data/examples/paludis_package/spec/remove_spec.rb +1 -1
- data/examples/paludis_package/spec/upgrade_spec.rb +1 -1
- data/examples/portage_package/spec/install_spec.rb +1 -1
- data/examples/portage_package/spec/purge_spec.rb +1 -1
- data/examples/portage_package/spec/remove_spec.rb +1 -1
- data/examples/portage_package/spec/upgrade_spec.rb +1 -1
- data/examples/powershell_script/spec/run_spec.rb +1 -1
- data/examples/reboot/spec/cancel_spec.rb +1 -1
- data/examples/reboot/spec/now_spec.rb +1 -1
- data/examples/reboot/spec/request_spec.rb +1 -1
- data/examples/recipe_block/metadata.rb +1 -0
- data/examples/recipe_block/spec/recipe_block_spec.rb +34 -0
- data/examples/recipe_block/templates/apache2.conf.erb +1 -0
- data/examples/registry_key/spec/create_if_missing_spec.rb +1 -1
- data/examples/registry_key/spec/create_spec.rb +1 -1
- data/examples/registry_key/spec/delete_key_spec.rb +1 -1
- data/examples/registry_key/spec/delete_spec.rb +1 -1
- data/examples/remote_directory/spec/create_if_missing_spec.rb +1 -1
- data/examples/remote_directory/spec/create_spec.rb +1 -1
- data/examples/remote_directory/spec/delete_spec.rb +1 -1
- data/examples/remote_file/spec/create_if_missing_spec.rb +1 -1
- data/examples/remote_file/spec/create_spec.rb +1 -1
- data/examples/remote_file/spec/delete_spec.rb +1 -1
- data/examples/remote_file/spec/touch_spec.rb +1 -1
- data/examples/render_file/spec/default_spec.rb +12 -1
- data/examples/render_file/spec/template_helpers_spec.rb +1 -1
- data/examples/roles/spec/default_spec.rb +1 -1
- data/examples/route/spec/add_spec.rb +1 -1
- data/examples/route/spec/delete_spec.rb +1 -1
- data/examples/rpm_package/spec/install_spec.rb +1 -1
- data/examples/rpm_package/spec/remove_spec.rb +1 -1
- data/examples/rpm_package/spec/upgrade_spec.rb +1 -1
- data/examples/ruby_block/spec/create_spec.rb +1 -1
- data/examples/ruby_block/spec/run_spec.rb +1 -1
- data/examples/runner/recipes/default.rb +1 -0
- data/examples/runner/spec/default_spec.rb +11 -0
- data/examples/script/spec/run_bash_spec.rb +1 -1
- data/examples/script/spec/run_csh_spec.rb +1 -1
- data/examples/script/spec/run_ksh_spec.rb +1 -1
- data/examples/script/spec/run_perl_spec.rb +1 -1
- data/examples/script/spec/run_python_spec.rb +1 -1
- data/examples/script/spec/run_ruby_spec.rb +1 -1
- data/examples/script/spec/run_script_spec.rb +1 -1
- data/examples/server/spec/client_spec.rb +1 -1
- data/examples/server/spec/data_bag_spec.rb +1 -1
- data/examples/server/spec/environment_spec.rb +1 -1
- data/examples/server/spec/exotic_port_spec.rb +1 -1
- data/examples/server/spec/node_spec.rb +4 -4
- data/examples/server/spec/render_with_cached_spec.rb +1 -1
- data/examples/server/spec/role_spec.rb +1 -1
- data/examples/server/spec/search_spec.rb +1 -1
- data/examples/service/spec/disable_spec.rb +1 -1
- data/examples/service/spec/enable_spec.rb +1 -1
- data/examples/service/spec/reload_spec.rb +1 -1
- data/examples/service/spec/restart_spec.rb +1 -1
- data/examples/service/spec/start_spec.rb +1 -1
- data/examples/service/spec/stop_spec.rb +1 -1
- data/examples/smartos_package/spec/install_spec.rb +1 -1
- data/examples/smartos_package/spec/remove_spec.rb +1 -1
- data/examples/smartos_package/spec/upgrade_spec.rb +1 -1
- data/examples/solaris_package/spec/install_spec.rb +1 -1
- data/examples/solaris_package/spec/remove_spec.rb +1 -1
- data/examples/solaris_package/spec/upgrade_spec.rb +1 -1
- data/examples/spec_attributes/attributes/default.rb +1 -0
- data/examples/spec_attributes/recipes/default.rb +5 -0
- data/examples/spec_attributes/spec/default_spec.rb +54 -0
- data/examples/spec_platform/recipes/default.rb +3 -0
- data/examples/spec_platform/spec/default_spec.rb +36 -0
- data/examples/spec_step_into/recipes/default.rb +3 -0
- data/examples/spec_step_into/resources/one.rb +3 -0
- data/examples/spec_step_into/resources/two.rb +3 -0
- data/examples/spec_step_into/spec/default_spec.rb +50 -0
- data/examples/state_attrs/spec/default_spec.rb +1 -1
- data/examples/step_into/spec/default_spec.rb +2 -2
- data/examples/stub_command/spec/default_spec.rb +1 -1
- data/examples/stub_data_bag/spec/default_spec.rb +1 -1
- data/examples/stub_data_bag_item/spec/default_spec.rb +1 -1
- data/examples/stub_node/spec/default_spec.rb +1 -1
- data/examples/stub_search/spec/block_spec.rb +1 -1
- data/examples/stub_search/spec/default_spec.rb +1 -1
- data/examples/stubs_for/resources/default.rb +20 -0
- data/examples/stubs_for/resources/old.rb +26 -0
- data/examples/stubs_for/spec/default_spec.rb +240 -0
- data/examples/subscribes/spec/before_spec.rb +1 -1
- data/examples/subscribes/spec/chained_spec.rb +1 -1
- data/examples/subscribes/spec/default_spec.rb +1 -1
- data/examples/subscribes/spec/delayed_spec.rb +1 -1
- data/examples/subscribes/spec/immediately_spec.rb +1 -1
- data/examples/subversion/spec/checkout_spec.rb +1 -1
- data/examples/subversion/spec/export_spec.rb +1 -1
- data/examples/subversion/spec/force_export_spec.rb +1 -1
- data/examples/subversion/spec/sync_spec.rb +1 -1
- data/examples/systemd_unit/spec/create_spec.rb +1 -1
- data/examples/systemd_unit/spec/delete_spec.rb +1 -1
- data/examples/systemd_unit/spec/disable_spec.rb +1 -1
- data/examples/systemd_unit/spec/enable_spec.rb +1 -1
- data/examples/systemd_unit/spec/mask_spec.rb +1 -1
- data/examples/systemd_unit/spec/reload_or_restart_spec.rb +1 -1
- data/examples/systemd_unit/spec/reload_or_try_restart_spec.rb +1 -1
- data/examples/systemd_unit/spec/restart_spec.rb +1 -1
- data/examples/systemd_unit/spec/start_spec.rb +1 -1
- data/examples/systemd_unit/spec/stop_spec.rb +1 -1
- data/examples/systemd_unit/spec/try_restart_spec.rb +1 -1
- data/examples/systemd_unit/spec/unmask_spec.rb +1 -1
- data/examples/template/spec/create_if_missing_spec.rb +1 -1
- data/examples/template/spec/create_spec.rb +1 -1
- data/examples/template/spec/delete_spec.rb +1 -1
- data/examples/template/spec/touch_spec.rb +1 -1
- data/examples/use_inline_resources/spec/default_spec.rb +1 -1
- data/examples/user/spec/create_spec.rb +1 -1
- data/examples/user/spec/lock_spec.rb +1 -1
- data/examples/user/spec/manage_spec.rb +1 -1
- data/examples/user/spec/modify_spec.rb +1 -1
- data/examples/user/spec/remove_spec.rb +1 -1
- data/examples/user/spec/unlock_spec.rb +1 -1
- data/examples/windows_package/spec/install_spec.rb +1 -1
- data/examples/windows_package/spec/remove_spec.rb +1 -1
- data/examples/windows_service/spec/configure_startup_spec.rb +1 -1
- data/examples/windows_service/spec/disable_spec.rb +1 -1
- data/examples/windows_service/spec/enable_spec.rb +1 -1
- data/examples/windows_service/spec/reload_spec.rb +1 -1
- data/examples/windows_service/spec/restart_spec.rb +1 -1
- data/examples/windows_service/spec/start_spec.rb +1 -1
- data/examples/windows_service/spec/stop_spec.rb +1 -1
- data/examples/yum_package/spec/install_spec.rb +1 -1
- data/examples/yum_package/spec/lock_spec.rb +1 -1
- data/examples/yum_package/spec/purge_spec.rb +1 -1
- data/examples/yum_package/spec/remove_spec.rb +1 -1
- data/examples/yum_package/spec/unlock_spec.rb +1 -1
- data/examples/yum_package/spec/upgrade_spec.rb +1 -1
- data/examples/yum_repository/spec/add_spec.rb +1 -1
- data/examples/yum_repository/spec/create_spec.rb +1 -1
- data/examples/yum_repository/spec/delete_spec.rb +1 -1
- data/examples/yum_repository/spec/makecache_spec.rb +1 -1
- data/examples/yum_repository/spec/remove_spec.rb +1 -1
- data/examples/zypper_package/spec/install_spec.rb +1 -1
- data/examples/zypper_package/spec/lock_spec.rb +1 -1
- data/examples/zypper_package/spec/purge_spec.rb +1 -1
- data/examples/zypper_package/spec/remove_spec.rb +1 -1
- data/examples/zypper_package/spec/unlock_spec.rb +1 -1
- data/examples/zypper_package/spec/upgrade_spec.rb +1 -1
- data/lib/chefspec.rb +1 -1
- data/lib/chefspec/api.rb +33 -14
- data/lib/chefspec/api/core.rb +195 -0
- data/lib/chefspec/api/described.rb +55 -0
- data/lib/chefspec/api/do_nothing.rb +24 -19
- data/lib/chefspec/api/include_recipe.rb +26 -21
- data/lib/chefspec/api/link.rb +26 -21
- data/lib/chefspec/api/notifications.rb +38 -33
- data/lib/chefspec/api/reboot.rb +11 -6
- data/lib/chefspec/api/render_file.rb +35 -30
- data/lib/chefspec/api/state_attrs.rb +28 -23
- data/lib/chefspec/api/stubs.rb +183 -0
- data/lib/chefspec/api/stubs_for.rb +134 -0
- data/lib/chefspec/api/subscriptions.rb +35 -30
- data/lib/chefspec/api/user.rb +222 -217
- data/lib/chefspec/berkshelf.rb +1 -1
- data/lib/chefspec/deprecations.rb +2 -11
- data/lib/chefspec/errors.rb +1 -0
- data/lib/chefspec/extensions.rb +4 -1
- data/lib/chefspec/extensions/chef/client.rb +3 -4
- data/lib/chefspec/extensions/chef/conditional.rb +3 -2
- data/lib/chefspec/extensions/chef/cookbook/gem_installer.rb +25 -28
- data/lib/chefspec/extensions/chef/cookbook_loader.rb +13 -0
- data/lib/chefspec/extensions/chef/cookbook_uploader.rb +3 -2
- data/lib/chefspec/extensions/chef/data_query.rb +5 -8
- data/lib/chefspec/extensions/chef/lwrp_base.rb +17 -21
- data/lib/chefspec/extensions/chef/provider.rb +36 -0
- data/lib/chefspec/extensions/chef/resource.rb +51 -4
- data/lib/chefspec/extensions/chef/resource/freebsd_package.rb +13 -16
- data/lib/chefspec/extensions/chef/run_context/cookbook_compiler.rb +64 -0
- data/lib/chefspec/matchers/render_file_matcher.rb +36 -26
- data/lib/chefspec/matchers/resource_matcher.rb +1 -1
- data/lib/chefspec/rspec.rb +3 -8
- data/lib/chefspec/runner.rb +8 -0
- data/lib/chefspec/server_methods.rb +1 -1
- data/lib/chefspec/server_runner.rb +13 -9
- data/lib/chefspec/solo_runner.rb +136 -32
- data/lib/chefspec/version.rb +1 -1
- data/spec/unit/deprecations_spec.rb +1 -26
- data/spec/unit/macros_spec.rb +14 -14
- data/spec/unit/matchers/render_file_matcher_spec.rb +8 -1
- data/spec/unit/solo_runner_spec.rb +3 -3
- data/templates/errors/may_need_to_specify_platform.erb +1 -1
- data/templates/errors/shell_out_not_stubbed.erb +10 -0
- metadata +38 -4
- data/lib/chefspec/macros.rb +0 -222
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4149e5d01cef76e9961206a3c21e026f8d1514c5411d4e736775494e7df2fd5e
|
4
|
+
data.tar.gz: 1c2f041d3734fb0d1b4311652e545753ae93e13ac1d5699287754a2005ab30c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86b9445a05713b120154104b065300b29944c2178277c0dbaf57271baed0d675b9582272c73044101cc36625b7ff9e1d189efff6f8525dbac2aed5ba225be0fd
|
7
|
+
data.tar.gz: 6dc383647ef0ca7f97c3a4ecfbe68a4323949a95d03c8ee853c80000682a7eab9deebed4db231cedd7ba0765c37d18a3304e145ed012739b6a5283b927f43889
|
data/.travis.yml
CHANGED
@@ -16,25 +16,33 @@ bundler_args: --jobs 7 --retry 3
|
|
16
16
|
|
17
17
|
matrix:
|
18
18
|
include:
|
19
|
-
- env:
|
20
|
-
rvm: 2.5.
|
21
|
-
- env:
|
19
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 14.3.37'\""
|
20
|
+
rvm: 2.5.1
|
21
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 14.2.0'\""
|
22
|
+
rvm: 2.5.1
|
23
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 14.1.12'\""
|
24
|
+
rvm: 2.5.1
|
25
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 14.0.202'\""
|
26
|
+
rvm: 2.5.1
|
27
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.10.0'\""
|
28
|
+
rvm: 2.4.4
|
29
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.9.4'\""
|
30
|
+
rvm: 2.4.4
|
31
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.8.5'\""
|
22
32
|
rvm: 2.4.3
|
23
|
-
- env:
|
33
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.7.16'\""
|
24
34
|
rvm: 2.4.3
|
25
|
-
- env:
|
35
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.6.4'\""
|
26
36
|
rvm: 2.4.2
|
27
|
-
- env:
|
37
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.5.3'\""
|
28
38
|
rvm: 2.4.2
|
29
|
-
- env:
|
39
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.4.24'\""
|
30
40
|
rvm: 2.4.2
|
31
|
-
- env:
|
41
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.3.42'\""
|
32
42
|
rvm: 2.4.2
|
33
|
-
- env:
|
43
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.2.20'\""
|
34
44
|
rvm: 2.4.2
|
35
|
-
- env:
|
45
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.1.31'\""
|
36
46
|
rvm: 2.4.2
|
37
|
-
- env:
|
47
|
+
- env: "GEMFILE_MOD=\"gem 'chef', '= 13.0.118'\""
|
38
48
|
rvm: 2.4.2
|
39
|
-
- env: CHEF_VERSION=12.21.31
|
40
|
-
rvm: 2.3.5
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# CHANGELOG for ChefSpec
|
2
2
|
|
3
|
+
## 7.3.0 (August 30, 2018)
|
4
|
+
|
5
|
+
- Major syntax overhaul and update. Check out the [README](/README.md) for examples of the updated syntax. The older syntax is still present and will be supported at least until ChefSpec 8.0, but documentation has been moved to [README_old](/README_old.md).
|
6
|
+
|
3
7
|
## 7.2.1 (May, 8, 2018)
|
4
8
|
|
5
9
|
- Updated an example and test for that example
|
data/Gemfile
CHANGED
@@ -1,19 +1,23 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
# env var for travis
|
4
|
-
if ENV['CHEF_VERSION']
|
5
|
-
if ENV['CHEF_VERSION'] == "master"
|
6
|
-
gem 'chef', git: "https://github.com/chef/chef"
|
7
|
-
gem 'ohai', git: "https://github.com/chef/ohai"
|
8
|
-
else
|
9
|
-
gem 'chef', ENV['CHEF_VERSION']
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
3
|
gemspec
|
14
4
|
|
15
5
|
group :development do
|
16
6
|
gem 'rake'
|
17
7
|
gem 'redcarpet'
|
18
8
|
gem 'yard'
|
9
|
+
gem 'pry'
|
10
|
+
gem 'pry-byebug'
|
19
11
|
end
|
12
|
+
|
13
|
+
if ENV["GEMFILE_MOD"]
|
14
|
+
puts "GEMFILE_MOD: #{ENV['GEMFILE_MOD']}"
|
15
|
+
instance_eval(ENV["GEMFILE_MOD"])
|
16
|
+
else
|
17
|
+
gem 'chef', git: "https://github.com/chef/chef"
|
18
|
+
gem 'ohai', git: "https://github.com/chef/ohai"
|
19
|
+
end
|
20
|
+
|
21
|
+
# If you want to load debugging tools into the bundle exec sandbox,
|
22
|
+
# add these additional dependencies into Gemfile.local
|
23
|
+
eval_gemfile(__FILE__ + ".local") if File.exist?(__FILE__ + ".local")
|
data/README.md
CHANGED
@@ -4,1241 +4,538 @@
|
|
4
4
|
|
5
5
|
ChefSpec is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers.
|
6
6
|
|
7
|
-
ChefSpec runs your
|
7
|
+
ChefSpec runs your cookbooks locally while skipping making actual changes. This has two primary benefits:
|
8
8
|
|
9
9
|
- It's really fast!
|
10
|
-
- Your tests can vary node attributes, operating systems, and
|
10
|
+
- Your tests can vary node attributes, operating systems, and other system data to assert behavior under varying conditions.
|
11
11
|
|
12
12
|
## Important Notes
|
13
13
|
|
14
14
|
- **ChefSpec requires Ruby 2.2 or later and Chef 12.14.89 or later!**
|
15
15
|
- **This documentation corresponds to the master branch, which may be unreleased. Please check the README of the latest git tag or the gem's source for your version's documentation!**
|
16
|
-
- **Each resource matcher is self-documented using [Yard](http://rubydoc.info/github/chefspec/chefspec) and has a corresponding test recipe in the [examples directory](https://github.com/chefspec/chefspec/tree/master/examples).**
|
17
16
|
|
18
|
-
|
17
|
+
**ChefSpec aims to maintain compatibility with at least the two most recent minor versions of Chef.** If you are running an older version of Chef it may work, or you will need to run an older version of ChefSpec.
|
19
18
|
|
20
|
-
|
19
|
+
As a general rule, if it is tested in the Travis CI matrix, it is a supported version.
|
21
20
|
|
22
|
-
|
21
|
+
## Quick Start
|
23
22
|
|
24
|
-
##
|
23
|
+
## When To Use ChefSpec?
|
25
24
|
|
26
|
-
|
25
|
+
As mentioned before, ChefSpec is built for speed. In order to run your tests as
|
26
|
+
quickly as possible (and to allow running tests on your workstation), ChefSpec
|
27
|
+
runs your recipe code with all the resource actions disabled. This means that
|
28
|
+
ChefSpec excels at testing complex logic in a cookbook, but can't actually tell
|
29
|
+
you if a cookbook is doing the right thing. Integration testing is provided by
|
30
|
+
the [Test Kitchen](https://kitchen.ci/) project, and for most simple cookbooks
|
31
|
+
without much logic in them we recommend you start off with integration tests and
|
32
|
+
only return to ChefSpec and unit tests as your code gets more complicated.
|
27
33
|
|
28
|
-
|
34
|
+
There are two common "units" of code in Chef cookbooks, custom resources and
|
35
|
+
recipes. If you find yourself with a lot of recipes that are so complex they
|
36
|
+
require unit tests, consider if they can be refactored as custom resources.
|
29
37
|
|
30
|
-
|
31
|
-
package 'foo'
|
32
|
-
```
|
38
|
+
### Testing a Custom Resource
|
33
39
|
|
34
|
-
|
40
|
+
If you have have a cookbook with a custom resource `resources/greet.rb` like:
|
35
41
|
|
36
42
|
```ruby
|
37
|
-
|
43
|
+
resource_name :mycookbook_greet
|
38
44
|
|
39
|
-
|
40
|
-
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '16.04').converge(described_recipe) }
|
45
|
+
property :greeting, String, default: 'Hello'
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
end
|
47
|
+
action :run do
|
48
|
+
log "#{new_resource.greeting} world"
|
45
49
|
end
|
46
50
|
```
|
47
51
|
|
48
|
-
|
49
|
-
|
50
|
-
1. At the top of the spec file we require the chefspec gem. This is required so that our custom matchers are loaded. In larger projects, it is common practice to create a file named "spec_helper.rb" and include ChefSpec and perform other setup tasks in that file.
|
51
|
-
2. The `describe` keyword is part of RSpec and indicates that everything nested beneath is describing the `example::default` recipe. The convention is to have a separate spec for each recipe in your cookbook.
|
52
|
-
3. The `let` block on creates the `ChefSpec:SoloRunner` with mocked Ohai data for Ubuntu 16.04 from [Fauxhai](https://github.com/chefspec/fauxhai). It then does a fake Chef run with the run_list of `example::default`. Any subsequent examples can then refer to `chef_run` in order to make assertions about the resources that were created during the mock converge.
|
53
|
-
4. The `described_recipe` macro is a ChefSpec helper method that infers the recipe from the `describe` block. Alternatively you could specify the recipe directly.
|
54
|
-
5. The `it` block is an example specifying that the `foo` package is installed. Normally you will have multiple `it` blocks per recipe, each making a single assertion.
|
55
|
-
|
56
|
-
## Configuration
|
57
|
-
|
58
|
-
ChefSpec exposes a configuration layer at the global level and at the `Runner` level. The following settings are available:
|
52
|
+
You can test that resource by creating a spec file `spec/greet_spec.rb`:
|
59
53
|
|
60
54
|
```ruby
|
61
|
-
|
62
|
-
|
63
|
-
# the location of the calling spec file])
|
64
|
-
config.cookbook_path = '/var/cookbooks'
|
65
|
-
|
66
|
-
# Specify the path for Chef Solo to find roles (default: [ascending search])
|
67
|
-
config.role_path = '/var/roles'
|
68
|
-
|
69
|
-
# Specify the path for Chef Solo to find environments (default: [ascending search])
|
70
|
-
config.environment_path = '/var/environments'
|
71
|
-
|
72
|
-
# Specify the path for Chef Solo file cache path (default: nil)
|
73
|
-
config.file_cache_path = Chef::Config[:file_cache_path]
|
55
|
+
# Load ChefSpec and put our test into ChefSpec mode.
|
56
|
+
require 'chefspec'
|
74
57
|
|
75
|
-
|
76
|
-
|
58
|
+
# Describing our custom resource.
|
59
|
+
describe 'mycookbook_greet' do
|
60
|
+
# Normally ChefSpec skips running resources, but for this test we want to
|
61
|
+
# actually run this one custom resource.
|
62
|
+
step_into :mycookbook_greet
|
63
|
+
# Nothing in this test is platform-specific, so use the latest Ubuntu for
|
64
|
+
# simulated data.
|
65
|
+
platform 'ubuntu'
|
66
|
+
|
67
|
+
# Create an example group for testing the resource defaults.
|
68
|
+
context 'with the default greeting' do
|
69
|
+
# Set the subject of this example group to a snippet of recipe code calling
|
70
|
+
# our custom resource.
|
71
|
+
recipe do
|
72
|
+
mycookbook_greet 'test'
|
73
|
+
end
|
77
74
|
|
78
|
-
|
79
|
-
|
75
|
+
# Confirm that the resources created by our custom resource's action are
|
76
|
+
# correct. ChefSpec matchers all take the form `action_type(name)`.
|
77
|
+
it { is_expected.to write_log('Hello world') }
|
78
|
+
end
|
80
79
|
|
81
|
-
#
|
82
|
-
|
80
|
+
# Create a second example group to test a different block of recipe code.
|
81
|
+
context 'with a custom greeting' do
|
82
|
+
# This time our test recipe code sets a property on the custom resource.
|
83
|
+
recipe do
|
84
|
+
mycookbook_greet 'test' do
|
85
|
+
greeting 'Bonjour'
|
86
|
+
end
|
87
|
+
end
|
83
88
|
|
84
|
-
|
85
|
-
|
89
|
+
# Use the same kind of matcher as before to confirm the action worked.
|
90
|
+
it { is_expected.to write_log('Bonjour world') }
|
91
|
+
end
|
86
92
|
end
|
87
93
|
```
|
88
94
|
|
89
|
-
|
90
|
-
|
91
|
-
```ruby
|
92
|
-
# Override only the operating system version (platform is still "ubuntu" from above)
|
93
|
-
ChefSpec::SoloRunner.new(version: '16.04')
|
94
|
-
|
95
|
-
# Use a different operating system platform and version
|
96
|
-
ChefSpec::SoloRunner.new(platform: 'centos', version: '7.3.1611')
|
97
|
-
|
98
|
-
# Specify a different cookbook_path
|
99
|
-
ChefSpec::SoloRunner.new(cookbook_path: '/var/my/other/path', role_path: '/var/my/roles')
|
100
|
-
|
101
|
-
# By default ChefSpec sets a new temporary directory for file caching in every run.
|
102
|
-
# This can be overridden by passing the `file_cache_path` option.
|
103
|
-
# Note: Resources containing `Chef::Config[:file_cache_path]` in their name or
|
104
|
-
# attributes, will fail unless this option is specified.
|
105
|
-
ChefSpec::SoloRunner.new(file_cache_path: Chef::Config[:file_cache_path])
|
106
|
-
|
107
|
-
# Add debug log output
|
108
|
-
ChefSpec::SoloRunner.new(log_level: :debug).converge(described_recipe)
|
109
|
-
```
|
110
|
-
|
111
|
-
**NOTE** You do not _need_ to specify a platform and version to use ChefSpec. However, some cookbooks may rely on [Ohai](http://github.com/chef/ohai) data that ChefSpec cannot not automatically generate. Specifying the `platform` and `version` keys instructs ChefSpec to load stubbed Ohai attributes from another platform using [fauxhai](https://github.com/chefspec/fauxhai). See the [PLATFORMS.md file](https://github.com/chefspec/fauxhai/blob/master/PLATFORMS.md) in the Fauxhai repo for a complete list of platforms and versions for use with ChefSpec.
|
95
|
+
And then run your test using `chef exec rspec`.
|
112
96
|
|
113
|
-
###
|
97
|
+
### Testing a Recipe
|
114
98
|
|
115
|
-
|
99
|
+
As a general rule of thumb, only very complex recipes benefit from ChefSpec unit
|
100
|
+
tests. If you find yourself writing a lot of recipe unit tests, consider converting
|
101
|
+
the recipes to custom resources instead. For the sake of example we'll use a
|
102
|
+
simple recipe, `recipes/farewell.rb`:
|
116
103
|
|
117
104
|
```ruby
|
118
|
-
|
119
|
-
# When using ChefSpec::ServerRunner, specify the data storage method (options: in_memory, on_disk; default: in_memory)
|
120
|
-
# If you are in a low-memory environment, setting this value to :on_disk may improve speed and/or reliability.
|
121
|
-
config.server_runner_data_store = :on_disk
|
122
|
-
|
123
|
-
# Whether or not to clear the cookbooks on the ChefZero instance in-between each test (default: true)
|
124
|
-
# For most people, not clearing the cookbooks will drastically improve test execution time. This is a
|
125
|
-
# good option for people who are using chefspec within the context of a single Berksfile or Policyfile.
|
126
|
-
config.server_runner_clear_cookbooks = false
|
127
|
-
end
|
105
|
+
log "#{node["mycookbook"]["farewell"]} world"
|
128
106
|
```
|
129
107
|
|
130
|
-
|
131
|
-
|
132
|
-
If you are using Berkshelf, simply require `chefspec/berkshelf` in your `spec_helper` after requiring `chefspec`:
|
108
|
+
You can test that recipe by creating a spec file `spec/farewell_spec.rb`:
|
133
109
|
|
134
110
|
```ruby
|
135
|
-
#
|
111
|
+
# Load ChefSpec and put our test into ChefSpec mode.
|
136
112
|
require 'chefspec'
|
137
|
-
require 'chefspec/berkshelf'
|
138
|
-
```
|
139
|
-
|
140
|
-
Requiring this file will:
|
141
113
|
|
142
|
-
|
143
|
-
|
144
|
-
|
114
|
+
# Describing our recipe. The group name should be the recipe string as you would
|
115
|
+
# use it with include_recipe.
|
116
|
+
describe 'mycookbook::farewell' do
|
117
|
+
# Nothing in this test is platform-specific, so use the latest Ubuntu for
|
118
|
+
# simulated data.
|
119
|
+
platform 'ubuntu'
|
120
|
+
|
121
|
+
# Create an example group for testing the recipe defaults.
|
122
|
+
context 'with default attributes' do
|
123
|
+
# Since there was no `recipe do .. end` block here, the default subject is
|
124
|
+
# recipe we named in the `describe`. ChefSpec matchers all take the form
|
125
|
+
# `action_type(name)`.
|
126
|
+
it { is_expected.to write_log('Goodbye world') }
|
127
|
+
end
|
145
128
|
|
146
|
-
|
129
|
+
# Create a second example group to test with attributes.
|
130
|
+
context 'with a custom farewell' do
|
131
|
+
# Set an override attribute for this group.
|
132
|
+
override_attributes['mycookbook']['farewell'] = 'Adios'
|
147
133
|
|
148
|
-
|
149
|
-
|
150
|
-
|
134
|
+
# Use the same kind of matcher as before to confirm the recipe worked.
|
135
|
+
it { is_expected.to write_log('Adios world') }
|
136
|
+
end
|
151
137
|
end
|
152
138
|
```
|
153
139
|
|
154
|
-
|
140
|
+
## Cookbook Dependencies
|
155
141
|
|
156
|
-
|
142
|
+
If your cookbook depends on other cookbooks, you must ensure ChefSpec knows how
|
143
|
+
to fetch those dependencies. If you use a monorepo-style layout with all your
|
144
|
+
cookbooks in a single `cookbooks/` folder, you don't need to do anything.
|
157
145
|
|
158
|
-
If you are using
|
146
|
+
If you are using Berkshelf, `require 'chefspec/berkshelf'` in your spec file (or `spec_helper.rb`):
|
159
147
|
|
160
148
|
```ruby
|
161
|
-
# spec_helper.rb
|
162
149
|
require 'chefspec'
|
163
|
-
require 'chefspec/
|
164
|
-
```
|
165
|
-
|
166
|
-
Requiring this file will:
|
167
|
-
|
168
|
-
- Create a temporary working directory
|
169
|
-
- Download all the dependencies listed in your `Cheffile` into the temporary directory
|
170
|
-
- Set ChefSpec's `cookbook_path` to the temporary directory
|
171
|
-
|
172
|
-
**NOTE** In order to test the cookbook in the current working directory, you have to write your `Cheffile` like this:
|
173
|
-
|
174
|
-
```ruby
|
175
|
-
# Cheffile
|
176
|
-
site 'https://supermarket.chef.io/api/v1'
|
177
|
-
|
178
|
-
cookbook 'name_of_your_cookbook', path: '.'
|
150
|
+
require 'chefspec/berkshelf'
|
179
151
|
```
|
180
152
|
|
181
|
-
|
182
|
-
|
183
|
-
If you are using Chef Policies with ChefDK, simply require `chefspec/policyfile` in your `spec_helper`, and ensure you are using the `ChefSpec::ServerRunner` - Chef Solo does not support the exported repository format because the cookbook names use the unique version identifier.
|
153
|
+
If you are using a Policyfile, `require 'chefspec/policyfile'` in you spec file (or `spec_helper.rb`):
|
184
154
|
|
185
155
|
```ruby
|
186
|
-
# spec_helper.rb
|
187
156
|
require 'chefspec'
|
188
157
|
require 'chefspec/policyfile'
|
189
158
|
```
|
190
159
|
|
191
|
-
Requiring this file will:
|
192
|
-
|
193
|
-
- Create a temporary working directory
|
194
|
-
- Download all the dependencies listed in your `Policyfile.rb` into the temporary directory
|
195
|
-
- Set ChefSpec's `cookbook_path` to the temporary directory
|
196
|
-
|
197
160
|
Your `Policyfile.rb` should look something like this:
|
198
161
|
|
199
162
|
```ruby
|
200
|
-
name
|
201
|
-
|
202
|
-
|
203
|
-
|
163
|
+
# The policy name is ignored but you need to specify one.
|
164
|
+
name 'my_cookbook'
|
165
|
+
# Pull dependent cookbooks from https://supermarket.chef.io/
|
166
|
+
default_source :supermarket
|
167
|
+
# The run list is also ignored by ChefSpec but you need to specify one.
|
168
|
+
run_list 'my_cookbook::default'
|
169
|
+
# The name here must match the name in metadata.rb.
|
170
|
+
cookbook 'my_cookbook', path: '.'
|
204
171
|
```
|
205
172
|
|
206
|
-
##
|
173
|
+
## Writing Tests
|
207
174
|
|
208
|
-
ChefSpec is
|
209
|
-
|
210
|
-
|
211
|
-
$ rspec
|
212
|
-
```
|
213
|
-
|
214
|
-
You can also specify a specific spec to run and various RSpec command line options:
|
215
|
-
|
216
|
-
```bash
|
217
|
-
$ rspec spec/unit/recipes/default_spec.rb --color
|
218
|
-
```
|
219
|
-
|
220
|
-
For more information on the RSpec CLI, please see the [documentation](https://relishapp.com/rspec/rspec-core/docs/command-line).
|
221
|
-
|
222
|
-
## Making Assertions
|
223
|
-
|
224
|
-
ChefSpec asserts that resource actions have been performed. In general, ChefSpec follows the following pattern:
|
175
|
+
ChefSpec is an RSpec library, so if you're already familiar with RSpec you can
|
176
|
+
use all the normal spec-y goodness to which you are accustomed. The usual structure
|
177
|
+
of an RSpec test file is a file named like `spec/something_spec.rb` containing:
|
225
178
|
|
226
179
|
```ruby
|
227
180
|
require 'chefspec'
|
228
181
|
|
229
|
-
describe '
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
182
|
+
describe 'resource name or recipe' do
|
183
|
+
# Some configuration for everything inside this `describe`.
|
184
|
+
platform 'redhat', '7'
|
185
|
+
default_attributes['value'] = 1
|
186
|
+
|
187
|
+
context 'when some condition' do
|
188
|
+
# Some configuration that only applies to this `context`.
|
189
|
+
default_attributes['value'] = 2
|
190
|
+
|
191
|
+
# `matcher` is some matcher function which we'll cover below.
|
192
|
+
it { expect(value).to matcher }
|
193
|
+
# There is a special value you can expect things on called `subject`, which
|
194
|
+
# is the main thing being tested.
|
195
|
+
it { expect(subject).to matcher }
|
196
|
+
# And if prefer it for readability, `expect(subject)` can be written as `is_expected`.
|
197
|
+
it { is_expected.to matcher }
|
234
198
|
end
|
235
|
-
end
|
236
|
-
```
|
237
|
-
|
238
|
-
where:
|
239
|
-
|
240
|
-
- _ACTION_ - the action on the resource (e.g. `install`)
|
241
|
-
- _RESOURCE_ - the name of the resource (e.g. `package`)
|
242
|
-
- _NAME_ - the name attribute for the resource (e.g. `apache2`)
|
243
|
-
|
244
|
-
**NOTE** One exception to this rule is the `create_if_missing` action on the `file` resource. In this case the assertion is actually `create_file_if_missing`. Refer to `examples/file/spec/create_if_missing_spec.rb` for some examples.
|
245
199
|
|
246
|
-
|
247
|
-
|
248
|
-
```ruby
|
249
|
-
require 'chefspec'
|
250
|
-
|
251
|
-
describe 'example::default' do
|
252
|
-
let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
|
253
|
-
|
254
|
-
it 'installs apache2' do
|
255
|
-
expect(chef_run).to install_package('apache2')
|
256
|
-
end
|
257
|
-
end
|
258
|
-
```
|
259
|
-
|
260
|
-
This test is asserting that the Chef run will have a _package_ resource with the name _apache2_ with an action of _install_.
|
261
|
-
|
262
|
-
To test that a resource action is performed with a specific set of attributes, you can call `with(ATTRIBUTES_HASH)` on the expectation, per the following example:
|
263
|
-
|
264
|
-
```ruby
|
265
|
-
require 'chefspec'
|
266
|
-
|
267
|
-
describe 'example::default' do
|
268
|
-
let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
|
269
|
-
|
270
|
-
it 'adds the member vagrant to the docker group' do
|
271
|
-
expect(chef_run).to modify_group('docker').with(members: ['vagrant'])
|
272
|
-
end
|
273
|
-
end
|
274
|
-
```
|
275
|
-
|
276
|
-
This test is asserting that the Chef run will have a _group_ resource with the name _docker_, an action of _modify_, and an attributes hash including `{ members: ['vagrant'] }`.
|
277
|
-
|
278
|
-
ChefSpec includes matchers for all of Chef's core resources using the above schema. Each resource matcher is self-documented using [Yard](http://rubydoc.info/github/chefspec/chefspec) and has a corresponding cucumber test from the [examples directory](https://github.com/chefspec/chefspec/tree/master/examples).
|
279
|
-
|
280
|
-
Additionally, ChefSpec includes the following helpful matchers. They are also [documented in Yard](http://rubydoc.info/github/chefspec/chefspec), but they are included here because they do not follow the "general pattern".
|
281
|
-
|
282
|
-
### include_recipe
|
283
|
-
|
284
|
-
Assert that the Chef run included a recipe from another cookbook
|
285
|
-
|
286
|
-
```ruby
|
287
|
-
expect(chef_run).to include_recipe('other_cookbook::recipe')
|
288
|
-
```
|
289
|
-
|
290
|
-
Keep the resources from an included recipe from being loaded into the Chef run, but test that the recipe was included
|
291
|
-
|
292
|
-
```ruby
|
293
|
-
describe 'example::default' do
|
294
|
-
let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
|
295
|
-
|
296
|
-
before do
|
297
|
-
allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).and_call_original
|
298
|
-
allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('other_cookbook::default')
|
299
|
-
end
|
300
|
-
|
301
|
-
it 'includes the other_cookbook' do
|
302
|
-
expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('other_cookbook::default')
|
303
|
-
chef_run
|
200
|
+
context 'when some other condition' do
|
201
|
+
# Repeat as needed.
|
304
202
|
end
|
305
203
|
end
|
306
204
|
```
|
307
205
|
|
308
|
-
###
|
309
|
-
|
310
|
-
Assert that a resource notifies another in the Chef run
|
311
|
-
|
312
|
-
```ruby
|
313
|
-
resource = chef_run.template('/etc/foo')
|
314
|
-
expect(resource).to notify('service[apache2]').to(:restart).immediately
|
315
|
-
```
|
316
|
-
|
317
|
-
### subscribes
|
206
|
+
### ChefSpec Matchers
|
318
207
|
|
319
|
-
|
208
|
+
The primary matcher used with ChefSpec are resource matchers:
|
320
209
|
|
321
210
|
```ruby
|
322
|
-
|
323
|
-
|
211
|
+
it { expect(chef_run).to ACTION_RESOURCE('NAME') }
|
212
|
+
# Or equivalently.
|
213
|
+
it { is_expected.to ACTION_RESOURCE('NAME') }
|
324
214
|
```
|
325
215
|
|
326
|
-
|
327
|
-
|
328
|
-
|
216
|
+
This checks that a resource like `RESOURCE 'NAME'` would have run the specified
|
217
|
+
action if the cookbook was executing normally. You can also test for specific
|
218
|
+
property values:
|
329
219
|
|
330
220
|
```ruby
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
# Regular RSpec matches work in here
|
336
|
-
expect(content).to include('any RSpec matcher')
|
337
|
-
}
|
221
|
+
it { is_expected.to create_user('asmithee').with(uid: 512, gid: 45) }
|
222
|
+
# You can also use other RSpec matchers to create a "compound matcher". Check
|
223
|
+
# RSpec documentation for a full reference on the built-in matchers.
|
224
|
+
it { is_expected.to install_package('myapp').with(version: starts_with("3.")) }
|
338
225
|
```
|
339
226
|
|
340
|
-
|
341
|
-
|
342
|
-
```ruby
|
343
|
-
expect(chef_run).to render_file('/etc/foo').with_content(start_with('# First line'))
|
344
|
-
```
|
227
|
+
#### `render_file`
|
345
228
|
|
346
|
-
|
229
|
+
For the common case of testing that a file is rendered to disk via either a
|
230
|
+
`template`, `file`, or `cookbook_file` resource, you can use a `render_file`
|
231
|
+
matcher:
|
347
232
|
|
348
233
|
```ruby
|
349
|
-
|
234
|
+
it { is_expected.to render_file('/etc/myapp.conf') }
|
235
|
+
# You can check for specific content in the file.
|
236
|
+
it { is_expected.to render_file('/etc/myapp.conf').with_content("debug = false\n") }
|
237
|
+
# Or with a regexp.
|
238
|
+
it { is_expected.to render_file('/etc/myapp.conf').with_content(/user = \d+/) }
|
239
|
+
# Or with a compound matcher.
|
240
|
+
it { is_expected.to render_file('/etc/myapp.conf').with_content(start_with('# This file managed by Chef')) }
|
241
|
+
# Or with a Ruby block of arbitrary assertions.
|
242
|
+
it do
|
243
|
+
is_expected.to render_file('/etc/myapp.conf').with_content { |content|
|
244
|
+
# Arbitrary RSpec code goes here.
|
245
|
+
}
|
246
|
+
end
|
350
247
|
```
|
351
248
|
|
352
|
-
|
249
|
+
#### Notifications
|
353
250
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
Similarly, you can assert that a resource is executed during convergence time:
|
251
|
+
As actions do not normally run in ChefSpec, testing for notifications is a special
|
252
|
+
case. Unlike the resource matchers which evaluate against the ChefSpec runner,
|
253
|
+
the notification matchers evaluate against a resource object:
|
359
254
|
|
360
255
|
```ruby
|
361
|
-
|
256
|
+
# To match `notifies :run, 'execute[unpack]', :immediately
|
257
|
+
it { expect(chef_run.remote_file('/download')).to notify('execute[unpack]') }
|
258
|
+
# To check for a specific notification action.
|
259
|
+
it { expect(chef_run.remote_file('/download')).to notify('execute[unpack]').to(:run) }
|
260
|
+
# And to check for a specific timing.
|
261
|
+
it { expect(chef_run.remote_file('/download')).to notify('execute[unpack]').to(:run).immediately }
|
362
262
|
```
|
363
263
|
|
364
|
-
|
365
|
-
|
366
|
-
### do_nothing
|
367
|
-
|
368
|
-
Assert that a resource performs no action
|
264
|
+
And similarly for subscriptions:
|
369
265
|
|
370
266
|
```ruby
|
371
|
-
|
372
|
-
expect(resource).to do_nothing
|
267
|
+
it { expect(chef_run.execute('unpack')).to subscribe_to('remote_file[/download]').on(:create) }
|
373
268
|
```
|
374
269
|
|
375
|
-
|
376
|
-
|
377
|
-
## Specifying Node Information
|
378
|
-
|
379
|
-
Node information can be set when creating the `Runner`. The initializer yields a block that gives full access to the node object:
|
380
|
-
|
381
|
-
```ruby
|
382
|
-
describe 'example::default' do
|
383
|
-
let(:chef_run) do
|
384
|
-
ChefSpec::SoloRunner.new do |node|
|
385
|
-
node.name 'test.node-1'
|
386
|
-
end.converge(described_recipe)
|
387
|
-
end
|
388
|
-
|
389
|
-
it 'has node name set' do
|
390
|
-
expect(chef_run.node.name).to eq('test.node-1')
|
391
|
-
end
|
392
|
-
end
|
393
|
-
```
|
270
|
+
### Test Subject
|
394
271
|
|
395
|
-
|
272
|
+
RSpec expectations always need a value to run against, with the main value being
|
273
|
+
tested for a given example group (`describe` or `context` block) is called the
|
274
|
+
`subject`. In ChefSpec this is almost always `ChefSpec::Runner` that has converge
|
275
|
+
some recipe code.
|
396
276
|
|
397
|
-
|
277
|
+
There are two ways to set which recipe code should be run for the test. More commonly
|
278
|
+
for testing custom resources, you use the `recipe` helper method in the test to
|
279
|
+
provide an in-line block of recipe code:
|
398
280
|
|
399
281
|
```ruby
|
400
|
-
describe '
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
end
|
282
|
+
describe 'something' do
|
283
|
+
recipe do
|
284
|
+
my_custom_resource 'something' do
|
285
|
+
debug true
|
286
|
+
end
|
405
287
|
end
|
406
288
|
end
|
407
289
|
```
|
408
290
|
|
409
|
-
|
291
|
+
By using an in-line block of recipe code, you can try many variations to test
|
292
|
+
different configurations of your custom resource.
|
410
293
|
|
411
|
-
|
294
|
+
If no `recipe` block is present, ChefSpec will use the name of the top-level
|
295
|
+
`describe` block as a recipe name to run. So for the case of testing a recipe
|
296
|
+
in your cookbook, use the `cookbookname::recipename` string as the label:
|
412
297
|
|
413
298
|
```ruby
|
414
|
-
describe '
|
415
|
-
|
416
|
-
|
417
|
-
node.automatic['memory']['total'] = '512kB'
|
418
|
-
end.converge(described_recipe)
|
419
|
-
end
|
420
|
-
end
|
299
|
+
describe 'mycookbook'
|
300
|
+
# Or.
|
301
|
+
describe 'mycookbook::myrecipe'
|
421
302
|
```
|
422
303
|
|
423
|
-
|
304
|
+
### Test Settings
|
424
305
|
|
425
|
-
|
306
|
+
Most ChefSpec configuration is set in your example groups (`describe` or `context`
|
307
|
+
blocks) using helper methods. These all follow the RSpec convention of inheriting
|
308
|
+
from a parent group to the groups inside it. So a setting in your top-level `describe`
|
309
|
+
will automatically be set in any `context` unless overridden:
|
426
310
|
|
427
311
|
```ruby
|
428
|
-
describe '
|
429
|
-
|
312
|
+
describe 'something' do
|
313
|
+
platform 'ubuntu'
|
430
314
|
|
431
|
-
|
432
|
-
|
433
|
-
chef_run.converge(described_recipe) # The converge happens inside the test
|
315
|
+
# Platform is Ubuntu for any tests here.
|
316
|
+
it { is_expected.to ... }
|
434
317
|
|
435
|
-
|
318
|
+
context 'when something' do
|
319
|
+
# Platform is still Ubuntu for any tests here.
|
436
320
|
end
|
437
|
-
end
|
438
|
-
```
|
439
|
-
|
440
|
-
## Using a Chef Server
|
441
321
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
```diff
|
447
|
-
describe 'example::default' do
|
448
|
-
- let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
|
449
|
-
+ let(:chef_run) { ChefSpec::ServerRunner.converge(described_recipe) }
|
322
|
+
context 'when something else' do
|
323
|
+
platform 'fedora'
|
324
|
+
# But platform here will be Fedora.
|
325
|
+
end
|
450
326
|
end
|
451
327
|
```
|
452
328
|
|
453
|
-
|
454
|
-
|
455
|
-
### DSL
|
329
|
+
#### Platform Data
|
456
330
|
|
457
|
-
|
458
|
-
|
459
|
-
|
331
|
+
To support simulating Chef runs on the same OS as you use your cookbooks on, ChefSpec
|
332
|
+
loads pre-fabricated Ohai data from [Fauxhai](https://github.com/chefspec/fauxhai/).
|
333
|
+
To configure which OS' data is set for your test, use the `platform` helper method:
|
460
334
|
|
461
335
|
```ruby
|
462
|
-
|
463
|
-
|
336
|
+
describe 'something' do
|
337
|
+
platform 'ubuntu', '18.04'
|
338
|
+
# ...
|
464
339
|
end
|
465
340
|
```
|
466
341
|
|
467
|
-
|
342
|
+
You can specify a partial version number to get the latest version of that OS
|
343
|
+
matching the provided prefix, or leave the version off entirely to get the latest
|
344
|
+
version overall:
|
468
345
|
|
469
346
|
```ruby
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
},
|
475
|
-
'item_2' => {
|
476
|
-
'password' => 'def456'
|
477
|
-
}
|
478
|
-
})
|
479
|
-
end
|
347
|
+
# Will use the latest RedHat 7.x.
|
348
|
+
platform 'redhat', '7'
|
349
|
+
# Will use the latest version of Windows.
|
350
|
+
platform 'windows'
|
480
351
|
```
|
481
352
|
|
482
|
-
|
353
|
+
**WARNING:** If you leave off the version or use a partial version prefix, the
|
354
|
+
behavior of your tests may change between versions of ChefDK as new data is
|
355
|
+
available in Fauxhai. Only use this feature if you're certain that your tests
|
356
|
+
do not (or should not) depend on the specifics of OS version.
|
483
357
|
|
484
|
-
|
485
|
-
ChefSpec::ServerRunner.new do |node, server|
|
486
|
-
server.create_environment('my_environment', { default_attributes: { description: '...' } })
|
487
|
-
end
|
488
|
-
```
|
358
|
+
#### Node Attributes
|
489
359
|
|
490
|
-
|
360
|
+
Node attributes are set using the `default_attributes`, `normal_attributes`,
|
361
|
+
`override_attributes`, and `automatic_attributes` helper methods. These inherit
|
362
|
+
from a parent group to its children using a deep merge, like in other places in
|
363
|
+
Chef:
|
491
364
|
|
492
365
|
```ruby
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
```
|
497
|
-
|
498
|
-
Note: the current "node" is always uploaded to the server. However, due to the way the Chef Client compiles cookbooks, you must update the current node on the server if any attributes are changed:
|
366
|
+
describe 'something' do
|
367
|
+
default_attributes['myapp']['name'] = 'one'
|
368
|
+
default_attributes['myapp']['email'] = 'myapp@example.com'
|
499
369
|
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
# At this point, the server already has a copy of the current node object due
|
505
|
-
# to the way Chef compiled the resources. However, that node does not have
|
506
|
-
# this new value. As such, you must "save" the node back to the server to
|
507
|
-
# persist this attribute update.
|
508
|
-
server.update_node(node)
|
370
|
+
context 'when something' do
|
371
|
+
default_attributes['myapp']['name'] = 'two'
|
372
|
+
end
|
509
373
|
end
|
510
374
|
```
|
511
375
|
|
512
|
-
|
513
|
-
|
514
|
-
```ruby
|
515
|
-
www = stub_node(platform: 'ubuntu', version: '16.04') do |node|
|
516
|
-
node.normal['attribute'] = 'value'
|
517
|
-
end
|
376
|
+
Any values set using `automatic_attributes` take priority over Fauxhai data.
|
518
377
|
|
519
|
-
|
520
|
-
# this node to the server, call `create_node`:
|
521
|
-
|
522
|
-
ChefSpec::ServerRunner.new do |node, server|
|
523
|
-
server.create_node(www)
|
524
|
-
end
|
525
|
-
```
|
378
|
+
#### Step Into
|
526
379
|
|
527
|
-
|
380
|
+
Normally ChefSpec skips all resource (and provider) actions. When testing the
|
381
|
+
implementation of a custom resource, we need to tell ChefSpec to run actions
|
382
|
+
on our specific custom resource so it can be tested:
|
528
383
|
|
529
384
|
```ruby
|
530
|
-
|
531
|
-
|
385
|
+
describe 'something' do
|
386
|
+
step_into :my_custom_resource
|
532
387
|
end
|
533
388
|
```
|
534
389
|
|
535
|
-
**NOTE** The ChefSpec server is empty at the start of each example to avoid interdependent tests.
|
536
390
|
|
537
|
-
|
391
|
+
#### Other ChefSpec Configuration
|
538
392
|
|
539
|
-
|
540
|
-
|
541
|
-
## Stubbing
|
542
|
-
|
543
|
-
### Command
|
544
|
-
|
545
|
-
Given a recipe with shell guard:
|
393
|
+
You can specify any other ChefSpec configuration options using the `chefspec_options`
|
394
|
+
helper:
|
546
395
|
|
547
396
|
```ruby
|
548
|
-
|
549
|
-
|
397
|
+
describe 'something' do
|
398
|
+
chefspec_options[:log_level] = :debug
|
550
399
|
end
|
551
400
|
```
|
552
401
|
|
553
|
-
|
554
|
-
|
555
|
-
```text
|
556
|
-
Real commands are disabled. Unregistered command: `grep text /tmp/foo.txt`
|
557
|
-
|
558
|
-
You can stub this command with:
|
559
|
-
|
560
|
-
stub_command("grep text /tmp/foo.txt").and_return(true)
|
561
|
-
|
562
|
-
============================================================
|
563
|
-
```
|
564
|
-
|
565
|
-
Just like the error message says, you must stub the command result. This can be done inside a `before` block or inside the `it` block, and the stubbing method accepts both a value or Ruby code. If provided a value, the result is static. If provided a Ruby block, the block is evaluated each time the search is called.
|
566
|
-
|
567
|
-
```ruby
|
568
|
-
describe 'example::default' do
|
569
|
-
let(:chef_run) { ChefSpec::SoloRunner.new }
|
570
|
-
|
571
|
-
before do
|
572
|
-
stub_command("grep text /tmp/foo.txt").and_return(true)
|
573
|
-
end
|
574
|
-
end
|
575
|
-
```
|
402
|
+
### Stubbing
|
576
403
|
|
577
|
-
|
578
|
-
|
579
|
-
|
404
|
+
In order to keep unit tests fast and independent of the target system, we have to
|
405
|
+
make sure that any interaction with the system (either the target node or the Chef
|
406
|
+
Server, both parts of the system just in opposite directions) is replaced with a
|
407
|
+
fake, local version. For some thing, like ensuring that resource actions are
|
408
|
+
replaced with a no-op, the stubbing is automatic. For others, we need to tell ChefSpec
|
409
|
+
how to handle things.
|
580
410
|
|
581
|
-
|
582
|
-
stub_command("grep text /tmp/foo.txt") { rand(50)%2 == 0 }
|
583
|
-
end
|
584
|
-
end
|
585
|
-
```
|
411
|
+
#### Guards
|
586
412
|
|
587
|
-
The
|
413
|
+
The most common case of interacting with the system is a guard clause on a resource:
|
588
414
|
|
589
415
|
```ruby
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
before do
|
594
|
-
stub_command(/(foo)|(bar)/).and_return(true)
|
595
|
-
end
|
596
|
-
end
|
416
|
+
not_if 'some command'
|
417
|
+
# Or.
|
418
|
+
only_if 'some command'
|
597
419
|
```
|
598
420
|
|
599
|
-
|
600
|
-
|
601
|
-
|
421
|
+
In order for ChefSpec to know how to evaluate the resource, we need to tell it
|
422
|
+
how the command would have returned for this test if it was running on the actual
|
423
|
+
machine:
|
602
424
|
|
603
425
|
```ruby
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
def has_bacon?
|
610
|
-
cmd = shell_out!('getent passwd bacon', {:returns => [0,2]})
|
611
|
-
cmd.stderr.empty? && (cmd.stdout =~ /^bacon/)
|
426
|
+
describe 'something' do
|
427
|
+
recipe do
|
428
|
+
execute '/opt/myapp/install.sh' do
|
429
|
+
# Check if myapp is installed and runnable.
|
430
|
+
not_if 'myapp --version'
|
612
431
|
end
|
613
432
|
end
|
614
|
-
end
|
615
|
-
```
|
616
|
-
|
617
|
-
Stub the output of the library helper. [Additional information](http://jtimberman.housepub.org/blog/2015/05/30/quick-tip-stubbing-library-helpers-in-chefspec/)
|
618
|
-
|
619
|
-
```ruby
|
620
|
-
before do
|
621
|
-
allow_any_instance_of(Chef::Node).to receive(:has_bacon?).and_return(true)
|
622
|
-
end
|
623
|
-
```
|
624
|
-
|
625
|
-
### Data Bag & Data Bag Item
|
626
|
-
|
627
|
-
**NOTE** This is not required if you are using a ChefSpec server.
|
628
|
-
|
629
|
-
Given a recipe that executes a `data_bag` method:
|
630
|
-
|
631
|
-
```ruby
|
632
|
-
data_bag('users').each do |user|
|
633
|
-
data_bag_item('users', user['id'])
|
634
|
-
end
|
635
|
-
```
|
636
|
-
|
637
|
-
ChefSpec will raise an error like:
|
638
|
-
|
639
|
-
```text
|
640
|
-
Real data_bags are disabled. Unregistered data_bag: data_bag(:users)
|
641
|
-
|
642
|
-
You can stub this data_bag with:
|
643
|
-
|
644
|
-
stub_data_bag("users").and_return([])
|
645
|
-
|
646
|
-
============================================================
|
647
|
-
```
|
648
|
-
|
649
|
-
Just like the error message says, you must stub the result of the `data_bag` call. This can be done inside a `before` block or inside the `it` block, and the stubbing method accepts both a value or Ruby code. If provided a value, the result is static. If provided a Ruby block, the block is evaluated each time the search is called.
|
650
|
-
|
651
|
-
```ruby
|
652
|
-
describe 'example::default' do
|
653
|
-
let(:chef_run) { ChefSpec::SoloRunner.new }
|
654
|
-
|
655
|
-
before do
|
656
|
-
stub_data_bag('users').and_return([])
|
657
|
-
end
|
658
|
-
end
|
659
|
-
```
|
660
|
-
|
661
|
-
```ruby
|
662
|
-
describe 'example::default' do
|
663
|
-
let(:chef_run) { ChefSpec::SoloRunner.new }
|
664
|
-
|
665
|
-
before do
|
666
|
-
stub_data_bag('users').and_return(['svargo', 'francis'])
|
667
|
-
|
668
|
-
stub_data_bag_item('users', 'svargo').and_return({ ... })
|
669
|
-
stub_data_bag_item('users', 'francis') { (ruby code) }
|
670
|
-
end
|
671
|
-
end
|
672
|
-
```
|
673
433
|
|
674
|
-
If you are using **Encrypted Data Bag Items**, you'll need to dive into the RSpec layer and stub that class method instead:
|
675
|
-
|
676
|
-
```ruby
|
677
|
-
describe 'example::default' do
|
678
434
|
before do
|
679
|
-
|
435
|
+
# Tell ChefSpec the command would have succeeded.
|
436
|
+
stub_command('myapp --version').and_return(true)
|
437
|
+
# Tell ChefSpec the command would have failed.
|
438
|
+
stub_command('myapp --version').and_return(false)
|
439
|
+
# You can also use a regexp to stub multiple commands at once.
|
440
|
+
stub_command(/^myapp/).and_return(false)
|
680
441
|
end
|
681
442
|
end
|
682
443
|
```
|
683
444
|
|
684
|
-
|
685
|
-
|
686
|
-
**NOTE** This is not required if you are using a ChefSpec server.
|
687
|
-
|
688
|
-
Because ChefSpec is a unit-testing framework, it is recommended that all third-party API calls be mocked or stubbed. ChefSpec exposes a helpful RSpec macro for stubbing search results in your tests. If you converge a Chef recipe that implements a `search` call, ChefSpec will throw an error like:
|
689
|
-
|
690
|
-
```text
|
691
|
-
Real searches are disabled. Unregistered search: search(:node, 'name:hello')
|
445
|
+
If using the Ruby code block form of a guard (e.g. `not_if { something }`), see
|
446
|
+
the [Ruby stubbing section](#ruby-code) below.
|
692
447
|
|
693
|
-
|
448
|
+
#### Search
|
694
449
|
|
695
|
-
|
696
|
-
|
697
|
-
============================================================
|
698
|
-
```
|
699
|
-
|
700
|
-
Just like the error message says, you must stub the search result. This can be done inside a `before` block or inside the `it` block, and the stubbing method accepts both a value or Ruby code. If provided a value, the result is static. If provided a Ruby block, the block is evaluated each time the search is called.
|
450
|
+
When testing code that uses the `search()` API in Chef, we have to stub out the
|
451
|
+
results that would normally come from the Chef Server:
|
701
452
|
|
702
453
|
```ruby
|
703
|
-
describe '
|
704
|
-
|
705
|
-
|
706
|
-
before do
|
707
|
-
stub_search(:node, 'name:hello').and_return([])
|
454
|
+
describe 'something' do
|
455
|
+
recipe do
|
456
|
+
web_servers = search(:node, 'roles:web').map { |n| n['hostname'] }
|
708
457
|
end
|
709
|
-
end
|
710
|
-
```
|
711
|
-
|
712
|
-
```ruby
|
713
|
-
describe 'example::default' do
|
714
|
-
let(:chef_run) { ChefSpec::SoloRunner.new }
|
715
458
|
|
716
459
|
before do
|
717
|
-
stub_search(:node, '
|
460
|
+
stub_search(:node, 'roles:web').and_return([{hostname: 'one'}, {hostname: two}])
|
718
461
|
end
|
719
462
|
end
|
720
463
|
```
|
721
464
|
|
722
|
-
|
465
|
+
#### Data Bags
|
723
466
|
|
724
|
-
|
467
|
+
Similar to the Search API, the `data_bag()` and `data_bag_item()` APIs normally
|
468
|
+
fetch data from Chef Server so we need to stub their results:
|
725
469
|
|
726
470
|
```ruby
|
727
|
-
describe '
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
end
|
734
|
-
|
735
|
-
it "tests something when /etc/myfile.txt does not exist" do
|
736
|
-
# only setup an expectation on our file
|
737
|
-
expect(File).to receive(:exist?).with("/etc/myfile.txt").and_return(false)
|
738
|
-
[ ... test that the chef resource collection is constructed correctly in this case ... ]
|
739
|
-
end
|
740
|
-
end
|
741
|
-
```
|
742
|
-
|
743
|
-
This is basic usage of rspec and not specific to chefspec. It applies to any class method in `File`, `Dir`, `FileUtils`, `IO` or any other ruby library. In general any time you `expect(Some::Symbol).to receive(:a_method).and_return(value)` you run the risk of breaking other code unless you isolate your mocking or expectation down to only the arguments which your code uses.
|
744
|
-
|
745
|
-
## Reporting
|
746
|
-
|
747
|
-
**NOTE: The coverage reporting feature is deprecated and will be removed in a future version. This documentation exists only for reference purposes.**
|
748
|
-
|
749
|
-
ChefSpec can generate a report of resources read over resources tested.
|
750
|
-
|
751
|
-
To generate the coverage report, add the following to your `spec_helper.rb` before you require any "Chef" code:
|
752
|
-
|
753
|
-
```ruby
|
754
|
-
require 'chefspec'
|
755
|
-
ChefSpec::Coverage.start!
|
756
|
-
|
757
|
-
# Existing spec_helper contents...
|
758
|
-
```
|
759
|
-
|
760
|
-
By default, that method will output helpful information to standard out:
|
761
|
-
|
762
|
-
```text
|
763
|
-
ChefSpec Coverage report generated...
|
764
|
-
|
765
|
-
Total Resources: 6
|
766
|
-
Touched Resources: 1
|
767
|
-
Touch Coverage: 16.67%
|
768
|
-
|
769
|
-
Untouched Resources:
|
770
|
-
|
771
|
-
package[git] bacon/recipes/default.rb:2
|
772
|
-
package[build-essential] bacon/recipes/default.rb:3
|
773
|
-
package[apache2] bacon/recipes/default.rb:4
|
774
|
-
package[libvirt] bacon/recipes/default.rb:5
|
775
|
-
package[core] bacon/recipes/default.rb:6
|
776
|
-
```
|
777
|
-
|
778
|
-
By default, ChefSpec will test all cookbooks that are loaded as part of the Chef Client run. If you have a cookbook with many dependencies, this may be less than desirable. To restrict coverage reporting against certain cookbooks, `ChefSpec::Coverage` yields a block:
|
779
|
-
|
780
|
-
```ruby
|
781
|
-
ChefSpec::Coverage.start! do
|
782
|
-
add_filter 'vendor/cookbooks'
|
783
|
-
end
|
784
|
-
```
|
785
|
-
|
786
|
-
The `add_filter` method accepts a variety of objects. For example:
|
787
|
-
|
788
|
-
```ruby
|
789
|
-
ChefSpec::Coverage.start! do
|
790
|
-
# Strings are interpreted as file paths, with a forward anchor
|
791
|
-
add_filter 'vendor/cookbooks'
|
792
|
-
|
793
|
-
# Regular expressions must be escaped, but provide a nicer API for negative
|
794
|
-
# back tracking
|
795
|
-
add_filter /cookbooks\/(?!omnibus)/
|
796
|
-
|
797
|
-
# Custom block filters yield a {Chef::Resource} object - if the block
|
798
|
-
# evaluates to true, it will be filtered
|
799
|
-
add_filter do |resource|
|
800
|
-
# Bob's cookbook's are completely untested! Ignore them until he gets his
|
801
|
-
# shit together.
|
802
|
-
resource.source_file =~ /cookbooks\/bob-(.+)/
|
803
|
-
end
|
804
|
-
end
|
805
|
-
```
|
806
|
-
|
807
|
-
For more complex scenarios, you can create a custom `Filter` object that inherits from `ChefSpec::Coverage::Filter` and implements the `matches?` method.
|
808
|
-
|
809
|
-
```ruby
|
810
|
-
class CustomFilter < ChefSpec::Coverage::Filter
|
811
|
-
def initialize(arg1, arg2, █)
|
812
|
-
# Create a custom initialization method, do some magic, etc.
|
471
|
+
describe 'something' do
|
472
|
+
recipe do
|
473
|
+
# Side note: don't write recipe code like this. This should be `search(:users, '*:*')`.
|
474
|
+
users = data_bag('users').map do |user|
|
475
|
+
data_bag_item('users', user['id'])
|
476
|
+
end
|
813
477
|
end
|
814
478
|
|
815
|
-
|
816
|
-
|
817
|
-
|
479
|
+
before do
|
480
|
+
stub_data_bag('users').and_return(['asmithee'])
|
481
|
+
stub_data_bag_item('users', 'asmithee').and_return({uid: 1234})
|
818
482
|
end
|
819
483
|
end
|
820
|
-
|
821
|
-
ChefSpec::Coverage.start! do
|
822
|
-
add_filter CustomFilter.new('foo', :bar)
|
823
|
-
end
|
824
|
-
```
|
825
|
-
|
826
|
-
If you are using ChefSpec's Berkshelf plugin, a filter is automatically created for you. If you would like to ignore that filter, you can `clear` all the filters before defining your own:
|
827
|
-
|
828
|
-
```ruby
|
829
|
-
ChefSpec::Coverage.start! do
|
830
|
-
filters.clear
|
831
|
-
|
832
|
-
# Add your custom filters now
|
833
|
-
end
|
834
|
-
```
|
835
|
-
|
836
|
-
If you would like a different output format for the Coverage.report! output, you can specify one of the three built-in templates, or supply your own by calling the set_template in the `ChefSpec::Coverage` block:
|
837
|
-
|
838
|
-
```ruby
|
839
|
-
ChefSpec::Coverage.start! do
|
840
|
-
set_template 'json.erb'
|
841
|
-
end
|
842
484
|
```
|
843
485
|
|
844
|
-
|
845
|
-
|
846
|
-
```ruby
|
847
|
-
ChefSpec::Coverage.start! do
|
848
|
-
set_template '/opt/custom/templates/verbose.erb'
|
849
|
-
end
|
850
|
-
```
|
486
|
+
#### Resource and Provider Methods
|
851
487
|
|
852
|
-
|
488
|
+
When testing custom resources, it is often useful to stub methods on the resource
|
489
|
+
or provider instance. These can be set up using the `stubs_for_resource` and
|
490
|
+
`stubs_for_provider` helpers:
|
853
491
|
|
854
492
|
```ruby
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
f.puts(reportOutput[:total])
|
859
|
-
f.puts(reportOutput[:touched])
|
860
|
-
f.puts(reportOutput[:coverage])
|
861
|
-
f.puts(reportOutput[:untouched_resources])
|
862
|
-
f.puts(reportOutput[:all_resources])
|
863
|
-
end
|
493
|
+
describe 'something' do
|
494
|
+
recipe do
|
495
|
+
my_custom_resource 'something'
|
864
496
|
end
|
865
|
-
end
|
866
|
-
```
|
867
|
-
|
868
|
-
Note the above example outputs the raw data without applying formatting.
|
869
|
-
|
870
|
-
## Mocking Out Environments
|
871
|
-
|
872
|
-
### ServerRunner
|
873
497
|
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
# Assign the environment to the node
|
880
|
-
node.chef_environment = 'staging'
|
881
|
-
end
|
882
|
-
```
|
883
|
-
|
884
|
-
### SoloRunner
|
885
|
-
|
886
|
-
If you want to mock out `node.chef_environment`, you'll need to use RSpec mocks/stubs twice:
|
887
|
-
|
888
|
-
```ruby
|
889
|
-
let(:chef_run) do
|
890
|
-
ChefSpec::SoloRunner.new do |node|
|
891
|
-
# Create a new environment (you could also use a different :let block or :before block)
|
892
|
-
env = Chef::Environment.new
|
893
|
-
env.name 'staging'
|
894
|
-
|
895
|
-
# Stub the node to return this environment
|
896
|
-
allow(node).to receive(:chef_environment).and_return(env.name)
|
897
|
-
|
898
|
-
# Stub any calls to Environment.load to return this environment
|
899
|
-
allow(Chef::Environment).to receive(:load).and_return(env)
|
900
|
-
end.converge('cookbook::recipe')
|
901
|
-
end
|
902
|
-
```
|
903
|
-
|
904
|
-
**There is probably a better/easier way to do this. If you have a better solution, please open an issue or Pull Request so we can make this less painful :)**
|
905
|
-
|
906
|
-
## Testing LWRPs
|
907
|
-
|
908
|
-
**WARNING** Cookbooks with dashes (hyphens) are difficult to test with ChefSpec because of how Chef classifies objects. We recommend naming cookbooks with underscores (`_`) instead of dashes (`-`).
|
909
|
-
|
910
|
-
ChefSpec overrides all providers to take no action (otherwise it would actually converge your system). This means that the steps inside your LWRP are not actually executed. If an LWRP performs actions, those actions are never executed or added to the resource collection.
|
911
|
-
|
912
|
-
In order to run the actions exposed by your LWRP, you have to explicitly tell the `Runner` to step into it:
|
913
|
-
|
914
|
-
```ruby
|
915
|
-
require 'chefspec'
|
916
|
-
|
917
|
-
describe 'foo::default' do
|
918
|
-
let(:chef_run) do
|
919
|
-
ChefSpec::SoloRunner.new(step_into: ['my_lwrp']).converge('foo::default')
|
498
|
+
# Set up stubs for just the one resource.
|
499
|
+
stubs_for_resource('my_custom_resource[something]') do |res|
|
500
|
+
# Can use any RSpec Mocks code here, see below.
|
501
|
+
allow(res).to receive(:something)
|
920
502
|
end
|
921
|
-
|
922
|
-
|
923
|
-
|
503
|
+
# Stubs for any instance of my_custom_resource.
|
504
|
+
stubs_for_resource('my_custom_resource') do |res|
|
505
|
+
# ...
|
924
506
|
end
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
**NOTE:** If your cookbook exposes LWRPs, it is highly recommended you also create a `libraries/matchers.rb` file as outlined below in the "Packaging Custom Matchers" section. **You should never `step_into` an LWRP unless you are testing it. Never `step_into` an LWRP from another cookbook!**
|
929
|
-
|
930
|
-
## Automatic Matchers
|
931
|
-
|
932
|
-
As of ChefSpec 7.1.0 there are "custom" matchers generated for all internal core-chef resources, along with any LWRPs/HWRPs/Custom Resources that are user-defined in cookbooks.
|
933
|
-
|
934
|
-
The matchers follow the standard custom of `<action>_<resource_name>` with the exception of the `create_if_missing` action which _also_ gets a `create_<resource_name>_if_missing` matcher.
|
935
|
-
|
936
|
-
Matchers should be wired up for the `resource_name` of the resource along with all define `provides` lines synonyms and any `action` methods or `allowed_actions`.
|
937
|
-
|
938
|
-
There should be little reason to package custom matchers in cookbooks any more, but the approach below still works if there are special matchers which cookbooks wish to expose which do not follow the automatically generated pattern.
|
939
|
-
|
940
|
-
## Packaging Custom Matchers
|
941
|
-
|
942
|
-
ChefSpec exposes the ability for cookbook authors to package custom matchers inside a cookbook so that other developers may take advantage of them in testing. This is done by creating a special library file in the cookbook named `matchers.rb`:
|
943
|
-
|
944
|
-
```ruby
|
945
|
-
# cookbook/libraries/matchers.rb
|
946
|
-
|
947
|
-
if defined?(ChefSpec)
|
948
|
-
def my_custom_matcher(resource_name)
|
949
|
-
ChefSpec::Matchers::ResourceMatcher.new(resource, action, resource_name)
|
507
|
+
# Stubs for any resource.
|
508
|
+
stubs_for_resource do |res|
|
509
|
+
# ...
|
950
510
|
end
|
951
|
-
end
|
952
|
-
```
|
953
|
-
|
954
|
-
1. The entire contents of this file must be wrapped with the conditional clause checking if `ChefSpec` is defined.
|
955
|
-
2. Each matcher is actually a top-level method. The above example corresponds to the following RSpec test:
|
956
|
-
|
957
|
-
```ruby
|
958
|
-
expect(chef_run).to my_custom_matcher('...')
|
959
|
-
```
|
960
|
-
|
961
|
-
3. `ChefSpec::Matchers::ResourceMatcher` accepts three parameters:
|
962
|
-
|
963
|
-
1. The name of the resource to find in the resource collection (i.e. the name of the LWRP).
|
964
|
-
2. The action that resource should receive.
|
965
|
-
3. The value of the name attribute of the resource to find. (This is typically proxied as the value from the matcher definition.)
|
966
|
-
|
967
|
-
ChefSpec's built-in `ResourceMatcher` _should_ satisfy most common use cases for packaging a custom matcher with your LWRPs. However, if your cookbook is extending Chef core or is outside of the scope of a traditional "resource", you may need to create a custom matcher. For more information on custom matchers in RSpec, please [watch the Railscast on Custom Matchers](http://railscasts.com/episodes/157-rspec-matchers-macros) or look at some of the other custom matchers in ChefSpec's source code.
|
968
|
-
|
969
|
-
### Example
|
970
|
-
|
971
|
-
Suppose I have a cookbook named "motd" with a resource/provider "message".
|
972
|
-
|
973
|
-
```ruby
|
974
|
-
# motd/resources/message.rb
|
975
|
-
actions :write
|
976
|
-
default_action :write
|
977
|
-
|
978
|
-
attribute :message, name_attribute: true
|
979
|
-
```
|
980
|
-
|
981
|
-
```ruby
|
982
|
-
# motd/providers/message.rb
|
983
|
-
action :write do
|
984
|
-
# ...
|
985
|
-
end
|
986
|
-
```
|
987
|
-
|
988
|
-
Chef will dynamically build the `motd_message` LWRP at runtime that can be used in the recipe DSL:
|
989
|
-
|
990
|
-
```ruby
|
991
|
-
motd_message 'my message'
|
992
|
-
```
|
993
511
|
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
if defined?(ChefSpec)
|
999
|
-
def write_motd_message(message)
|
1000
|
-
ChefSpec::Matchers::ResourceMatcher.new(:motd_message, :write, message)
|
1001
|
-
end
|
1002
|
-
end
|
1003
|
-
```
|
1004
|
-
|
1005
|
-
Other developers can write RSpec tests against your LWRP in their cookbooks:
|
1006
|
-
|
1007
|
-
```ruby
|
1008
|
-
expect(chef_run).to write_motd_message('my message')
|
1009
|
-
```
|
1010
|
-
|
1011
|
-
**Don't forget to include documentation in your cookbook's README noting the custom matcher and its API!**
|
1012
|
-
|
1013
|
-
As a caveat, if your custom LWRP uses a custom `provides` value as shown below (Chef 12+), you will need to package slightly different custom matchers:
|
1014
|
-
|
1015
|
-
```ruby
|
1016
|
-
# motd/resources/message.rb
|
1017
|
-
actions :write
|
1018
|
-
default_action :write
|
1019
|
-
|
1020
|
-
provides :foobar
|
1021
|
-
|
1022
|
-
attribute :message, name_attribute: true
|
1023
|
-
```
|
1024
|
-
|
1025
|
-
With a custom `provides` declaration, the resource is still inserted into the resource collection with its generic name; `provides` is just sugar for use in the recipe. As such, you will also need to introduce sugar into your custom matchers:
|
1026
|
-
|
1027
|
-
```ruby
|
1028
|
-
# motd/libraries/matchers.rb
|
1029
|
-
if defined?(ChefSpec)
|
1030
|
-
def write_foobar(message)
|
1031
|
-
ChefSpec::Matchers::ResourceMatcher.new(:motd_message, :write, message)
|
512
|
+
# Stubs for the provider for just the one resource.
|
513
|
+
stubs_for_provider('my_custom_resource[something]') do |res|
|
514
|
+
# Can use any RSpec Mocks code here, see below.
|
515
|
+
allow(res).to receive(:something)
|
1032
516
|
end
|
517
|
+
# And similar to the above for any provider of a type or any overall.
|
1033
518
|
end
|
1034
519
|
```
|
1035
520
|
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
If you are testing a cookbook that does not package its LWRP matchers, you can create your own following the same pattern as the "Packaging Custom Matchers" section. Simply, create a file at `spec/support/matchers.rb` and add your resource matchers:
|
1041
|
-
|
1042
|
-
```ruby
|
1043
|
-
# spec/support/matchers.rb
|
1044
|
-
def my_custom_matcher(resource_name)
|
1045
|
-
ChefSpec::Matchers::ResourceMatcher.new(:resource, :action, resource_name)
|
1046
|
-
end
|
1047
|
-
```
|
1048
|
-
|
1049
|
-
Then require this file in your `spec_helper.rb` so the matcher can be used:
|
1050
|
-
|
1051
|
-
```ruby
|
1052
|
-
require_relative 'support/matchers'
|
1053
|
-
```
|
1054
|
-
|
1055
|
-
Please use this as a _temporary_ solution. Consider sending a Pull Request to the LWRP author(s) packaging the custom resource matchers (see previous section).
|
1056
|
-
|
1057
|
-
## Matchers for looking up custom resources
|
1058
|
-
|
1059
|
-
ChefSpec also provides a helper method to define a method on the Chef runner for locating a resource in the collection. This is helpful while asserting against custom resource notifications.
|
1060
|
-
|
1061
|
-
```ruby
|
1062
|
-
# matchers.rb
|
1063
|
-
ChefSpec.define_matcher :my_custom_resource
|
1064
|
-
```
|
1065
|
-
|
1066
|
-
And then in your spec suite, you can obtain the custom resource for assertions:
|
1067
|
-
|
1068
|
-
```ruby
|
1069
|
-
let(:chef_run) { ChefSpec::SoloRunner.converge('...') }
|
1070
|
-
|
1071
|
-
it 'notifies the thing' do
|
1072
|
-
custom = chef_run.my_custom_resource('name')
|
1073
|
-
expect(custom).to notify('service[apache2]').to(:restart).immediately
|
1074
|
-
end
|
1075
|
-
```
|
1076
|
-
|
1077
|
-
You can use this functionality to bundle lookup matchers with cookbooks, or to provide your own when the upstream cookbook doesn't include it.
|
1078
|
-
|
1079
|
-
## Expecting Exceptions
|
1080
|
-
|
1081
|
-
In Chef 11, custom formatters were introduced and ChefSpec uses a custom formatter to suppress Chef Client output. In the event of a convergence failure, ChefSpec will output the error message from the run to help you debug:
|
1082
|
-
|
1083
|
-
```text
|
1084
|
-
================================================================================
|
1085
|
-
Recipe Compile Error in apt_package/recipes/install.rb
|
1086
|
-
================================================================================
|
1087
|
-
|
1088
|
-
RuntimeError
|
1089
|
-
------------
|
1090
|
-
RuntimeError
|
1091
|
-
|
1092
|
-
Cookbook Trace:
|
1093
|
-
---------------
|
1094
|
-
.../apt_package/recipes/install.rb:1:in `from_file'
|
1095
|
-
.../apt_package/spec/install_spec.rb:4:in `block (2 levels) in <top (required)>'
|
1096
|
-
.../apt_package/spec/install_spec.rb:7:in `block (2 levels) in <top (required)>'
|
1097
|
-
|
1098
|
-
Relevant File Content:
|
1099
|
-
----------------------
|
1100
|
-
.../apt_package/recipes/install.rb:
|
1101
|
-
|
1102
|
-
1>> raise RuntimeError
|
1103
|
-
2:
|
1104
|
-
3: apt_package 'default_action'
|
1105
|
-
```
|
1106
|
-
|
1107
|
-
This output is automatically silenced when using RSpec's `raise_error` matcher:
|
1108
|
-
|
1109
|
-
```ruby
|
1110
|
-
let(:chef_run) { ChefSpec::SoloRunner.converge('cookbook::recipe') }
|
1111
|
-
|
1112
|
-
it 'raises an error' do
|
1113
|
-
expect {
|
1114
|
-
chef_run
|
1115
|
-
}.to raise_error
|
1116
|
-
end
|
1117
|
-
```
|
1118
|
-
|
1119
|
-
You can also assert that a particular error was raised. If the error matches the given type, the output is suppressed. If not, the test fails and the entire stack trace is presented.
|
1120
|
-
|
1121
|
-
```ruby
|
1122
|
-
let(:chef_run) { ChefSpec::SoloRunner.converge('cookbook::recipe') }
|
1123
|
-
|
1124
|
-
it 'raises an error' do
|
1125
|
-
expect {
|
1126
|
-
chef_run
|
1127
|
-
}.to raise_error(RuntimeError)
|
1128
|
-
end
|
1129
|
-
```
|
1130
|
-
|
1131
|
-
## Testing Multiple Recipes
|
1132
|
-
|
1133
|
-
Even though ChefSpec is cookbook-centric, you can still converge multiple recipes in a single `ChefSpec::SoloRunner` instance. Given a cookbook "sandwich" with recipes "bacon", "lettuce" and "tomato":
|
1134
|
-
|
1135
|
-
```ruby
|
1136
|
-
# cookbooks/sandwich/recipes/bacon.rb
|
1137
|
-
package 'bacon'
|
1138
|
-
|
1139
|
-
# cookbooks/sandwich/recipes/lettuce.rb
|
1140
|
-
package 'lettuce'
|
1141
|
-
|
1142
|
-
# cookbooks/sandwich/recipes/tomato.rb
|
1143
|
-
package 'tomato'
|
1144
|
-
```
|
1145
|
-
|
1146
|
-
```ruby
|
1147
|
-
let(:chef_run) { ChefSpec::SoloRunner.converge('sandwich::bacon', 'sandwich::lettuce', 'sandwich::tomato') }
|
1148
|
-
```
|
1149
|
-
|
1150
|
-
```ruby
|
1151
|
-
expect(chef_run).to install_package('bacon')
|
1152
|
-
expect(chef_run).to install_package('lettuce')
|
1153
|
-
expect(chef_run).to install_package('tomato')
|
1154
|
-
```
|
1155
|
-
|
1156
|
-
## Testing Roles
|
1157
|
-
|
1158
|
-
Roles can also be used in a single `ChefSpec::SoloRunner` instance. Given a cookbook "bacon" with a default recipe:
|
1159
|
-
|
1160
|
-
```ruby
|
1161
|
-
# cookbooks/bacon/recipes/default.rb
|
1162
|
-
package 'foo'
|
1163
|
-
```
|
1164
|
-
|
1165
|
-
and a default attributes file:
|
1166
|
-
|
1167
|
-
```ruby
|
1168
|
-
# cookbooks/bacon/attributes/default.rb
|
1169
|
-
default['bacon']['temperature'] = 200
|
1170
|
-
```
|
1171
|
-
|
1172
|
-
and a role "breakfast":
|
1173
|
-
|
1174
|
-
```ruby
|
1175
|
-
# roles/breakfast.rb
|
1176
|
-
default_attributes(
|
1177
|
-
'bacon' => {
|
1178
|
-
'temperature' => 150 # NOTE: This is different from the default value
|
1179
|
-
}
|
1180
|
-
)
|
1181
|
-
run_list([
|
1182
|
-
'recipe[bacon::default]'
|
1183
|
-
])
|
1184
|
-
```
|
1185
|
-
|
1186
|
-
You can test that the role is appropriately applied by telling the `ChefSpec::SoloRunner` to converge on the _role_ instead of a recipe:
|
1187
|
-
|
1188
|
-
```ruby
|
1189
|
-
let(:chef_run) { ChefSpec::SoloRunner.converge('role[breakfast]') }
|
1190
|
-
```
|
1191
|
-
|
1192
|
-
Assert that the run_list is properly expanded:
|
1193
|
-
|
1194
|
-
```ruby
|
1195
|
-
expect(chef_run).to include_recipe('bacon::default')
|
1196
|
-
```
|
1197
|
-
|
1198
|
-
Assert that the correct attribute is used:
|
521
|
+
By default, stubs for the resource will also be set up on the `current_resource`
|
522
|
+
object. This can be disabled by using `stubs_for_resource('my_custom_resource[something]', current_resource: false)`.
|
523
|
+
You can also manually set stubs for only the `current_resource` using `stubs_for_current_resource`.
|
1199
524
|
|
1200
|
-
|
1201
|
-
expect(chef_run.node['bacon']['temperature']).to eq(150)
|
1202
|
-
```
|
525
|
+
#### Ruby Code
|
1203
526
|
|
1204
|
-
|
527
|
+
For more complex Ruby code, in recipes, libraries, or custom resources, you have
|
528
|
+
the full power of RSpec and RSpec Mocks available to you:
|
1205
529
|
|
1206
530
|
```ruby
|
1207
|
-
|
1208
|
-
|
531
|
+
before do
|
532
|
+
allow(File).to receive(:exist?).and_call_original
|
533
|
+
allow(File).to receive(:exist?).with('/test/path').and_return(true)
|
1209
534
|
end
|
1210
|
-
|
1211
|
-
# - OR -
|
1212
|
-
|
1213
|
-
ChefSpec::SoloRunner.new(role_path: '/var/my/roles') # local setting
|
1214
|
-
```
|
1215
|
-
|
1216
|
-
## Faster Specs
|
1217
|
-
|
1218
|
-
ChefSpec aims to provide the easiest and simplest path for new users to write RSpec examples for Chef cookbooks. In doing so, it makes some sacrifices in terms of speed and agility of execution. In other words, ChefSpec favors "speed to develop" over "speed to execute". Many of these decisions are directly related to the way Chef dynamically loads resources at runtime.
|
1219
|
-
|
1220
|
-
If you understand how RSpec works and would like to see some significant speed improvements in your specs, you can use the `ChefSpec::Cacher` module inspired by [Juri Timošin](https://github.com/DracoAter). Simply convert all your `let` blocks to `cached`:
|
1221
|
-
|
1222
|
-
```ruby
|
1223
|
-
# before
|
1224
|
-
let(:chef_run) { ChefSpec::SoloRunner.new }
|
1225
|
-
|
1226
|
-
# after
|
1227
|
-
cached(:chef_run) { ChefSpec::SoloRunner.new }
|
1228
535
|
```
|
1229
536
|
|
1230
|
-
|
1231
|
-
|
1232
|
-
## Media & Third-party Tutorials
|
1233
|
-
|
1234
|
-
- [CustomInk's Testing Chef Cookbooks](http://technology.customink.com/blog/2012/08/03/testing-chef-cookbooks/)
|
1235
|
-
- [Jake Vanderdray's Practical ChefSpec](http://files.meetup.com/1780846/ChefSpec.pdf)
|
1236
|
-
- [Jim Hopp's excellent Test Driven Development for Chef Practitioners](http://www.youtube.com/watch?v=o2e0aZUAVGw)
|
1237
|
-
- [Joshua Timberman's Starting ChefSpec Examples](http://jtimberman.housepub.org/blog/2013/05/09/starting-chefspec-example/)
|
1238
|
-
- [Juri Timošin's post on faster specs](http://dracoater.blogspot.com/2013/12/testing-chef-cookbooks-part-25-speeding.html)
|
1239
|
-
- [Seth Vargo's Chef recipe code coverage](https://sethvargo.com/chef-recipe-code-coverage/)
|
1240
|
-
- [Seth Vargo's TDDing tmux talk](http://www.confreaks.com/videos/2364-mwrc2013-tdding-tmux)
|
1241
|
-
- [Stephen Nelson Smith's Test-Driven Infrastructure with Chef](http://shop.oreilly.com/product/0636920030973.do)
|
537
|
+
Check out the [RSpec Mocks documentation](https://relishapp.com/rspec/rspec-mocks/docs)
|
538
|
+
for more information about setting up Ruby method stubs.
|
1242
539
|
|
1243
540
|
## Development
|
1244
541
|
|