[DRE-commits] [ruby-chefspec] 01/05: Imported Upstream version 4.3.0

Hleb Valoshka tsfgnu-guest at moszumanska.debian.org
Sat Aug 15 07:22:15 UTC 2015


This is an automated email from the git hooks/post-receive script.

tsfgnu-guest pushed a commit to branch master
in repository ruby-chefspec.

commit 106789774fe9e91482f0dc1ad7c9496c61ef99f6
Author: Hleb Valoshka <375gnu at gmail.com>
Date:   Tue Aug 4 11:53:00 2015 +0300

    Imported Upstream version 4.3.0
---
 .gitignore                                         |   15 +
 .travis.yml                                        |   36 +
 CHANGELOG.md                                       |  582 +++++++++++
 CONTRIBUTING.md                                    |   52 +
 Gemfile                                            |    2 +
 ISSUES.md                                          |   41 +
 LICENSE                                            |   22 +
 README.md                                          | 1038 ++++++++++++++++++++
 Rakefile                                           |   29 +
 chefspec.gemspec                                   |   40 +
 examples/apt_package/recipes/install.rb            |   13 +
 examples/apt_package/recipes/purge.rb              |   13 +
 examples/apt_package/recipes/reconfig.rb           |   13 +
 examples/apt_package/recipes/remove.rb             |   13 +
 examples/apt_package/recipes/upgrade.rb            |   13 +
 examples/apt_package/spec/install_spec.rb          |   23 +
 examples/apt_package/spec/purge_spec.rb            |   19 +
 examples/apt_package/spec/reconfig_spec.rb         |   19 +
 examples/apt_package/spec/remove_spec.rb           |   19 +
 examples/apt_package/spec/upgrade_spec.rb          |   19 +
 examples/attributes/attributes/default.rb          |    1 +
 examples/attributes/recipes/default.rb             |    4 +
 examples/attributes/spec/default_spec.rb           |   20 +
 examples/batch/recipes/run.rb                      |   13 +
 examples/batch/spec/run_spec.rb                    |   23 +
 examples/cached/recipes/default.rb                 |    1 +
 examples/cached/spec/default_spec.rb               |   13 +
 examples/chef_gem/recipes/install.rb               |   18 +
 examples/chef_gem/recipes/purge.rb                 |   16 +
 examples/chef_gem/recipes/reconfig.rb              |   16 +
 examples/chef_gem/recipes/remove.rb                |   16 +
 examples/chef_gem/recipes/upgrade.rb               |   16 +
 examples/chef_gem/spec/install_spec.rb             |   23 +
 examples/chef_gem/spec/purge_spec.rb               |   19 +
 examples/chef_gem/spec/reconfig_spec.rb            |   19 +
 examples/chef_gem/spec/remove_spec.rb              |   19 +
 examples/chef_gem/spec/upgrade_spec.rb             |   19 +
 examples/compile_time/recipes/default.rb           |    3 +
 examples/compile_time/spec/default_spec.rb         |   27 +
 examples/cookbook_file/recipes/create.rb           |   15 +
 .../cookbook_file/recipes/create_if_missing.rb     |   15 +
 examples/cookbook_file/recipes/delete.rb           |   15 +
 examples/cookbook_file/recipes/touch.rb            |   15 +
 .../cookbook_file/spec/create_if_missing_spec.rb   |   28 +
 examples/cookbook_file/spec/create_spec.rb         |   32 +
 examples/cookbook_file/spec/delete_spec.rb         |   19 +
 examples/cookbook_file/spec/touch_spec.rb          |   19 +
 examples/cron/recipes/create.rb                    |   10 +
 examples/cron/recipes/delete.rb                    |    9 +
 examples/cron/spec/create_spec.rb                  |   19 +
 examples/cron/spec/delete_spec.rb                  |   15 +
 examples/custom_matcher/libraries/matcher.rb       |   23 +
 examples/custom_matcher/providers/thing.rb         |    2 +
 examples/custom_matcher/recipes/install.rb         |   13 +
 examples/custom_matcher/recipes/remove.rb          |   13 +
 examples/custom_matcher/resources/thing.rb         |    5 +
 examples/custom_matcher/spec/install_spec.rb       |   27 +
 examples/custom_matcher/spec/remove_spec.rb        |   23 +
 examples/deploy/recipes/deploy.rb                  |   10 +
 examples/deploy/recipes/force_deploy.rb            |    9 +
 examples/deploy/recipes/rollback.rb                |    9 +
 examples/deploy/spec/deploy_spec.rb                |   15 +
 examples/deploy/spec/force_deploy_spec.rb          |   15 +
 examples/deploy/spec/rollback_spec.rb              |   15 +
 examples/directory/recipes/create.rb               |   18 +
 examples/directory/recipes/delete.rb               |   14 +
 examples/directory/spec/create_spec.rb             |   34 +
 examples/directory/spec/delete_spec.rb             |   26 +
 examples/do_nothing/recipes/default.rb             |    3 +
 examples/do_nothing/spec/default_spec.rb           |   15 +
 examples/dpkg_package/recipes/install.rb           |   13 +
 examples/dpkg_package/recipes/purge.rb             |   13 +
 examples/dpkg_package/recipes/remove.rb            |   13 +
 examples/dpkg_package/spec/install_spec.rb         |   23 +
 examples/dpkg_package/spec/purge_spec.rb           |   19 +
 examples/dpkg_package/spec/remove_spec.rb          |   19 +
 examples/easy_install_package/recipes/install.rb   |   13 +
 examples/easy_install_package/recipes/purge.rb     |   13 +
 examples/easy_install_package/recipes/remove.rb    |   13 +
 examples/easy_install_package/recipes/upgrade.rb   |   13 +
 examples/easy_install_package/spec/install_spec.rb |   23 +
 examples/easy_install_package/spec/purge_spec.rb   |   19 +
 examples/easy_install_package/spec/remove_spec.rb  |   19 +
 examples/easy_install_package/spec/upgrade_spec.rb |   19 +
 examples/env/recipes/create.rb                     |   13 +
 examples/env/recipes/delete.rb                     |   13 +
 examples/env/recipes/modify.rb                     |   13 +
 examples/env/spec/create_spec.rb                   |   23 +
 examples/env/spec/delete_spec.rb                   |   19 +
 examples/env/spec/modify_spec.rb                   |   19 +
 examples/erl_call/recipes/run.rb                   |   13 +
 examples/erl_call/spec/run_spec.rb                 |   23 +
 examples/execute/recipes/run.rb                    |   13 +
 examples/execute/spec/run_spec.rb                  |   23 +
 examples/expect_exception/recipes/compile_error.rb |    1 +
 .../expect_exception/recipes/converge_error.rb     |    5 +
 examples/expect_exception/recipes/no_error.rb      |    1 +
 .../expect_exception/spec/compile_error_spec.rb    |   10 +
 .../expect_exception/spec/converge_error_spec.rb   |   10 +
 examples/expect_exception/spec/no_error_spec.rb    |   10 +
 examples/file/recipes/create.rb                    |   15 +
 examples/file/recipes/create_if_missing.rb         |   15 +
 examples/file/recipes/delete.rb                    |   15 +
 examples/file/recipes/touch.rb                     |   15 +
 examples/file/spec/create_if_missing_spec.rb       |   28 +
 examples/file/spec/create_spec.rb                  |   32 +
 examples/file/spec/delete_spec.rb                  |   19 +
 examples/file/spec/touch_spec.rb                   |   19 +
 examples/freebsd_package/recipes/install.rb        |   13 +
 examples/freebsd_package/recipes/remove.rb         |   13 +
 examples/freebsd_package/spec/install_spec.rb      |   23 +
 examples/freebsd_package/spec/remove_spec.rb       |   19 +
 examples/gem_package/recipes/install.rb            |   13 +
 examples/gem_package/recipes/purge.rb              |   13 +
 examples/gem_package/recipes/reconfig.rb           |   13 +
 examples/gem_package/recipes/remove.rb             |   13 +
 examples/gem_package/recipes/upgrade.rb            |   13 +
 examples/gem_package/spec/install_spec.rb          |   23 +
 examples/gem_package/spec/purge_spec.rb            |   19 +
 examples/gem_package/spec/reconfig_spec.rb         |   19 +
 examples/gem_package/spec/remove_spec.rb           |   19 +
 examples/gem_package/spec/upgrade_spec.rb          |   19 +
 examples/git/recipes/checkout.rb                   |   13 +
 examples/git/recipes/export.rb                     |   13 +
 examples/git/recipes/sync.rb                       |   13 +
 examples/git/spec/checkout_spec.rb                 |   19 +
 examples/git/spec/export_spec.rb                   |   19 +
 examples/git/spec/sync_spec.rb                     |   23 +
 examples/group/recipes/create.rb                   |   13 +
 examples/group/recipes/manage.rb                   |   13 +
 examples/group/recipes/modify.rb                   |   13 +
 examples/group/recipes/remove.rb                   |   13 +
 examples/group/spec/create_spec.rb                 |   23 +
 examples/group/spec/manage_spec.rb                 |   19 +
 examples/group/spec/modify_spec.rb                 |   19 +
 examples/group/spec/remove_spec.rb                 |   18 +
 examples/guards/recipes/default.rb                 |   13 +
 examples/guards/spec/default_spec.rb               |   17 +
 .../libraries/resource_service.rb                  |   15 +
 .../providers/service.rb                           |    9 +
 .../recipes/default.rb                             |    4 +
 .../spec/provider_service_spec.rb                  |    7 +
 examples/http_request/recipes/delete.rb            |   13 +
 examples/http_request/recipes/get.rb               |   13 +
 examples/http_request/recipes/head.rb              |   13 +
 examples/http_request/recipes/options.rb           |   13 +
 examples/http_request/recipes/post.rb              |   13 +
 examples/http_request/recipes/put.rb               |   13 +
 examples/http_request/spec/delete_spec.rb          |   19 +
 examples/http_request/spec/get_spec.rb             |   23 +
 examples/http_request/spec/head_spec.rb            |   19 +
 examples/http_request/spec/options_spec.rb         |   19 +
 examples/http_request/spec/post_spec.rb            |   19 +
 examples/http_request/spec/put_spec.rb             |   19 +
 examples/ifconfig/recipes/add.rb                   |    9 +
 examples/ifconfig/recipes/delete.rb                |    8 +
 examples/ifconfig/recipes/disable.rb               |    8 +
 examples/ifconfig/recipes/enable.rb                |    8 +
 examples/ifconfig/spec/add_spec.rb                 |   19 +
 examples/ifconfig/spec/delete_spec.rb              |   15 +
 examples/ifconfig/spec/disable_spec.rb             |   15 +
 examples/ifconfig/spec/enable_spec.rb              |   15 +
 examples/include_recipe/recipes/default.rb         |    1 +
 examples/include_recipe/recipes/not.rb             |    0
 examples/include_recipe/recipes/other.rb           |    0
 examples/include_recipe/spec/default_spec.rb       |   13 +
 examples/ips_package/recipes/install.rb            |   13 +
 examples/ips_package/recipes/remove.rb             |   13 +
 examples/ips_package/recipes/upgrade.rb            |   13 +
 examples/ips_package/spec/install_spec.rb          |   23 +
 examples/ips_package/spec/remove_spec.rb           |   19 +
 examples/ips_package/spec/upgrade_spec.rb          |   19 +
 examples/link/recipes/create.rb                    |   13 +
 examples/link/recipes/delete.rb                    |   13 +
 examples/link/recipes/link_to.rb                   |    3 +
 examples/link/spec/create_spec.rb                  |   23 +
 examples/link/spec/delete_spec.rb                  |   19 +
 examples/link/spec/link_to_spec.rb                 |   11 +
 examples/log/recipes/write.rb                      |   14 +
 examples/log/spec/write_spec.rb                    |   24 +
 examples/macports_package/recipes/install.rb       |   13 +
 examples/macports_package/recipes/purge.rb         |   13 +
 examples/macports_package/recipes/remove.rb        |   13 +
 examples/macports_package/recipes/upgrade.rb       |   13 +
 examples/macports_package/spec/install_spec.rb     |   23 +
 examples/macports_package/spec/purge_spec.rb       |   19 +
 examples/macports_package/spec/remove_spec.rb      |   19 +
 examples/macports_package/spec/upgrade_spec.rb     |   19 +
 examples/mdadm/recipes/assemble.rb                 |   13 +
 examples/mdadm/recipes/create.rb                   |   13 +
 examples/mdadm/recipes/stop.rb                     |   13 +
 examples/mdadm/spec/assemble_spec.rb               |   19 +
 examples/mdadm/spec/create_spec.rb                 |   23 +
 examples/mdadm/spec/stop_spec.rb                   |   19 +
 examples/mount/recipes/disable.rb                  |    8 +
 examples/mount/recipes/enable.rb                   |    8 +
 examples/mount/recipes/mount.rb                    |    9 +
 examples/mount/recipes/remount.rb                  |    8 +
 examples/mount/recipes/umount.rb                   |    8 +
 examples/mount/spec/disable_spec.rb                |   15 +
 examples/mount/spec/enable_spec.rb                 |   15 +
 examples/mount/spec/mount_spec.rb                  |   19 +
 examples/mount/spec/remount_spec.rb                |   15 +
 examples/mount/spec/umount_spec.rb                 |   15 +
 examples/multiple_actions/recipes/default.rb       |    3 +
 examples/multiple_actions/recipes/sequential.rb    |    7 +
 examples/multiple_actions/spec/default_spec.rb     |   14 +
 examples/multiple_actions/spec/sequential_spec.rb  |   16 +
 examples/multiple_run_action/recipes/default.rb    |    5 +
 examples/multiple_run_action/spec/default_spec.rb  |   18 +
 examples/notifications/recipes/chained.rb          |   12 +
 examples/notifications/recipes/default.rb          |    7 +
 examples/notifications/recipes/delayed.rb          |    7 +
 examples/notifications/recipes/immediately.rb      |    7 +
 examples/notifications/spec/chained_spec.rb        |   21 +
 examples/notifications/spec/default_spec.rb        |   16 +
 examples/notifications/spec/delayed_spec.rb        |   16 +
 examples/notifications/spec/immediately_spec.rb    |   16 +
 examples/ohai/recipes/reload.rb                    |   13 +
 examples/ohai/spec/reload_spec.rb                  |   23 +
 examples/package/recipes/install.rb                |   13 +
 examples/package/recipes/purge.rb                  |   13 +
 examples/package/recipes/reconfig.rb               |   13 +
 examples/package/recipes/remove.rb                 |   13 +
 examples/package/recipes/upgrade.rb                |   13 +
 examples/package/spec/install_spec.rb              |   23 +
 examples/package/spec/purge_spec.rb                |   19 +
 examples/package/spec/reconfig_spec.rb             |   19 +
 examples/package/spec/remove_spec.rb               |   19 +
 examples/package/spec/upgrade_spec.rb              |   19 +
 examples/pacman_package/recipes/install.rb         |   13 +
 examples/pacman_package/recipes/purge.rb           |   13 +
 examples/pacman_package/recipes/remove.rb          |   13 +
 examples/pacman_package/recipes/upgrade.rb         |   13 +
 examples/pacman_package/spec/install_spec.rb       |   23 +
 examples/pacman_package/spec/purge_spec.rb         |   19 +
 examples/pacman_package/spec/remove_spec.rb        |   19 +
 examples/pacman_package/spec/upgrade_spec.rb       |   19 +
 examples/portage_package/recipes/install.rb        |   13 +
 examples/portage_package/recipes/purge.rb          |   13 +
 examples/portage_package/recipes/remove.rb         |   13 +
 examples/portage_package/recipes/upgrade.rb        |   13 +
 examples/portage_package/spec/install_spec.rb      |   23 +
 examples/portage_package/spec/purge_spec.rb        |   19 +
 examples/portage_package/spec/remove_spec.rb       |   19 +
 examples/portage_package/spec/upgrade_spec.rb      |   19 +
 examples/powershell_script/recipes/run.rb          |   13 +
 examples/powershell_script/spec/run_spec.rb        |   23 +
 examples/reboot/recipes/cancel.rb                  |    3 +
 examples/reboot/recipes/now.rb                     |    3 +
 examples/reboot/recipes/request.rb                 |    3 +
 examples/reboot/spec/cancel_spec.rb                |   10 +
 examples/reboot/spec/now_spec.rb                   |   10 +
 examples/reboot/spec/request_spec.rb               |   10 +
 examples/registry_key/recipes/create.rb            |   13 +
 examples/registry_key/recipes/create_if_missing.rb |   13 +
 examples/registry_key/recipes/delete.rb            |   13 +
 examples/registry_key/recipes/delete_key.rb        |   13 +
 .../registry_key/spec/create_if_missing_spec.rb    |   19 +
 examples/registry_key/spec/create_spec.rb          |   23 +
 examples/registry_key/spec/delete_key_spec.rb      |   19 +
 examples/registry_key/spec/delete_spec.rb          |   19 +
 examples/remote_directory/recipes/create.rb        |   13 +
 .../remote_directory/recipes/create_if_missing.rb  |   13 +
 examples/remote_directory/recipes/delete.rb        |   13 +
 .../spec/create_if_missing_spec.rb                 |   19 +
 examples/remote_directory/spec/create_spec.rb      |   23 +
 examples/remote_directory/spec/delete_spec.rb      |   19 +
 examples/remote_file/recipes/create.rb             |   18 +
 examples/remote_file/recipes/create_if_missing.rb  |   16 +
 examples/remote_file/recipes/delete.rb             |   16 +
 examples/remote_file/recipes/touch.rb              |   16 +
 .../remote_file/spec/create_if_missing_spec.rb     |   19 +
 examples/remote_file/spec/create_spec.rb           |   23 +
 examples/remote_file/spec/delete_spec.rb           |   19 +
 examples/remote_file/spec/touch_spec.rb            |   19 +
 examples/render_file/files/default/cookbook_file   |    1 +
 examples/render_file/recipes/default.rb            |   15 +
 examples/render_file/recipes/template_helpers.rb   |    9 +
 examples/render_file/spec/default_spec.rb          |  155 +++
 examples/render_file/spec/template_helpers_spec.rb |   10 +
 .../render_file/templates/default/_partial.erb     |    1 +
 examples/render_file/templates/default/partial.erb |    1 +
 .../render_file/templates/default/template.erb     |    1 +
 .../templates/default/template_with_helper.erb     |    1 +
 examples/roles/recipes/another.rb                  |    3 +
 examples/roles/recipes/default.rb                  |    1 +
 examples/roles/roles/role.rb                       |    9 +
 examples/roles/spec/default_spec.rb                |   21 +
 examples/route/recipes/add.rb                      |   13 +
 examples/route/recipes/delete.rb                   |   13 +
 examples/route/spec/add_spec.rb                    |   23 +
 examples/route/spec/delete_spec.rb                 |   19 +
 examples/rpm_package/recipes/install.rb            |   13 +
 examples/rpm_package/recipes/remove.rb             |   13 +
 examples/rpm_package/recipes/upgrade.rb            |   13 +
 examples/rpm_package/spec/install_spec.rb          |   23 +
 examples/rpm_package/spec/remove_spec.rb           |   19 +
 examples/rpm_package/spec/upgrade_spec.rb          |   19 +
 examples/ruby_block/recipes/run.rb                 |    9 +
 examples/ruby_block/spec/run_spec.rb               |   18 +
 examples/script/recipes/run_bash.rb                |   13 +
 examples/script/recipes/run_csh.rb                 |   13 +
 examples/script/recipes/run_perl.rb                |   13 +
 examples/script/recipes/run_python.rb              |   13 +
 examples/script/recipes/run_ruby.rb                |   13 +
 examples/script/recipes/run_script.rb              |   13 +
 examples/script/spec/run_bash_spec.rb              |   23 +
 examples/script/spec/run_csh_spec.rb               |   23 +
 examples/script/spec/run_perl_spec.rb              |   23 +
 examples/script/spec/run_python_spec.rb            |   23 +
 examples/script/spec/run_ruby_spec.rb              |   23 +
 examples/script/spec/run_script_spec.rb            |   23 +
 examples/server/recipes/client.rb                  |    6 +
 examples/server/recipes/data_bag.rb                |    7 +
 examples/server/recipes/environment.rb             |    6 +
 examples/server/recipes/node.rb                    |    6 +
 examples/server/recipes/render_with_cached.rb      |    3 +
 examples/server/recipes/role.rb                    |    6 +
 examples/server/recipes/search.rb                  |    5 +
 examples/server/spec/client_spec.rb                |   18 +
 examples/server/spec/data_bag_spec.rb              |   27 +
 examples/server/spec/environment_spec.rb           |   18 +
 examples/server/spec/node_spec.rb                  |   49 +
 examples/server/spec/render_with_cached_spec.rb    |   15 +
 examples/server/spec/role_spec.rb                  |   18 +
 examples/server/spec/search_spec.rb                |   61 ++
 examples/service/recipes/disable.rb                |   13 +
 examples/service/recipes/enable.rb                 |   13 +
 examples/service/recipes/reload.rb                 |   13 +
 examples/service/recipes/restart.rb                |   13 +
 examples/service/recipes/start.rb                  |   13 +
 examples/service/recipes/stop.rb                   |   13 +
 examples/service/spec/disable_spec.rb              |   19 +
 examples/service/spec/enable_spec.rb               |   19 +
 examples/service/spec/reload_spec.rb               |   19 +
 examples/service/spec/restart_spec.rb              |   19 +
 examples/service/spec/start_spec.rb                |   19 +
 examples/service/spec/stop_spec.rb                 |   19 +
 examples/smartos_package/recipes/install.rb        |   13 +
 examples/smartos_package/recipes/remove.rb         |   13 +
 examples/smartos_package/recipes/upgrade.rb        |   13 +
 examples/smartos_package/spec/install_spec.rb      |   23 +
 examples/smartos_package/spec/remove_spec.rb       |   19 +
 examples/smartos_package/spec/upgrade_spec.rb      |   19 +
 examples/solaris_package/recipes/install.rb        |   13 +
 examples/solaris_package/recipes/remove.rb         |   13 +
 examples/solaris_package/spec/install_spec.rb      |   23 +
 examples/solaris_package/spec/remove_spec.rb       |   19 +
 examples/state_attrs/providers/lwrp.rb             |    3 +
 examples/state_attrs/recipes/default.rb            |    3 +
 examples/state_attrs/resources/lwrp.rb             |    6 +
 examples/state_attrs/spec/default_spec.rb          |   12 +
 examples/step_into/providers/lwrp.rb               |    3 +
 examples/step_into/recipes/default.rb              |    1 +
 examples/step_into/resources/lwrp.rb               |    4 +
 examples/step_into/spec/default_spec.rb            |   22 +
 examples/stub_command/recipes/default.rb           |    5 +
 examples/stub_command/recipes/other_recipe.rb      |    3 +
 examples/stub_command/spec/default_spec.rb         |   28 +
 examples/stub_data_bag/recipes/default.rb          |    5 +
 examples/stub_data_bag/spec/default_spec.rb        |   27 +
 examples/stub_data_bag_item/recipes/default.rb     |    5 +
 examples/stub_data_bag_item/spec/default_spec.rb   |   27 +
 examples/stub_node/recipes/default.rb              |    0
 examples/stub_node/spec/default_spec.rb            |   17 +
 examples/stub_search/recipes/default.rb            |    5 +
 examples/stub_search/spec/default_spec.rb          |   27 +
 examples/subscribes/recipes/chained.rb             |   10 +
 examples/subscribes/recipes/default.rb             |    5 +
 examples/subscribes/recipes/delayed.rb             |    5 +
 examples/subscribes/recipes/immediately.rb         |    5 +
 examples/subscribes/spec/chained_spec.rb           |   21 +
 examples/subscribes/spec/default_spec.rb           |   17 +
 examples/subscribes/spec/delayed_spec.rb           |   16 +
 examples/subscribes/spec/immediately_spec.rb       |   16 +
 examples/subversion/recipes/checkout.rb            |   13 +
 examples/subversion/recipes/export.rb              |   13 +
 examples/subversion/recipes/force_export.rb        |   13 +
 examples/subversion/recipes/sync.rb                |   13 +
 examples/subversion/spec/checkout_spec.rb          |   19 +
 examples/subversion/spec/export_spec.rb            |   19 +
 examples/subversion/spec/force_export_spec.rb      |   19 +
 examples/subversion/spec/sync_spec.rb              |   23 +
 examples/template/recipes/create.rb                |   15 +
 examples/template/recipes/create_if_missing.rb     |   15 +
 examples/template/recipes/delete.rb                |   15 +
 examples/template/recipes/touch.rb                 |   15 +
 examples/template/spec/create_if_missing_spec.rb   |   28 +
 examples/template/spec/create_spec.rb              |   32 +
 examples/template/spec/delete_spec.rb              |   19 +
 examples/template/spec/touch_spec.rb               |   19 +
 .../use_inline_resources/libraries/matchers.rb     |    5 +
 examples/use_inline_resources/providers/lwrp.rb    |    7 +
 examples/use_inline_resources/recipes/default.rb   |    1 +
 examples/use_inline_resources/resources/lwrp.rb    |    4 +
 examples/use_inline_resources/spec/default_spec.rb |   18 +
 examples/user/recipes/create.rb                    |   13 +
 examples/user/recipes/lock.rb                      |   13 +
 examples/user/recipes/manage.rb                    |   13 +
 examples/user/recipes/modify.rb                    |   13 +
 examples/user/recipes/remove.rb                    |   13 +
 examples/user/recipes/unlock.rb                    |   13 +
 examples/user/spec/create_spec.rb                  |   23 +
 examples/user/spec/lock_spec.rb                    |   19 +
 examples/user/spec/manage_spec.rb                  |   19 +
 examples/user/spec/modify_spec.rb                  |   19 +
 examples/user/spec/remove_spec.rb                  |   19 +
 examples/user/spec/unlock_spec.rb                  |   19 +
 .../windows_service/recipes/configure_startup.rb   |   13 +
 examples/windows_service/recipes/disable.rb        |   13 +
 examples/windows_service/recipes/enable.rb         |   13 +
 examples/windows_service/recipes/reload.rb         |   13 +
 examples/windows_service/recipes/restart.rb        |   13 +
 examples/windows_service/recipes/start.rb          |   13 +
 examples/windows_service/recipes/stop.rb           |   13 +
 .../windows_service/spec/configure_startup_spec.rb |   19 +
 examples/windows_service/spec/disable_spec.rb      |   19 +
 examples/windows_service/spec/enable_spec.rb       |   19 +
 examples/windows_service/spec/reload_spec.rb       |   19 +
 examples/windows_service/spec/restart_spec.rb      |   19 +
 examples/windows_service/spec/start_spec.rb        |   19 +
 examples/windows_service/spec/stop_spec.rb         |   19 +
 examples/yum_package/recipes/install.rb            |   13 +
 examples/yum_package/recipes/purge.rb              |   13 +
 examples/yum_package/recipes/remove.rb             |   13 +
 examples/yum_package/recipes/upgrade.rb            |   13 +
 examples/yum_package/spec/install_spec.rb          |   23 +
 examples/yum_package/spec/purge_spec.rb            |   19 +
 examples/yum_package/spec/remove_spec.rb           |   19 +
 examples/yum_package/spec/upgrade_spec.rb          |   19 +
 features/apt_package.feature                       |   14 +
 features/attributes.feature                        |    7 +
 features/batch.feature                             |   10 +
 features/cached.feature                            |    7 +
 features/chef_gem.feature                          |   14 +
 features/compile_time.feature                      |    7 +
 features/cookbook_file.feature                     |   13 +
 features/cron.feature                              |   11 +
 features/custom_matcher.feature                    |   11 +
 features/deploy.feature                            |   12 +
 features/directory.feature                         |   11 +
 features/do_nothing.feature                        |   10 +
 features/dpkg_package.feature                      |   12 +
 features/easy_install_package.feature              |   13 +
 features/env.feature                               |   12 +
 features/erl_call.feature                          |   10 +
 features/execute.feature                           |   10 +
 features/expect_exception.feature                  |   12 +
 features/file.feature                              |   13 +
 features/freebsd_package.feature                   |   11 +
 features/gem_package.feature                       |   14 +
 features/git.feature                               |   12 +
 features/group.feature                             |   13 +
 features/guards.feature                            |   10 +
 features/heavy_provider_light_resource.feature     |   10 +
 features/http_request.feature                      |   15 +
 features/ifconfig.feature                          |   13 +
 features/include_recipe.feature                    |   10 +
 features/ips_package.feature                       |   12 +
 features/link.feature                              |   12 +
 features/log.feature                               |   10 +
 features/macports_package.feature                  |   13 +
 features/mdadm.feature                             |   12 +
 features/mount.feature                             |   14 +
 features/multiple_actions.feature                  |   11 +
 features/multiple_run_action.feature               |    7 +
 features/notifications.feature                     |   13 +
 features/ohai.feature                              |   10 +
 features/package.feature                           |   14 +
 features/pacman_package.feature                    |   13 +
 features/portage_package.feature                   |   13 +
 features/powershell_script.feature                 |   10 +
 features/reboot.feature                            |   19 +
 features/registry_key.feature                      |   13 +
 features/remote_directory.feature                  |   12 +
 features/remote_file.feature                       |   13 +
 features/render_file.feature                       |   11 +
 features/roles.feature                             |    7 +
 features/route.feature                             |   11 +
 features/rpm_package.feature                       |   12 +
 features/ruby_block.feature                        |   10 +
 features/script.feature                            |   15 +
 features/server.feature                            |   16 +
 features/service.feature                           |   15 +
 features/smartos_package.feature                   |   12 +
 features/solaris_package.feature                   |   11 +
 features/state_attrs.feature                       |    7 +
 features/step_definitions/background_steps.rb      |    4 +
 features/step_into.feature                         |    7 +
 features/stub_command.feature                      |    7 +
 features/stub_data_bag.feature                     |    7 +
 features/stub_data_bag_item.feature                |    7 +
 features/stub_node.feature                         |    7 +
 features/stub_search.feature                       |    7 +
 features/subscribes.feature                        |   13 +
 features/subversion.feature                        |   13 +
 features/support/env.rb                            |   29 +
 features/support/executor.rb                       |   20 +
 features/template.feature                          |   13 +
 features/use_inline_resources.feature              |    7 +
 features/user.feature                              |   15 +
 features/windows_service.feature                   |   23 +
 features/yum_package.feature                       |   13 +
 gemfiles/chefspec.gemfile                          |    9 +
 lib/chefspec.rb                                    |   83 ++
 lib/chefspec/api.rb                                |   78 ++
 lib/chefspec/api/apt_package.rb                    |  192 ++++
 lib/chefspec/api/batch.rb                          |   43 +
 lib/chefspec/api/chef_gem.rb                       |  191 ++++
 lib/chefspec/api/cookbook_file.rb                  |  166 ++++
 lib/chefspec/api/cron.rb                           |   80 ++
 lib/chefspec/api/deploy.rb                         |  117 +++
 lib/chefspec/api/directory.rb                      |   80 ++
 lib/chefspec/api/do_nothing.rb                     |   24 +
 lib/chefspec/api/dpkg_package.rb                   |  117 +++
 lib/chefspec/api/easy_install_package.rb           |  154 +++
 lib/chefspec/api/env.rb                            |  117 +++
 lib/chefspec/api/erl_call.rb                       |   43 +
 lib/chefspec/api/execute.rb                        |   43 +
 lib/chefspec/api/file.rb                           |  166 ++++
 lib/chefspec/api/freebsd_package.rb                |   80 ++
 lib/chefspec/api/gem_package.rb                    |  191 ++++
 lib/chefspec/api/git.rb                            |  117 +++
 lib/chefspec/api/group.rb                          |  154 +++
 lib/chefspec/api/http_request.rb                   |  228 +++++
 lib/chefspec/api/ifconfig.rb                       |  154 +++
 lib/chefspec/api/include_recipe.rb                 |   26 +
 lib/chefspec/api/ips_package.rb                    |  117 +++
 lib/chefspec/api/link.rb                           |  102 ++
 lib/chefspec/api/log.rb                            |   43 +
 lib/chefspec/api/macports_package.rb               |  154 +++
 lib/chefspec/api/mdadm.rb                          |  117 +++
 lib/chefspec/api/mount.rb                          |  192 ++++
 lib/chefspec/api/notifications.rb                  |   38 +
 lib/chefspec/api/ohai.rb                           |   43 +
 lib/chefspec/api/package.rb                        |  192 ++++
 lib/chefspec/api/pacman_package.rb                 |  155 +++
 lib/chefspec/api/portage_package.rb                |  155 +++
 lib/chefspec/api/powershell_script.rb              |   43 +
 lib/chefspec/api/reboot.rb                         |   18 +
 lib/chefspec/api/registry_key.rb                   |  166 ++++
 lib/chefspec/api/remote_directory.rb               |  120 +++
 lib/chefspec/api/remote_file.rb                    |  166 ++++
 lib/chefspec/api/render_file.rb                    |   35 +
 lib/chefspec/api/route.rb                          |   80 ++
 lib/chefspec/api/rpm_package.rb                    |  117 +++
 lib/chefspec/api/ruby_block.rb                     |   37 +
 lib/chefspec/api/script.rb                         |  239 +++++
 lib/chefspec/api/service.rb                        |  246 +++++
 lib/chefspec/api/smartos_package.rb                |  117 +++
 lib/chefspec/api/solaris_package.rb                |   80 ++
 lib/chefspec/api/state_attrs.rb                    |   28 +
 lib/chefspec/api/subscriptions.rb                  |   35 +
 lib/chefspec/api/subversion.rb                     |  154 +++
 lib/chefspec/api/template.rb                       |  166 ++++
 lib/chefspec/api/user.rb                           |  228 +++++
 lib/chefspec/api/windows_service.rb                |  286 ++++++
 lib/chefspec/api/yum_package.rb                    |  154 +++
 lib/chefspec/berkshelf.rb                          |   57 ++
 lib/chefspec/cacher.rb                             |   63 ++
 lib/chefspec/chef_backwards_compat.rb              |   79 ++
 lib/chefspec/coverage.rb                           |  200 ++++
 lib/chefspec/coverage/filters.rb                   |   79 ++
 lib/chefspec/deprecations.rb                       |   51 +
 lib/chefspec/errors.rb                             |   40 +
 lib/chefspec/expect_exception.rb                   |   50 +
 lib/chefspec/extensions/chef/client.rb             |   22 +
 lib/chefspec/extensions/chef/conditional.rb        |   14 +
 lib/chefspec/extensions/chef/cookbook_uploader.rb  |   11 +
 lib/chefspec/extensions/chef/data_query.rb         |   47 +
 lib/chefspec/extensions/chef/lwrp_base.rb          |   23 +
 lib/chefspec/extensions/chef/resource.rb           |   49 +
 .../extensions/chef/resource/freebsd_package.rb    |   19 +
 lib/chefspec/extensions/chef/securable.rb          |   19 +
 lib/chefspec/formatter.rb                          |  270 +++++
 lib/chefspec/librarian.rb                          |   50 +
 lib/chefspec/macros.rb                             |  222 +++++
 lib/chefspec/matchers.rb                           |   12 +
 lib/chefspec/matchers/do_nothing_matcher.rb        |   52 +
 lib/chefspec/matchers/include_recipe_matcher.rb    |   46 +
 lib/chefspec/matchers/link_to_matcher.rb           |   37 +
 lib/chefspec/matchers/notifications_matcher.rb     |  121 +++
 lib/chefspec/matchers/render_file_matcher.rb       |  129 +++
 lib/chefspec/matchers/resource_matcher.rb          |  173 ++++
 lib/chefspec/matchers/state_attrs_matcher.rb       |   71 ++
 lib/chefspec/matchers/subscribes_matcher.rb        |   63 ++
 lib/chefspec/mixins/normalize.rb                   |   22 +
 lib/chefspec/renderer.rb                           |  145 +++
 lib/chefspec/rspec.rb                              |   20 +
 lib/chefspec/server.rb                             |    4 +
 lib/chefspec/server_methods.rb                     |  176 ++++
 lib/chefspec/server_runner.rb                      |  103 ++
 lib/chefspec/solo_runner.rb                        |  403 ++++++++
 lib/chefspec/stubs/command_registry.rb             |   11 +
 lib/chefspec/stubs/command_stub.rb                 |   37 +
 lib/chefspec/stubs/data_bag_item_registry.rb       |   13 +
 lib/chefspec/stubs/data_bag_item_stub.rb           |   25 +
 lib/chefspec/stubs/data_bag_registry.rb            |   13 +
 lib/chefspec/stubs/data_bag_stub.rb                |   23 +
 lib/chefspec/stubs/registry.rb                     |   32 +
 lib/chefspec/stubs/search_registry.rb              |   13 +
 lib/chefspec/stubs/search_stub.rb                  |   25 +
 lib/chefspec/stubs/stub.rb                         |   38 +
 lib/chefspec/util.rb                               |   58 ++
 lib/chefspec/version.rb                            |    3 +
 metadata.yml                                       |  891 +++++++++++++++++
 spec/spec_helper.rb                                |    8 +
 spec/support/hash.rb                               |   35 +
 spec/unit/api_spec.rb                              |   18 +
 spec/unit/cacher_spec.rb                           |   54 +
 spec/unit/coverage/filters_spec.rb                 |   60 ++
 spec/unit/deprecations_spec.rb                     |   76 ++
 spec/unit/errors_spec.rb                           |   57 ++
 spec/unit/expect_exception_spec.rb                 |   32 +
 spec/unit/extensions/lwrp_base_spec.rb             |   96 ++
 spec/unit/macros_spec.rb                           |  119 +++
 spec/unit/matchers/do_nothing_matcher.rb           |    5 +
 spec/unit/matchers/include_recipe_matcher_spec.rb  |   38 +
 spec/unit/matchers/link_to_matcher_spec.rb         |   55 ++
 spec/unit/matchers/notifications_matcher_spec.rb   |   39 +
 spec/unit/matchers/render_file_matcher_spec.rb     |   50 +
 spec/unit/matchers/resource_matcher_spec.rb        |    5 +
 spec/unit/matchers/state_attrs_matcher_spec.rb     |   68 ++
 spec/unit/matchers/subscribes_matcher_spec.rb      |   64 ++
 spec/unit/renderer_spec.rb                         |   69 ++
 spec/unit/solo_runner_spec.rb                      |  170 ++++
 spec/unit/stubs/command_registry_spec.rb           |   27 +
 spec/unit/stubs/command_stub_spec.rb               |   61 ++
 spec/unit/stubs/data_bag_item_registry_spec.rb     |   39 +
 spec/unit/stubs/data_bag_item_stub_spec.rb         |   36 +
 spec/unit/stubs/data_bag_registry_spec.rb          |   39 +
 spec/unit/stubs/data_bag_stub_spec.rb              |   35 +
 spec/unit/stubs/registry_spec.rb                   |   29 +
 spec/unit/stubs/search_registry_spec.rb            |   39 +
 spec/unit/stubs/search_stub_spec.rb                |   36 +
 spec/unit/stubs/stub_spec.rb                       |   64 ++
 templates/coverage/human.erb                       |   22 +
 templates/errors/cookbook_path_not_found.erb       |    3 +
 templates/errors/gem_load_error.erb                |    7 +
 templates/errors/no_conversion_error.erb           |    1 +
 templates/errors/not_stubbed.erb                   |    7 +
 642 files changed, 21334 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c184d6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+.idea
+.yardoc
+*.gem
+*.html
+*.rbc
+*.swp
+^coverage
+doc
+tmp
+Gemfile.lock
+gemfiles/*.lock
+.bundle
+bin
+vendor
+.rspec
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..39b47e3
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,36 @@
+language: ruby
+cache: bundler
+sudo: false
+
+rvm:
+  - 2.0.0
+  - 2.1
+  - 2.2
+
+branches:
+  only:
+    - master
+
+bundler_args: --jobs 7 --retry 3
+
+gemfile: gemfiles/chefspec.gemfile
+
+env:
+  - CHEF_VERSION=master
+  - CHEF_VERSION=12.2.1
+  - CHEF_VERSION=12.1.2
+  - CHEF_VERSION=12.1.1
+  - CHEF_VERSION=12.1.0
+  - CHEF_VERSION=12.0.3
+  - CHEF_VERSION=11.18.6
+  - CHEF_VERSION=11.18.0
+  - CHEF_VERSION=11.16.4
+  - CHEF_VERSION=11.16.2
+  - CHEF_VERSION=11.16.0
+  - CHEF_VERSION=11.14.6
+  - CHEF_VERSION=11.14.2
+
+matrix:
+  fast_finish: true
+  allow_failures:
+    - env: CHEF_VERSION=master
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..01d5d5f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,582 @@
+CHANGELOG for ChefSpec
+======================
+
+## 4.3.0 (July 16, 2015)
+
+Bugfixes:
+  - Improved documentation
+  - Use TemplateContext's `_extend_modules` for passing on template helper
+    modules to nested partial templates
+  - Only exit if the status is a failure [GH-565]
+  - Fix load order on Windows when using the `rights` attributes
+  - Improve testing matrix and remove deprecated Chef versions
+  - Supress chef_gem compile_time warnings
+  - Fix exceptions on converge for Chef 12.1.0 masking
+  - Update Librarian `Cheffile` site URL to supermerket.chef.io
+  - Filter Windows and Unix paths for test coverage report with Berkshelf
+
+Improvements:
+  - Added possibility to specify file_cache_path globally
+  - Added new capabilities for reboot resouce
+  - Use `Chef::Resource#declared_type` first if available (Chef >= 12),
+    otherwise fall back to `Chef::Resource#resource_name` (Chef <= 11)
+  - Extend render_file to yield the content as a block
+  - Add windows_service resource
+
+## 4.2.0 (December 25, 2014)
+Bugfixes:
+  - Updated README grep examples
+  - Fix various typographical errors
+  - Improved error message when using a deprecated resource matcher
+
+Improvements:
+  - Add support for Chef 12
+  - Added support for environments in Chef Solo
+  - Fix location definition for caching
+  - Allow passing `Chef::Config[:file_cache_path]` from outside
+  - Update support for Chef 12
+
+## 4.1.1 (October 13, 2014)
+Bugfixes:
+  - Fix total fail on my part with deprecations and add test coverage
+  - Do not validate cookbooks on upload (speed)
+
+## 4.1.0 (October 12, 2014)
+Bugfixes:
+  - Bump the minimum required version of Chef to 11.14
+  - Filter resources that have no source line in the coverage report - this is related to a bug in Chef where, when cloning a resource, both the clone and the original resource lose their metadata
+
+Improvements:
+  - Allow multiple instances of a Chef Server instance
+  - Separate ChefSpec::Runner into `ChefSpec::SoloRunner` and `ChefSpec::ServerRunner`
+  - Preliminary support for Chef 12 alpha (not 100% complete)
+  - Add `SoloRunner` and `ServerRunner` `.converge` methods for quickly converging if you do not need to customize the runner object
+  - Create isolated Chef Server instances on a per-test or per-suite basis
+  - Deprecate `ChefSpec::Runner.define_runner_method` in favor of `ChefSpec.define_matcher`
+  - Deprecate `ChefSpec::Runner.new` - you should specify if you want a `SoloRunner` or `ServerRunner`
+  - Updated documentation
+
+Breaking changes:
+  - Due to the new `ServerRunner`, the `ChefSpec::Server` singleton object has been deprecated. As much as I would like to provide a backwards-compatible interface, there is no way to do so (as the code now supports multipel Chef Server instances). Sorry :(.
+  - Remove old deprecations - this is not really a breaking change, but the v2.0.0 deprecations have been removed in favor of new ones
+
+## 4.0.2 (August 13, 2014)
+Bugfixes:
+  - Fix a regression caused by a new version of Chef Zero in single_org mode
+
+Improvements:
+  - Test against Chef 11.14.0 in the Travis matrix
+
+## 4.0.1 (June 27, 2014)
+Bugfixes:
+  - Fix class comparisons
+  - Update ChefZero API to work again
+  - Use proper cookbook in the template_finder
+  - Fix Ruby deprecations
+  - Various documentation updates
+
+## 4.0.0 (June 2, 2014)
+Breaking Changes:
+  - **Upgraded to RSpec 3!** RSpec 3 brings many new API changes and syntaxes
+  - **Bump minimum required Chef version to 11.12!** Without this change, Chef Zero will blow up
+
+Bugfixes:
+  - Gracefully fail if a resource does not report it's source line in the reporter
+  - Pull the correct cookbook folder from the stack on Windows (88bfc6)
+  - Cover resources in render_file matchers for reporting
+  - Cover resources in link_to matchers for reporting
+  - Cover resources in do_nothing matchers for reporting
+  - Fix memory leak in LWRP Resource classes
+  - Restore the original `cookbook_path` when using librarian-chef
+  - Documentation fixes
+  - Disable lazy loading of cached resources
+  - Fix a bug that will happen in later Chef versions because FreeBSD is evil (13ff143)
+  - Do not pass local file paths to `preferred_filename_on_disk_location`
+
+Features:
+  - Add runner methods for all the bash, csh, perl, etc resources
+  - Upgraded fauxhai dependency and specs
+  - Upgrade Chef Zero for multi-org support
+
+Improvements:
+  - Improved documentation around the `define_runner_method` method
+  - Update badges to be all SVG
+  - Test on Ruby 2.1
+  - Use a randomly assigned port for Chef Zero
+  - Remove references to `.stub` from documentation
+
+
+## 3.4.0 (March 16, 2014)
+Bugfixes:
+  - Restore Berkshelf 2 support (missing edge case)
+  - Add negative failure message for subscribes/notifies matchers
+
+Features:
+  - Added `do_nothing` matcher for asserting a resource performed no actions
+
+Improvements:
+  - Increased spec coverage for matchers
+  - Support RSpec matchers in `with_content`
+
+## 3.3.1 (March 11, 2014)
+Bugfixes:
+  - Various typographical fixes in the README
+  - Remove unused JSON report
+  - Restore coverage reporting for Berkshelf 2 users
+  - Minor formatting changes for errors
+
+Improvements:
+  - Remove dependency on i18n and use native ERB instead
+  - Vendor Berkshelf/Librarian in a "cookbooks" directory so we can shorten the pathname during coverage reporting
+
+## 3.3.0 (March 9, 2014)
+Bugfixes:
+  - Update documentation for setting `automatic` attributes
+  - Update documentation for stubbing data_bags
+  - Use a non-internal RSpec variable name for expecting exceptions
+  - Fix the airity of the `stub_node` method
+
+Improvements:
+  - Better filtering protocols for resource coverage
+
+## 3.2.0 (January 31, 2014)
+Bugfixes:
+  - Fix coverage calculation when there are no resources in the collection
+  - Fix misplaced quote in matcher output
+  - Include all files in generated gem
+  - Ensure compatability with Chef HEAD
+
+Improvements:
+  - Test Ruby 2.1 on Travis
+  - Miscellaneous README typos
+  - Add `subscribe_to` matchers
+  - Add documentation for the `with` matcher
+  - Add support for passing a `node` object directly to the server `create_node` method
+
+## 3.1.4 (January 3, 2014)
+Bugfixes:
+  - Correctly expand the node's run_list in server mode
+  - Actually package i18n translations with the cookbook
+
+Improvements:
+  - Document setting a node's role
+  - Add examples and features for `stub_node`
+
+## 3.1.3 (January 2, 2014)
+Bugfixes:
+  - Fix broken deprecations module
+  - Update a broken test ([@bknowles][])
+
+Improvements:
+  - Better gem description on GitHub and Rubygems
+  - Helper support for template rendering (Chef 11.4+) ([@geraud][])
+
+## 3.1.2 (December 30, 2013)
+Bugfixes:
+  - Require i18n 0.6.9 or higher (but less than 1.0.0) ([#289](https://github.com/sethvargo/chefspec/issues/245))
+
+## 3.1.1 (December 29, 2013)
+Bugfixes:
+  - Handle duplicate resources in the coverage reporter
+
+## 3.1.0 (December 29, 2013)
+Bugfixes:
+  - Only remove existing LWRP resources and providers when a new one is encountered ([@student][])
+  - Attempt to coerce dashes to underscores in LWRPs
+  - Various documentation fixes and typos
+
+Improvements:
+  - Don't include `deprecations` module by default
+  - Add examples for stubbing commands across recipes
+  - Yield a block after cookbooks are compiled but before they are converged ([@rteabeault][])
+  - Add IRC Channel #chefspec
+  - Raise a nice error when `cookbook_path` cannot be determined ([@ryotarai][])
+
+Features:
+  - Add support for Librarian ([@mlafeldt][])
+  - Add Cacher module for caching runner results ([@sethvargo][] & [@DracoAter][])
+  - Add Chef Zero (server) mode ([@sethvargo][])
+  - Add basic resource reporting (Chef recipe code coverage) ([@sethvargo][])
+  - Use i18n (internationalization) ([@sethvargo][])
+
+## 3.0.2 (November 6, 2013)
+Bugfixes:
+  - Update links to point at new repository location
+  - Deleted relish docs to remove confusion
+  - Bump fauxhai dependency to 2.0 (removing the HTTParty transitive dependency)
+  - Convert resource names with dashes to underscores when stepping into them
+
+Improvements:
+  - Added a "what people are saying" section to the README - if you have something cool to say about ChefSpec, let me know!
+  - Add a link to [`knife-spec`](https://github.com/sethvargo/knife-spec)
+  - Test against Chef 11.8
+  - New matcher: `have_state_attrs` for testing custom LWRP state attributes
+  - Run tests in a sandboxed parent instead of tmp (makes Travis happier)
+
+
+## 3.0.1 (October 22, 2013)
+Bugfixes:
+  - Fix an issue where LWRPs were not properly stepped into when `use_inline_resources` was specified ([#244](https://github.com/sethvargo/chefspec/issues/244))
+  - Coerce `render_file` `@expected_content` to a string before asserting if content is included ([#243](https://github.com/sethvargo/chefspec/issues/243))
+
+Improvements:
+  - Miscellaneous documentation fixes and additions ([#245](https://github.com/sethvargo/chefspec/issues/245), [#241](https://github.com/sethvargo/chefspec/issues/241), [#238](https://github.com/sethvargo/chefspec/issues/238))
+
+
+## 3.0.0 (October 20, 2013)
+Breaking:
+  - Dropped support for Chef 9 & 10
+  - Renamed `ChefSpec::ChefRunner` to `ChefSpec::Runner` to better reflect what happens in Chef Core. Using `ChefRunner` will throw deprecation errors for now and will be removed in a future release.
+  - Removed MiniTest Chef Handler examples/matchers
+  - No longer load default cookbook paths:
+    - vendor/cookbooks
+    - test/cookbooks
+    - test/integration (test kitchen)
+    - spec/cookbooks
+  - Resource matchers all follow the pattern "(action)_(resource_name)". ChefSpec will warn you of these deprecations in 3.0. They will be removed in 4.0. However, some resources cannot be automatically converted - **these resources will raise a deprecation exception of `ChefSpec::NoConversionError`**. The following matchers have changed:
+    - `execute_command` => `run_execute`
+    - `set_service_to_start_on_boot` => `enable_service`
+    - `create_file_with_content` => `render_file`
+    - `execute_(script)` => `run_(script)`
+    - `execute_ruby_block` => `run_ruby_block`
+    - `install_package_at_version` => `install_package().with(version: '')`
+    - `*_python_pip` => (removed - see "Packaging Custom LWRPs in the README")
+  - Remove dependency on Erubis
+  - Remove dependency on MiniTest Chef Handler
+  - Remove development dependency on Cucumber
+  - Remove development dependency on i18n
+  - Remove development dependency on simplecov
+  - Separate package matchers. In prior versions of ChefSpec, the `package` matcher would match on any kind of package and any kind of package action. However, some subclasses of the package resource do not support all actions. Each package provider now has it's own matcher with only the actions it supports. Prior specs that used the generic `package` matcher will no longer match on subclasses - you must use the specific subclass matcher.
+  - Separate file/cookbook_file/template matchers. In prior versions of ChefSpec, the `file` matcher would match on `file`, `cookbook_file`, and `template`. This is not ideal because it doesn't verify the correct message was sent. Now, `file`, `cookbook_file`, and `template` matchers will _only_ match resources of that type. For generic file checking, please use the new `render_file` matcher.
+  - Guards are now evaluated by default. If a shell guard is executed, it must first be stubbed with the `stub_command` macro.
+  - `Runner#resources` converted from an Array to a Hash. This is to ensure that all resource actions are added (when multiple calls to run_action exist (#201)). This also drastically improves resource lookup times.
+  - `Resource#actions` is no longer maniuplated. Instead, a new method `Resource#performed_actions` now keeps track of the actions taken on a resource (as well as the phase in which they were taken), preserving the original state of the resource.
+
+Features:
+  - Added a new `render_file` action to replace `create_file_with_content`. This matcher will render the contents of any file to a string and then optionally compare the result if given a `with` chainable.
+  - All resources now accept a `with` chainable for matching specific resource attributes.
+  - Windows attributes are now testable on non-Windows systems (like `inherits`)
+  - Added `batch` resource matchers
+  - Added `cookbook_file` resource matchers
+  - Added `deploy` resource matchers
+  - Added `erl_call` resource matchers
+  - Added `git` resource matchers
+  - Added `http_request` resource matchers
+  - Added `ifconfig` resource matchers
+  - Normalized `link` resource matchers
+  - Added `log` resource matchers
+  - Added `mdadm` resource matchers
+  - Added `mount` resource matchers
+  - Added `:immediate` and `:delayed` notification matchers
+  - Added `ohai` resource matchers
+  - Added `powershell_script` matchers (Chef 11.6+)
+  - Added `registry_key` matchers
+  - Added `remote_directory` matchers
+  - Added `route` matchers
+  - Added `subversion` matchers
+  - Added `stub_command` macro (formerly on `ChefSpec::ChefRunner`) for stubbbing the results of shell commands. Because shell commands are evaluated by default, ChefSpec will raise an exception when encountering a shell command that has not been stubbed.
+  - Added `stub_search` macro for easily stubbing `search` calls. Like shell commands, ChefSpec will raise an exception when encountering a `search` call that has not been stubbed.
+  - Added `stub_data_bag` macro for easily stubbing `data_bag` calls. Like shell commands, ChefSpec will raise an exception when encountering a `data_bag` call that has not been stubbed.
+  - Added `stub_data_bag_item` macro for easily stubbing` data_bag_item` calls. Like shell commands, ChefSpec will raise an exception when encountering a `data_bag_item` call that has not been stubbed.
+  - Added `stub_node` helper for quickly generating a node object from Fauxhai data
+  - Added `ChefSpec::Runner#apply` command to mimic the behavior of `chef-apply` (use with caution)
+  - Share the `ChefSpec::Runner` object with the Node object
+  - Add `chefspec/berkshelf` for easily integrating specs with Berkshelf (2 & 3)
+  - Add `.at_compile_time` and `.at_converge_time` matchers for asserting which phase of the Chef run a resource should be run
+
+Improvements:
+  - Move to inline documentation (Yard)
+  - Implement InProcess Aruba testing for ultra-fast tests
+  - Create "examples" directory for testing and demonstration
+  - Unified all failure_messages_for_should
+  - Use `shared_examples` for easily testing defined custom matchers
+  - Infer the `cookbook_path` from the calling spec
+  - Directly set node attributes with Fauxhai (formerly this was an O(n) operation)
+  - Refactored ExpectExpectation to work without stubbing
+  - Use Chef's `resource_collection` to identify resources instead of our own custom proxy
+
+
+## 2.0.1 (August 28, 2013)
+
+Bugfixes:
+
+  - Add missing second optional parameter to `Hash#respond_to?` monkey patch
+
+Features
+
+  - Improve error message when using a regular express
+  - Improve documentation for Python packages
+
+## 2.0.0 (August 22, 2013)
+
+Breaking:
+
+  - Remove support for REE ([@andrewgross][] for the CI)
+
+Bugfixes:
+
+  - Better failure message for `create_remote_file` ([@tmatilai][])
+  - Add `cookbook_file` as an accepted type to the `create_file` matchers ([@dafyddcrosby][])
+  - Ensure formatter is only registered once ([@student][])
+  - Signifant README updates ([@phoolish][])
+  - Fix `described_recipe` helper (S.R.Garcia)
+  - Refactor Chef 10/11 template rendering ([@sethvargo][])
+  - Fix CI ([@sethvargo][])
+  - Match File actions as an array ([@sethvargo][])
+
+Features:
+
+  - Extend `ruby_block` matcher to accept regular expressions ([@ssimeonov][])
+  - Add `create_remote_file_if_missing` matcher ([@jimhopp][])
+  - Extend `execute` matcher to accept regular expressions ([@eliaslevy][])
+  - Add ability to expect exceptions during a run ([@student][])
+  - Add regular expression support for resource names ([@mapleoin][])
+  - Add support for `python_pip` LWRP ([@mapleoin][])
+
+## 1.3.1 (June 10, 2013)
+
+Bugfixes:
+
+  - Allow the user to override `cookbook_path` again
+
+## 1.3.0 (June 6, 2013)
+
+Features:
+
+  - Added the ability to evaluate `only_if` and `not_if` conditional guards and
+    support for stubbing shell guards (#142, #144).
+  - New `described_recipe` and `described_cookbook` helpers to keep your specs
+    DRY (#140). Thanks Andrey Chernih.
+
+Bugfixes:
+
+  - Ensure that Ohai plugin reloader works with ChefSpec (#141). Thanks Andrey
+    Chernih.
+
+## 1.2.0 (May 16, 2013)
+
+Features:
+
+  - Add support for partial files (@RanjibDey)
+  - Automatically check certain directories for cookbooks (@sethvargo)
+
+## 1.1.0 (May 10, 2013)
+
+Features:
+
+  - Upgrade to newest version of fauxhai (@tmatilai)
+  - Make `find_resource` a public method (@adamhjk)
+  - Add path support (from fauxhai) (@RanjibDey)
+  - Custom Chef formatter for ChefSpec (removes pesky output) (@sethvargo)
+
+Bugfixes:
+
+  - Remove pesky output from Chef 11
+
+## 1.0.0 (April 22, 2013)
+
+Features:
+
+  - Add support for matching file content using Regular Expressions (@ketan)
+  - Add support for `ruby_block` matcher (Andrey Vorobiev)
+  - Use Fauxhai for node attributes (see 4529c10)
+  - Moved `test` and `development` into gemspec for transparency
+  - Improve message logging and testing (@tmatilai)
+  - Chef 11 compatibility (still Chef 10 compatible)
+  - Accept and document new RSpec "expect" syntax
+  - Attribute matchers for various providers (@bjoernalbers)
+  - Add execute_{bash,csh,perl,python,ruby}_script matchers ([@mlafeldt][])
+  - Add group and user resource matchers (@gildegoma)
+  - Add support for `yum_package` (Justin Witrick)
+  - Add ISSUES.md
+  - Add CONTRIBUTING.md
+  - Relax gemspec constraints (@juliandunn)
+  - Improve documentation and examples
+
+Bugfixes:
+
+  - Fix Rubygems/Bundler 2.0 compatibility issues
+  - Upgrade to newest RSpec
+  - Fix Chef 11 incompatibility
+  - Various documentation fixes
+
+## 0.9.0 (November 10, 2012)
+
+Features:
+
+  - Support added for the `user` resource (#6). Thanks Ranjib Dey.
+  - Support for making assertions about notifications added (#49). Thanks to
+    Ranjib Dey.
+  - New `include_recipe` matcher added (#50). Thanks Ranjib Dey.
+  - Support added for the Windows `env` resource (#51). Thanks Ranjib Dey.
+  - Convenience methods for common resources added to `ChefRunner` (#51).
+    Thanks Ranjib Dey.
+  - Further resource convenience methods added (#60). Thanks to Ketan
+    Padegaonkar.
+  - Support for the `:disable` action added to the service resource (#67).
+    Thanks to Chris Lundquist.
+  - Add a matcher to assert that a service is not running on boot (#58). Thanks
+    to Ketan Padegaonkar.
+  - Support added for the `chef_gem` resource (#74). Thanks to Jim Hopp.
+
+Bugfixes:
+
+  - Avoid failure if template path is not writable (#48). Thanks to Augusto
+    Becciu and Jim Hopp.
+  - Style fix for the README (#55). Thanks Greg Karékinian.
+  - Ensure notification assertions work where the resource name contains
+    brackets (#57). Thanks Sean Nolen.
+  - Unit tests updated to explicitly specify attribute precedence as required
+    from Chef 11 (#70). Thanks Mathias Lafeldt.
+  - Documentation added to the README for the `create_remote_file` matcher
+    (#71). Thanks Mathias Lafeldt.
+  - Clarify that `create_file_with_content` matches on partial content (#72).
+    Thanks Mathias Lafeldt.
+
+## 0.8.0 (September 14, 2012)
+
+Features:
+
+  - LWRP support added (#40). You can now make assertions about the resources
+    created by a provider by telling chefspec to [step into a provider
+    implementation](https://github.com/sethvargo/chefspec/tree/v0.8.0#writing-examples-for-lwrps).
+    Thanks to Augusto Becciu for implementing this feature.
+  - Updated for compatibility with Chef 10.14. Thanks Augusto Becciu.
+
+Bugfixes:
+
+  - Template paths are no longer hard-coded to live under `default` (#32).
+    Thanks Augusto Becciu.
+
+## 0.7.0 (August 27, 2012)
+
+Features:
+
+  - Cron resource support added (#33). Thanks Alexander Tamoykin.
+  - RSpec dependency
+    [bumped to 2.11](https://github.com/rspec/rspec-core/blob/b8197262d143294bf849ab58d1586d24537965ab/Changelog.md)
+    which has
+    [named subject](http://blog.davidchelimsky.net/2012/05/13/spec-smell-explicit-use-of-subject/)
+    support (#37). Thanks Doug Ireton.
+
+Bugfixes:
+
+  - Correctly infer the default `cookbook_path` on Windows (#38). Thanks Torben
+    Knerr.
+
+## 0.6.1 (June 21, 2012)
+
+Features:
+
+  - With the
+    [release of Chef 10.12.0](http://www.opscode.com/blog/2012/06/19/chef-10-12-0-released/)
+    the Chef versioning scheme has changed to make use of the major version
+    field. The constraint on chef is now optimistic. Thanks to Robert J. Berger
+    of Runa.com for flagging this issue (#28).
+
+## 0.6.0 (May 31, 2012)
+
+Features:
+
+  - Service matchers extended to add support for the `:nothing` and `:enabled`
+    actions. Thanks to Steve Lum (#20).
+  - Added mock value for `node['languages']` to prevent failure when loading
+    cookbooks that expect this attribute to have been populated by OHAI. Thanks
+    to Jim Hopp (#23).
+  - Matchers added for the `link` resource. Thanks to James Burgess (#25).
+  - Matchers added for the `remote_file` resource. Thanks to Matt Pruitt (#26).
+
+## 0.5.0 (February 20, 2012)
+
+Features:
+
+  - Thanks to Chris Griego and Morgan Nelson for these improvements:
+      - Support both arrays and raw symbols for actions in the file content matcher (#14).
+      - Add support for cookbook_file resources (#14).
+  - Support added for `gem_package` resources. Thanks to Jim Hopp from Lookout (#16).
+
+Bugfixes:
+
+  - Set the client_key to nil so that Chef::Search::Query.new doesn't raise (#14). Thanks Chris Griego and Morgan Nelson.
+
+## 0.4.0 (November 14, 2011)
+
+Features:
+
+  - Ruby 1.9.3 is now supported.
+  - The `create_file_with_content` matcher now matches on partial content (#13). This is an API behaviour change but
+  sufficiently minor and unlikely to break existing specs that I'm not bumping the major version. Thanks Chris Griego
+  and Morgan Nelson from getaroom.
+
+Bugfixes:
+
+  - Fixed a bug in the `install_package_at_version` matcher where it would error if the package action was not
+  explicitly specified (#13). Thanks Chris Griego and Morgan Nelson from getaroom.
+
+## 0.3.0 (October 2, 2011)
+
+Features:
+
+  - [Added new matcher](https://www.relishapp.com/sethvargo/chefspec/docs/write-examples-for-templates) `create_file_with_content` for verifying Chef `template` resource generated content.
+  - [Knife plugin](https://www.relishapp.com/sethvargo/chefspec/docs/generate-placeholder-examples) added to generate placeholder examples.
+
+## 0.2.1 (September 21, 2011)
+Bugfixes:
+
+  - Fixed typo in 0.2.0 botched release. Pro-tip: run your tests.
+
+## 0.2.0 (September 21, 2011)
+
+Features:
+
+  - Significantly improved performance by not invoking OHAI.
+  - ChefRunner constructor now accepts a block to set node attributes.
+  - ChefRunner constructor now takes an options hash.
+  - Converge now returns a reference to the ChefRunner to support calling converge in a let block.
+
+Bugfixes:
+
+  - Removed LWRP redefinition warnings.
+  - Reset run_list between calls to converge.
+  - Readable to_s output for failed specs.
+
+## 0.1.0 (August 9, 2011)
+
+Features:
+
+  - Support for Chef 0.1.x (#2)
+  - Support MRI 1.9.2 (#3)
+
+Bugfixes:
+
+  - Added specs.
+
+## 0.0.2 (July 31, 2011)
+
+Bugfixes:
+
+  - Corrected gem dependencies.
+
+## 0.0.1 (July 31, 2011)
+
+Initial version.
+
+- - -
+[@acrmp]: <https://github.com/acrmp> "Andrew Crump GitHub"
+[@andrewgross]: <https://github.com/andrewgross> "Andrew Gross's GitHub"
+[@bknowles]: <https://github.com/bknowles> "Brad Knowles's GitHub"
+[@dafyddcrosby]: <https://github.com/dafyddcrosby> "Dafydd Crosby's GitHub"
+[@DracoAter]: <https://github.com/DracoAter> "Juri Timošin's GitHub"
+[@eliaslevy]: <https://github.com/eliaslevy> "eliaslevy's GitHub"
+[@jimhopp]: <https://github.com/jimhopp> "Jim Hopp's GitHub"
+[@geraud]: <https://github.com/geraud> "Geraud Boyer's GitHub"
+[@mapleoin]: <https://github.com/mapleoin> "Ionuț Arțăriși's GitHub"
+[@mlafeldt]: <https://github.com/mlafeldt> "Mathias Lafeldt's GitHub"
+[@phoolish]: <https://github.com/phoolish> "phoolish's GitHub"
+[@ranjib]: <https://github.com/ranjib> "Ranjib Dey's GitHub"
+[@ryotarai]: <https://github.com/ryotarai> "Ryota Arai's GitHub"
+[@rteabeault]: <https://github.com/rteabeault> "rteabeault's GitHub"
+[@sethvargo]: <https://github.com/sethvargo> "Seth Vargo GitHub"
+[@ssimeonov]: <https://github.com/ssimeonov> "Simeon Simeonov's GitHub"
+[@student]: <https://github.com/student> "Nathan Zook's GitHub"
+[@tmatilai]: <https://github.com/tmatilai> "Teemu Matilainen's GitHub"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..488caef
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,52 @@
+Contributing to ChefSpec
+========================
+Pull requests are merged via Github, you can find the documentation about how to fork a repository and start contributing to ChefSpec here [https://help.github.com/articles/fork-a-repo](https://help.github.com/articles/fork-a-repo).
+
+All contributions are welcome to be submitted for review for inclusion, but before they will be accepted, we ask that you follow these simple steps:
+
+* [Coding standards](#coding-standards)
+* [Testing](#testing)
+* [Documentation](#documentation)
+
+Also, please be patient as not all items will be tested or reviewed immediately by the core team.
+
+Please be receptive and responsive to feedback about your additions or changes. The core team and/or other community members may make suggestions or ask questions about your change. This is part of the review process, and helps everyone to understand what is happening, why it is happening, and potentially optimizes your code.
+
+If you're looking to contribute but aren't sure where to start, check out the [open issues](https://github.com/sethvargo/chefspec/issues?state=open).
+
+
+Will Not Merge
+--------------
+This section details, specifically, Pull Requests or features that will _not_ be merged:
+
+1. Matchers for non-Chef core resources. ChefSpec 3.0 introduced a way for cookbook maintainers to [package matchers _with_ their cookbooks](https://github.com/sethvargo/chefspec#packaging-custom-matchers) at distribution time.
+2. New features without accompanying unit tests, cucumber tests, and documentation.
+
+
+Coding Standards
+----------------
+The submitted code should be compatible with the standard Ruby coding guidelines. Here are some additional resources:
+
+ * [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide)
+ * [Github Styleguide](https://github.com/styleguide/ruby)
+
+There is a tool called [Cane](https://github.com/square/cane) that allows you to validate your code's ABC complexity and documentation.
+
+
+Testing
+-------
+Whether your pull request is a bug fix or introduces new classes or methods to the project, we kindly ask that you include tests for your changes. Even if it's just a small improvement, a test is necessary to ensure the bug is never re-introduced.
+
+We understand that not all users submitting pull requests will be proficient with RSpec. The maintainers and community as a whole are a helpful group and can help you with writing tests. The [Better Specs](http://betterspecs.org/) site should provide some helpful resources to get you started.
+
+ChefSpec is tested on [Travis CI](https://travis-ci.org/sethvargo/chefspec) against multiple Chef Versions and Ruby Versions. **Your patches must work for all Chef and Ruby Versions on Travis.** This is in an effort to maintain backward compatibility as long as possible. For more information on which Chef and Ruby versions to support, checkout the [`.travis.yml`](https://github.com/sethvargo/chefspec/blob/master/.travis.yml) file.
+
+
+Documentation
+-------------
+Documentation is a crucial part to ChefSpec, especially given its broad depth of features. All documentation is placed inline on the method matcher so it can be generated with Yard. Please see existing matchers for an example and check out the [Yard documentation](http://yardoc.info)
+
+When contributing new features, please ensure adequate documentation and examples are present.
+
+---
+This contributing guide is based off of the [Joomla Contributing Guide](https://raw.github.com/joomla/joomla-framework/master/CONTRIBUTING.markdown).
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..851fabc
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,2 @@
+source 'https://rubygems.org'
+gemspec
diff --git a/ISSUES.md b/ISSUES.md
new file mode 100644
index 0000000..6b7de45
--- /dev/null
+++ b/ISSUES.md
@@ -0,0 +1,41 @@
+ChefSpec Issues
+---------------
+This file documents the steps necessary to report and issue with ChefSpec. Following these guidelines will help ensure your issue is resolved in a timely manner.
+
+Reporting
+---------
+When you report an issue, please include the following information:
+
+- What you're trying to accomplish
+- An [SSCCE](http://sscce.org/)
+- The command you ran
+- What you expected to happen
+- What actually happened
+- The exception backtrace(s), if any
+- What operating system and version
+- Everything output by running `env`
+- What version of Ruby you are using (run `ruby -v`)
+- What version of Rubygems you are using (run `gem -v`)
+- What version of Chef you are using (run `knife -v`)
+
+Here's a snippet you can copy-paste into the issue and fill out:
+
+```text
+(What is the issue? What are you trying to do? What happened?)
+
+- Command: `...`
+- OS:
+- Ruby:
+- Rubygems:
+- Chef:
+- env:
+    ```text
+    # Paste your env here
+    ```
+- Backtrace:
+    ```text
+    # Paste backtrace here
+    ```
+```
+
+[Create a ticket](https://github.com/sethvargo/chefspec/issues/new) describing your problem and include the information above.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d6cee1e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (C) 2011 by Andrew Crump
+Copyright (C) 2013 by Seth Vargo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..46909c9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1038 @@
+ChefSpec
+========
+[![Gem Version](http://img.shields.io/gem/v/chefspec.svg)][gem]
+[![Build Status](http://img.shields.io/travis/sethvargo/chefspec.svg)][travis]
+
+[gem]: https://rubygems.org/gems/chefspec
+[travis]: http://travis-ci.org/sethvargo/chefspec
+
+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.
+
+ChefSpec runs your cookbook(s) locally with Chef Solo without actually converging a node. This has two primary benefits:
+
+- It's really fast!
+- Your tests can vary node attributes, operating systems, and search results to assert behavior under varying conditions.
+
+
+What people are saying
+----------------------
+> I just wanted to drop you a line to say "HELL YES!" to ChefSpec. - [Joe Goggins](https://twitter.com/jgoggins)
+
+> OK ChefSpec is my new best friend. Delightful few hours working with it. - [Michael Ivey](https://twitter.com/ivey)
+
+**Chat with us - [#chefspec](irc://irc.freenode.net/chefspec) on Freenode**
+
+
+Important Notes
+---------------
+- **ChefSpec 3.0+ requires Ruby 1.9 or higher!**
+- **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!**
+- **Each resource matcher is self-documented using [Yard](http://rubydoc.info/github/sethvargo/chefspec) and has a corresponding aruba test from the [examples directory](https://github.com/sethvargo/chefspec/tree/master/examples).**
+- **ChefSpec aims to maintain compatability with 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.
+
+
+Notes on Compatability with Chef Versions
+-----------------------------------------
+As a general rule, if it is tested in the Travis CI matrix, it is a supported version. The section below details any specific versions that are _not_ supported and why:
+
+- Chef 12 prior to Chef 12.0.2 is not supported due to the lack of a declared resource type. This was fixed in [Chef 12.0.2](https://github.com/chef/chef/blob/12.0.2/lib/chef/resource.rb#L422-428).
+
+Additionally, if you look at a cucumber feature and see a tag like `@not_chef_x_y_z`, that means that particular functionality is not supported on those versions of Chef.
+
+
+Writing a Cookbook Example
+--------------------------
+If you want `knife` to automatically generate spec stubs for you, install [knife-spec](https://github.com/sethvargo/knife-spec).
+
+Given an extremely basic Chef recipe that just installs an operating system package:
+
+```ruby
+package 'foo'
+```
+
+the associated ChefSpec test might look like:
+
+```ruby
+require 'chefspec'
+
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs foo' do
+    expect(chef_run).to install_package('foo')
+  end
+end
+```
+
+Let's step through this file to see what is happening:
+
+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.
+1. 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.
+1. The `let` block on creates the `ChefSpec:Runner` and 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.
+1. The `described_recipe` macro is a ChefSpec helper method that infers the recipe from the `describe` block. Alternatively you could specify the recipe directly.
+1. 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.
+
+
+Configuration
+-------------
+ChefSpec exposes a configuration layer at the global level and at the `Runner` level. The following settings are available:
+
+```ruby
+RSpec.configure do |config|
+  # Specify the path for Chef Solo to find cookbooks (default: [inferred from
+  # the location of the calling spec file])
+  config.cookbook_path = '/var/cookbooks'
+
+  # Specify the path for Chef Solo to find roles (default: [ascending search])
+  config.role_path = '/var/roles'
+
+  # Specify the path for Chef Solo to find environments (default: [ascending search])
+  config.environment_path = '/var/environments'
+
+  # Specify the path for Chef Solo file cache path (default: nil)
+  config.file_cache_path = '/var/chef/cache'
+
+  # Specify the Chef log_level (default: :warn)
+  config.log_level = :debug
+
+  # Specify the path to a local JSON file with Ohai data (default: nil)
+  config.path = 'ohai.json'
+
+  # Specify the operating platform to mock Ohai data from (default: nil)
+  config.platform = 'ubuntu'
+
+  # Specify the operating version to mock Ohai data from (default: nil)
+  config.version = '12.04'
+end
+```
+
+Values specified at the initialization of a "Runner" merge and take precedence over any global settings:
+
+```ruby
+# Override only the operating system version (platform is still "ubuntu" from above)
+ChefSpec::SoloRunner.new(version: '10.04')
+
+# Use a different operating system platform and version
+ChefSpec::SoloRunner.new(platform: 'centos', version: '5.10')
+
+# Specify a different cookbook_path
+ChefSpec::SoloRunner.new(cookbook_path: '/var/my/other/path', role_path: '/var/my/roles')
+
+# By default ChefSpec sets a new temporary directory for file caching in every run.
+# This can be overridden by passing the `file_cache_path` option.
+# Note: Resources containing `Chef::Config[:file_cache_path]` in their name or
+# attributes, will fail unless this option is specified.
+ChefSpec::SoloRunner.new(file_cache_path: '/var/chef/cache')
+
+# Add debug log output
+ChefSpec::SoloRunner.new(log_level: :debug).converge(described_recipe)
+```
+
+**NOTE** You do not _need_ to specify a platform and version to use ChefSpec. However, some cookbooks may rely on [Ohai](http://github.com/opscode/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/customink/fauxhai).
+
+### Berkshelf
+If you are using Berkshelf, simply require `chefspec/berkshelf` in your `spec_helper` after requiring `chefspec`:
+
+```ruby
+# spec_helper.rb
+require 'chefspec'
+require 'chefspec/berkshelf'
+```
+
+Requiring this file will:
+
+- Create a temporary working directory
+- Download all the dependencies listed in your `Berksfile` into the temporary directory
+- Set ChefSpec's `cookbook_path` to the temporary directory
+
+### Librarian
+
+If you are using Librarian, simply require `chefspec/librarian` in your `spec_helper` after requiring `chefspec`:
+
+```ruby
+# spec_helper.rb
+require 'chefspec'
+require 'chefspec/librarian'
+```
+
+Requiring this file will:
+
+- Create a temporary working directory
+- Download all the dependencies listed in your `Cheffile` into the temporary directory
+- Set ChefSpec's `cookbook_path` to the temporary directory
+
+**NOTE** In order to test the cookbook in the current working directory, you
+have to write your `Cheffile` like this:
+
+```ruby
+# Cheffile
+site 'https://supermarket.chef.io/api/v1'
+
+cookbook 'name_of_your_cookbook', path: '.'
+```
+
+
+Running Specs
+-------------
+ChefSpec is actually an RSpec extension, so you can run your tests using the RSpec CLI:
+
+```bash
+$ rspec
+```
+
+You can also specify a specific spec to run and various RSpec command line options:
+
+```bash
+$ rspec spec/unit/recipes/default_spec.rb --color
+```
+
+For more information on the RSpec CLI, please see the [documentation](https://relishapp.com/rspec/rspec-core/docs/command-line).
+
+
+Making Assertions
+-----------------
+ChefSpec asserts that resource actions have been performed. In general, ChefSpec follows the following pattern:
+
+```ruby
+require 'chefspec'
+
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'does something' do
+    expect(chef_run).to ACTION_RESOURCE(NAME)
+  end
+end
+```
+
+where:
+
+- _ACTION_ - the action on the resource (e.g. `install`)
+- _RESOURCE_ - the name of the resource (e.g. `package`)
+- _NAME_ - the name attribute for the resource (e.g. `apache2`)
+
+**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.
+
+Here's a more concrete example:
+
+```ruby
+require 'chefspec'
+
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'does something' do
+    expect(chef_run).to install_package('apache2')
+  end
+end
+```
+
+This test is asserting that the Chef run will have a _package_ resource with the name _apache2_ with an action of _install_.
+
+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:
+
+```ruby
+require 'chefspec'
+
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'does something' do
+    expect(chef_run).to modify_group('docker').with(members: ['vagrant'])
+  end
+end
+```
+
+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'] }`.
+
+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/sethvargo/chefspec) and has a corresponding cucumber test from the [examples directory](https://github.com/sethvargo/chefspec/tree/master/examples).
+
+Additionally, ChefSpec includes the following helpful matchers. They are also [documented in Yard](http://rubydoc.info/github/sethvargo/chefspec), but they are included here because they do not follow the "general pattern".
+
+##### include_recipe
+Assert that the Chef run included a recipe from another cookbook
+
+```ruby
+expect(chef_run).to include_recipe('other_cookbook::recipe')
+```
+
+Keep the resources from an included recipe from being loaded into the Chef run, but test that the recipe was included
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  before do
+    allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).and_call_original
+    allow_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('other_cookbook::default')
+  end
+
+  it 'includes the other_cookbook' do
+    expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('other_cookbook::default')
+    chef_run
+  end
+end
+```
+
+##### notify
+Assert that a resource notifies another in the Chef run
+
+```ruby
+resource = chef_run.template('/etc/foo')
+expect(resource).to notify('service[apache2]').to(:restart).immediately
+```
+
+##### subscribes
+Assert that a resource subscribes to another in the Chef run
+
+```ruby
+resource = chef_run.service('apache2')
+expect(resource).to subscribe_to('template[/etc/foo]').on(:create).delayed
+```
+
+##### render_file
+Assert that the Chef run renders a file (with optional content); this will match `cookbook_file`, `file`, and `template` resources and can also check the resulting content
+
+```ruby
+expect(chef_run).to render_file('/etc/foo')
+expect(chef_run).to render_file('/etc/foo').with_content('This is content')
+expect(chef_run).to render_file('/etc/foo').with_content(/regex works too.+/)
+expect(chef_run).to render_file('/etc/foo').with_content { |content|
+  # Regular RSpec matches work in here
+  expect(content).to include('any RSpec matcher')
+}
+```
+
+You can use any RSpec content matcher inside of the `with_content` predicate:
+
+```ruby
+expect(chef_run).to render_file('/etc/foo').with_content(start_with('# First line'))
+```
+
+It is possible to assert which [Chef phase of execution](http://docs.opscode.com/essentials_nodes_chef_run.html) a resource is created. Given a resource that is installed at compile time using `run_action`:
+
+```ruby
+package('apache2').run_action(:install)
+```
+
+You can assert that this package is installed during runtime using the `.at_compile_time` predicate on the resource matcher:
+
+```ruby
+expect(chef_run).to install_package('apache2').at_compile_time
+```
+
+Similarly, you can assert that a resource is executed during convergence time:
+
+```ruby
+expect(chef_run).to install_package('apache2').at_converge_time
+```
+
+Since "converge time" is the default behavior for all recipes, this test might be redundant and the predicate could be dropped depending on your situation.
+
+##### do_nothing
+Assert that a resource performs no action
+
+```ruby
+resource = chef_run.execute('install')
+expect(resource).to do_nothing
+```
+
+**For more complex examples, please see the [examples directory](https://github.com/sethvargo/chefspec/tree/master/examples) or the [Yard documentation](http://rubydoc.info/github/sethvargo/chefspec).**
+
+
+Setting node Attributes
+-----------------------
+Node attribute can be set when creating the `Runner`. The initializer yields a block that gives full access to the node object:
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) do
+    ChefSpec::SoloRunner.new do |node|
+      node.set['cookbook']['attribute'] = 'hello'
+    end.converge(described_recipe)
+  end
+end
+```
+
+### Automatic attributes
+ChefSpec provides mocked automatic Ohai data using [fauxhai](https://github.com/customink/fauxhai). To mock out `automatic` attributes, you must use the `automatic` key:
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) do
+    ChefSpec::SoloRunner.new do |node|
+      node.automatic['memory']['total'] = '512kB'
+    end.converge(described_recipe)
+  end
+end
+```
+
+The `node` that is returned is actually a [`Chef::Node`](http://docs.opscode.com/essentials_node_object.html) object.
+
+To set an attribute within a specific test, set the attribute in the `it` block and then **(re-)converge the node**:
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.new } # Notice we don't converge here
+
+  it 'performs the action' do
+    chef_run.node.set['cookbook']['attribute'] = 'hello'
+    chef_run.converge(described_recipe) # The converge happens inside the test
+
+    expect(chef_run).to do_something
+  end
+end
+```
+
+Using a Chef Server
+-------------------
+All the examples thus far have used the `ChefSpec::SoloRunner`, which runs ChefSpec in Chef Solo mode. ChefSpec also includes the ability to create in-memory Chef Servers. This server can be populated with fake data and used to test search, data bags, and other "server-only" features.
+
+To use the ChefSpec server, simply replace `ChefSpec::SoloRunner` with `ChefSpec::ServerRunner`:
+
+```diff
+describe 'example::default' do
+-  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
++  let(:chef_run) { ChefSpec::ServerRunner.converge(described_recipe) }
+end
+```
+
+This will automatically create a Chef Server, synchronize all the cookbooks in your `cookbook_path`, and wire all the internals of Chef together. Recipe calls to `search`, `data_bag` and `data_bag_item` will now query this ChefSpec server.
+
+### DSL
+The ChefSpec server includes a collection of helpful DSL methods for populating data into the Chef Server.
+
+Create a client:
+
+```ruby
+ChefSpec::ServerRunner.new do |node, server|
+  server.create_client('my_client', { admin: true })
+end
+```
+
+Create a data bag (and items):
+
+```ruby
+ChefSpec::ServerRunner.new do |node, server|
+  server.create_data_bag('my_data_bag', {
+    'item_1' => {
+      'password' => 'abc123'
+    },
+    'item_2' => {
+      'password' => 'def456'
+    }
+  })
+end
+```
+
+Create an environment:
+
+```ruby
+ChefSpec::ServerRunner.new do |node, server|
+  server.create_environment('my_environment', { description: '...' })
+end
+```
+
+Create a node:
+
+```ruby
+ChefSpec::ServerRunner.new do |node, server|
+  server.create_node('my_node', { run_list: ['...'] })
+end
+```
+
+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:
+
+```ruby
+ChefSpec::ServerRunner.new do |node, server|
+  node.set['attribute'] = 'value'
+
+  # At this point, the server already has a copy of the current node object due
+  # to the way Chef compiled the resources. However, that node does not have
+  # this new value. As such, you must "save" the node back to the server to
+  # persist this attribute update.
+  server.update_node(node)
+end
+```
+
+You may also use the `stub_node` macro, which will create a new `Chef::Node` object and accepts the same parameters as the Chef Runner and a Fauxhai object:
+
+```ruby
+www = stub_node(platform: 'ubuntu', version: '12.04') do |node|
+        node.set['attribute'] = 'value'
+      end
+
+# `www` is now a local Chef::Node object you can use in your test. To publish
+# this node to the server, call `create_node`:
+
+ChefSpec::ServerRunner.new do |node, server|
+  server.create_node(www)
+end
+```
+
+Create a role:
+
+```ruby
+ChefSpec::ServerRunner.new do |node, server|
+  server.create_role('my_role', { default_attributes: {} })
+end
+```
+
+**NOTE** The ChefSpec server is empty at the start of each example to avoid interdependent tests.
+
+
+Stubbing
+--------
+### Command
+Given a recipe with shell guard:
+
+```ruby
+template '/tmp/foo.txt' do
+  not_if 'grep text /tmp/foo.txt'
+end
+```
+
+ChefSpec will raise an error like:
+
+```text
+Real commands are disabled. Unregistered command: `grep text /tmp/foo.txt`
+
+You can stub this command with:
+
+ stub_command("grep text /tmp/foo.txt").and_return(true)
+
+============================================================
+```
+
+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.
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.new }
+
+  before do
+    stub_command("grep text /tmp/foo.txt").and_return(true)
+  end
+end
+```
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.new }
+
+  before do
+    stub_command("grep text /tmp/foo.txt") { rand(50)%2 == 0 }
+  end
+end
+```
+
+### Data Bag & Data Bag Item
+**NOTE** This is not required if you are using a ChefSpec server.
+
+Given a recipe that executes a `data_bag` method:
+
+```ruby
+data_bag('users').each do |user|
+  data_bag_item('users', user['id'])
+end
+```
+
+ChefSpec will raise an error like:
+
+```text
+Real data_bags are disabled. Unregistered data_bag: data_bag(:users)
+
+You can stub this data_bag with:
+
+  stub_data_bag("users").and_return([])
+
+============================================================
+```
+
+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.
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.new }
+
+  before do
+    stub_data_bag('users').and_return([])
+  end
+end
+```
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.new }
+
+  before do
+    stub_data_bag('users').and_return(['svargo', 'francis'])
+
+    stub_data_bag_item('users', 'svargo').and_return({ ... })
+    stub_data_bag_item('users', 'francis') { (ruby code) }
+  end
+end
+```
+
+If you are using **Encrypted Data Bag Items**, you'll need to dive into the RSpec layer and stub that class method instead:
+
+```ruby
+describe 'example::default' do
+  before do
+    allow(Chef::EncryptedDataBagItem).to receive(:load).with('users', 'svargo').and_return(...)
+  end
+end
+```
+
+### Search
+**NOTE** This is not required if you are using a ChefSpec server.
+
+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:
+
+```text
+Real searches are disabled. Unregistered search: search(:node, 'name:hello')
+
+You can stub this search with:
+
+  stub_search(:node, 'name:hello') {  }
+
+============================================================
+```
+
+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.
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.new }
+
+  before do
+    stub_search(:node, 'name:hello').and_return([])
+  end
+end
+```
+
+```ruby
+describe 'example::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.new }
+
+  before do
+    stub_search(:node, 'name:hello') { (ruby_code) }
+  end
+end
+```
+
+
+Reporting
+---------
+ChefSpec can generate a report of resources read over resources tested.
+
+To generate the coverage report, add the following to your `spec_helper.rb` before you require any "Chef" code:
+
+```ruby
+require 'chefspec'
+ChefSpec::Coverage.start!
+
+# Existing spec_helper contents...
+```
+
+By default, that method will output helpful information to standard out:
+
+```text
+ChefSpec Coverage report generated...
+
+  Total Resources:   6
+  Touched Resources: 1
+  Touch Coverage:    16.67%
+
+Untouched Resources:
+
+  package[git]               bacon/recipes/default.rb:2
+  package[build-essential]   bacon/recipes/default.rb:3
+  package[apache2]           bacon/recipes/default.rb:4
+  package[libvrt]            bacon/recipes/default.rb:5
+  package[core]              bacon/recipes/default.rb:6
+```
+
+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 desireable. To restrict coverage reporting against certain cookbooks, `ChefSpec::Coverage` yields a block:
+
+```ruby
+ChefSpec::Coverage.start! do
+  add_filter 'vendor/cookbooks'
+end
+```
+
+The `add_filter` method accepts a variety of objects. For example:
+
+```ruby
+ChefSpec::Coverage.start! do
+  # Strings are interpreted as file paths, with a forward anchor
+  add_filter 'vendor/cookbooks'
+
+  # Regular expressions must be escaped, but provide a nicer API for negative
+  # back tracking
+  add_filter /cookbooks\/(?!omnibus)/
+
+  # Custom block filters yield a {Chef::Resource} object - if the block
+  # evaluates to true, it will be filtered
+  add_filter do |resource|
+    # Bob's cookbook's are completely untested! Ignore them until he gets his
+    # shit together.
+    resource.source_file =~ /cookbooks\/bob-(.+)/
+  end
+end
+```
+
+For more complex scenarios, you can create a custom `Filter` object that inherits from `ChefSpec::Coverage::Filter` and implements the `matches?` method.
+
+```ruby
+class CustomFilter < ChefSpec::Coverage::Filter
+  def initialize(arg1, arg2, &block)
+    # Create a custom initialization method, do some magic, etc.
+  end
+
+  def matches?(resource)
+    # Custom matching logic in here - anything that evaluates to "true" will be
+    # filtered.
+  end
+end
+
+ChefSpec::Coverage.start! do
+  add_filter CustomFilter.new('foo', :bar)
+end
+```
+
+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:
+
+```ruby
+ChefSpec::Coverage.start! do
+  filters.clear
+
+  # Add your custom filters now
+end
+```
+
+
+Mocking Out Environments
+------------------------
+If you want to mock out `node.chef_environment`, you'll need to use RSpec mocks/stubs twice:
+
+```ruby
+let(:chef_run) do
+  ChefSpec::SoloRunner.new do |node|
+    # Create a new environment (you could also use a different :let block or :before block)
+    env = Chef::Environment.new
+    env.name 'staging'
+
+    # Stub the node to return this environment
+    allow(node).to receive(:chef_environment).and_return(env.name)
+
+    # Stub any calls to Environment.load to return this environment
+    allow(Chef::Environment).to receive(:load).and_return(env)
+  end.converge('cookbook::recipe')
+end
+```
+
+**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 :)**
+
+
+Testing LWRPs
+-------------
+**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 (`-`).
+
+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.
+
+In order to run the actions exposed by your LWRP, you have to explicitly tell the `Runner` to step into it:
+
+```ruby
+require 'chefspec'
+
+describe 'foo::default' do
+  let(:chef_run) do
+    ChefSpec::SoloRunner.new(step_into: ['my_lwrp']).converge('foo::default')
+  end
+
+  it 'installs the foo package through my_lwrp' do
+    expect(chef_run).to install_package('foo')
+  end
+end
+```
+
+**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!**
+
+
+Packaging Custom Matchers
+-------------------------
+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`:
+
+```ruby
+# cookbook/libraries/matchers.rb
+
+if defined?(ChefSpec)
+  def my_custom_matcher(resource_name)
+    ChefSpec::Matchers::ResourceMatcher.new(resource, action, resource_name)
+  end
+end
+```
+
+1. The entire contents of this file must be wrapped with the conditional clause checking if `ChefSpec` is defined.
+2. Each matcher is actually a top-level method. The above example corresponds to the following RSpec test:
+    ```ruby
+    expect(chef_run).to my_custom_matcher('...')
+    ```
+
+3. `ChefSpec::Matchers::ResourceMatcher` accepts three parameters:
+    1. The name of the resource to find in the resource collection (i.e. the name of the LWRP).
+    2. The action that resource should receive.
+    3. The value of the name attribute of the resource to find. (This is typically proxied as the value from the matcher definition.)
+
+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.
+
+#### Example
+Suppose I have a cookbook named "motd" with a resource/provider "message".
+
+```ruby
+# motd/resources/message.rb
+actions :write
+default_action :write
+
+attribute :message, name_attribute: true
+```
+
+```ruby
+# motd/providers/message.rb
+action :write do
+  # ...
+end
+```
+
+Chef will dynamically build the `motd_message` LWRP at runtime that can be used in the recipe DSL:
+
+```ruby
+motd_message 'my message'
+```
+
+You can package a custom ChefSpec matcher with the motd cookbook by including the following code in `libraries/matchers.rb`:
+
+```ruby
+# motd/libraries/matchers.rb
+if defined?(ChefSpec)
+  def write_motd_message(message)
+    ChefSpec::Matchers::ResourceMatcher.new(:motd_message, :write, message)
+  end
+end
+```
+
+Other developers can write RSpec tests against your LWRP in their cookbooks:
+
+```ruby
+expect(chef_run).to write_motd_message('my message')
+```
+
+**Don't forget to include documentation in your cookbook's README noting the custom matcher and its API!**
+
+
+Writing Custom Matchers
+-----------------------
+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:
+
+```ruby
+# spec/support/matchers.rb
+def my_custom_matcher(resource_name)
+  ChefSpec::Matchers::ResourceMatcher.new(:resource, :action, resource_name)
+end
+```
+
+Then require this file in your `spec_helper.rb` so the matcher can be used:
+
+```ruby
+require_relative 'support/matchers'
+```
+
+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).
+
+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.
+
+```ruby
+# matchers.rb
+ChefSpec.define_matcher :my_custom_resource
+```
+
+And then in your spec suite, you can obtain the custom resource for assertions:
+
+```ruby
+let(:chef_run) { ChefSpec::SoloRunner.converge('...') }
+
+it 'notifies the thing' do
+  custom = chef_run.my_custom_resource('name')
+  expect(custom).to notify('service[apache2]').to(:restart).immediately
+end
+```
+
+
+Expecting Exceptions
+--------------------
+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:
+
+```text
+================================================================================
+Recipe Compile Error in apt_package/recipes/install.rb
+================================================================================
+
+RuntimeError
+------------
+RuntimeError
+
+Cookbook Trace:
+---------------
+  .../apt_package/recipes/install.rb:1:in `from_file'
+  .../apt_package/spec/install_spec.rb:4:in `block (2 levels) in <top (required)>'
+  .../apt_package/spec/install_spec.rb:7:in `block (2 levels) in <top (required)>'
+
+Relevant File Content:
+----------------------
+.../apt_package/recipes/install.rb:
+
+  1>> raise RuntimeError
+  2:
+  3:  apt_package 'default_action'
+```
+
+This output is automatically silenced when using RSpec's `raise_error` matcher:
+
+```ruby
+let(:chef_run) { ChefSpec::SoloRunner.converge('cookbook::recipe') }
+
+it 'raises an error' do
+  expect {
+    chef_run
+  }.to raise_error
+end
+```
+
+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.
+
+```ruby
+let(:chef_run) { ChefSpec::SoloRunner.converge('cookbook::recipe') }
+
+it 'raises an error' do
+  expect {
+    chef_run
+  }.to raise_error(RuntimeError)
+end
+```
+
+Testing Roles
+-------------
+Even though ChefSpec is cookbook-centric, you can still converge multiple recipes and roles in a single `ChefSpec::SoloRunner` instance. Given a cookbook "bacon" with a default recipe:
+
+```ruby
+# cookbooks/bacon/recipes/default.rb
+package 'foo'
+```
+
+and a default attributes file:
+
+```ruby
+# cookbooks/bacon/attributes/default.rb
+default['bacon']['temperature'] = 200
+```
+
+and a role "breakfast":
+
+```ruby
+# roles/breakfast.rb
+default_attributes(
+  'bacon' => {
+    'temperature' => 150 # NOTE: This is different from the default value
+  }
+)
+run_list([
+  'recipe[bacon::default]'
+])
+```
+
+You can test that the role is appropriately applied by telling the `ChefSpec::SoloRunner` to converge on the _role_ instead of a recipe:
+
+```ruby
+let(:chef_run) { ChefSpec::SoloRunner.converge('role[breakfast]') }
+```
+
+Assert that the run_list is properly expanded:
+
+```ruby
+expect(chef_run).to include_recipe('bacon::default')
+```
+
+Assert that the correct attribute is used:
+
+```ruby
+expect(chef_run.node['bacon']['temperature']).to eq(150)
+```
+
+**NOTE** If your roles live somewhere outside of the expected path, you must set `RSpec.config.role_path` to point to the directory containing your roles **before** invoking the `#converge` method!
+
+```ruby
+RSpec.configure do |config|
+  config.role_path = '/var/my/roles' # global setting
+end
+
+# - OR -
+
+ChefSpec::SoloRunner.new(role_path: '/var/my/roles') # local setting
+```
+
+
+Faster Specs
+------------
+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.
+
+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`:
+
+```ruby
+# before
+let(:chef_run) { ChefSpec::SoloRunner.new }
+
+# after
+cached(:chef_run) { ChefSpec::SoloRunner.new }
+```
+
+Everything else should work the same. Be advised, as the method name suggests, this will cache the results of your Chef Client Run for the **entire RSpec example**. This makes stubbing more of a challenge, since the node is already converged. For more information, please see [Juri Timošin's blog post on faster specs](http://dracoater.blogspot.com/2013/12/testing-chef-cookbooks-part-25-speeding.html) as well as the discussion in [#275](https://github.com/sethvargo/chefspec/issues/275).
+
+
+Media & Third-party Tutorials
+-----------------------------
+- [CustomInk's Testing Chef Cookbooks](http://technology.customink.com/blog/2012/08/03/testing-chef-cookbooks/)
+- [Jake Vanderdray's Practical ChefSpec](http://files.meetup.com/1780846/ChefSpec.pdf)
+- [Jim Hopp's excellent Test Driven Development for Chef Practitioners](http://www.youtube.com/watch?v=o2e0aZUAVGw)
+- [Joshua Timberman's Starting ChefSpec Examples](http://jtimberman.housepub.org/blog/2013/05/09/starting-chefspec-example/)
+- [Juri Timošin's post on faster specs](http://dracoater.blogspot.com/2013/12/testing-chef-cookbooks-part-25-speeding.html)
+- [Seth Vargo's Chef recipe code coverage](https://sethvargo.com/chef-recipe-code-coverage/)
+- [Seth Vargo's TDDing tmux talk](http://www.confreaks.com/videos/2364-mwrc2013-tdding-tmux)
+- [Stephen Nelson Smith's Test-Driven Infrastructure with Chef](http://shop.oreilly.com/product/0636920030973.do)
+
+
+Development
+-----------
+1. Fork the repository from GitHub.
+2. Clone your fork to your local machine:
+
+        $ git clone git at github.com:USER/chefspec.git
+
+3. Create a git branch
+
+        $ git checkout -b my_bug_fix
+
+4. **Write tests**
+5. Make your changes/patches/fixes, committing appropriately
+6. Run the tests: `bundle exec rake`
+7. Push your changes to GitHub
+8. Open a Pull Request
+
+ChefSpec is on [Travis CI](http://travis-ci.org/sethvargo/chefspec) which tests against multiple Chef and Ruby versions.
+
+If you are contributing, please see the [Contributing Guidelines](https://github.com/sethvargo/chefspec/blob/master/CONTRIBUTING.md) for more information.
+
+
+License
+-------
+MIT - see the accompanying [LICENSE](https://github.com/sethvargo/chefspec/blob/master/LICENSE) file for details.
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..ffc957c
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,29 @@
+require 'bundler/gem_tasks'
+require 'cucumber/rake/task'
+require 'rspec/core/rake_task'
+require 'yard/rake/yardoc_task'
+
+require 'chef/version'
+
+YARD::Rake::YardocTask.new
+
+RSpec::Core::RakeTask.new(:unit) do |t|
+  t.rspec_opts = [].tap do |a|
+    a.push('--color')
+    a.push('--format progress')
+  end.join(' ')
+end
+
+Cucumber::Rake::Task.new(:acceptance) do |t|
+  t.cucumber_opts = [].tap do |a|
+    a.push('--color')
+    a.push('--format progress')
+    a.push('--strict')
+    a.push('--tags ~@not_chef_' + Chef::VERSION.gsub('.', '_'))
+  end.join(' ')
+end
+
+desc 'Run all tests'
+task :test => [:unit, :acceptance]
+
+task :default => [:test]
diff --git a/chefspec.gemspec b/chefspec.gemspec
new file mode 100644
index 0000000..2baec34
--- /dev/null
+++ b/chefspec.gemspec
@@ -0,0 +1,40 @@
+lib = File.expand_path('../lib/', __FILE__)
+$:.unshift lib unless $:.include?(lib)
+require 'chefspec/version'
+
+Gem::Specification.new do |s|
+  s.name          = 'chefspec'
+  s.version       = ChefSpec::VERSION
+  s.authors       = ['Andrew Crump', 'Seth Vargo']
+  s.email         = ['andrew.crump at ieee.org', 'sethvargo at gmail.com']
+  s.summary       = 'Write RSpec examples and generate coverage reports for ' \
+                    'Chef recipes!'
+  s.description   = 'ChefSpec is a unit testing and resource coverage ' \
+                    '(code coverage) 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.'
+  s.homepage      = 'https://sethvargo.github.io/chefspec/'
+  s.license       = 'MIT'
+
+  # Packaging
+  s.files         = `git ls-files`.split($/)
+  s.executables   = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
+  s.test_files    = s.files.grep(%r{^(test|spec|features)/})
+  s.require_paths = ['lib']
+
+  # ChefSpec requires Ruby 1.9+
+  s.required_ruby_version = '>= 1.9'
+
+  s.add_dependency 'chef',    '>= 11.14'
+  s.add_dependency 'fauxhai', '~> 2.0'
+  s.add_dependency 'rspec',   '~> 3.0'
+
+  # Development Dependencies
+  s.add_development_dependency 'rake'
+  s.add_development_dependency 'redcarpet', '~> 3.0'
+  s.add_development_dependency 'yard',      '~> 0.8'
+
+  # Testing Dependencies
+  s.add_development_dependency 'aruba', '~> 0.5'
+end
diff --git a/examples/apt_package/recipes/install.rb b/examples/apt_package/recipes/install.rb
new file mode 100644
index 0000000..7ebb72b
--- /dev/null
+++ b/examples/apt_package/recipes/install.rb
@@ -0,0 +1,13 @@
+apt_package 'default_action'
+
+apt_package 'explicit_action' do
+  action :install
+end
+
+apt_package 'with_attributes' do
+  version '1.0.0'
+end
+
+apt_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/apt_package/recipes/purge.rb b/examples/apt_package/recipes/purge.rb
new file mode 100644
index 0000000..6e29581
--- /dev/null
+++ b/examples/apt_package/recipes/purge.rb
@@ -0,0 +1,13 @@
+apt_package 'explicit_action' do
+  action :purge
+end
+
+apt_package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+apt_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/apt_package/recipes/reconfig.rb b/examples/apt_package/recipes/reconfig.rb
new file mode 100644
index 0000000..794f333
--- /dev/null
+++ b/examples/apt_package/recipes/reconfig.rb
@@ -0,0 +1,13 @@
+apt_package 'explicit_action' do
+  action :reconfig
+end
+
+apt_package 'with_attributes' do
+  version '1.0.0'
+  action  :reconfig
+end
+
+apt_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :reconfig
+end
diff --git a/examples/apt_package/recipes/remove.rb b/examples/apt_package/recipes/remove.rb
new file mode 100644
index 0000000..9f2ddc8
--- /dev/null
+++ b/examples/apt_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+apt_package 'explicit_action' do
+  action :remove
+end
+
+apt_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+apt_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/apt_package/recipes/upgrade.rb b/examples/apt_package/recipes/upgrade.rb
new file mode 100644
index 0000000..592d984
--- /dev/null
+++ b/examples/apt_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+apt_package 'explicit_action' do
+  action :upgrade
+end
+
+apt_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+apt_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/apt_package/spec/install_spec.rb b/examples/apt_package/spec/install_spec.rb
new file mode 100644
index 0000000..fca55fd
--- /dev/null
+++ b/examples/apt_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'apt_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a apt_package with the default action' do
+    expect(chef_run).to install_apt_package('default_action')
+    expect(chef_run).to_not install_apt_package('not_default_action')
+  end
+
+  it 'installs a apt_package with an explicit action' do
+    expect(chef_run).to install_apt_package('explicit_action')
+  end
+
+  it 'installs a apt_package with attributes' do
+    expect(chef_run).to install_apt_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_apt_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a apt_package when specifying the identity attribute' do
+    expect(chef_run).to install_apt_package('identity_attribute')
+  end
+end
diff --git a/examples/apt_package/spec/purge_spec.rb b/examples/apt_package/spec/purge_spec.rb
new file mode 100644
index 0000000..72212db
--- /dev/null
+++ b/examples/apt_package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'apt_package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a apt_package with an explicit action' do
+    expect(chef_run).to purge_apt_package('explicit_action')
+    expect(chef_run).to_not purge_apt_package('not_explicit_action')
+  end
+
+  it 'purges a apt_package with attributes' do
+    expect(chef_run).to purge_apt_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_apt_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a apt_package when specifying the identity attribute' do
+    expect(chef_run).to purge_apt_package('identity_attribute')
+  end
+end
diff --git a/examples/apt_package/spec/reconfig_spec.rb b/examples/apt_package/spec/reconfig_spec.rb
new file mode 100644
index 0000000..86d8214
--- /dev/null
+++ b/examples/apt_package/spec/reconfig_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'apt_package::reconfig' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'reconfigs a apt_package with an explicit action' do
+    expect(chef_run).to reconfig_apt_package('explicit_action')
+    expect(chef_run).to_not reconfig_apt_package('not_explicit_action')
+  end
+
+  it 'reconfigs a apt_package with attributes' do
+    expect(chef_run).to reconfig_apt_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not reconfig_apt_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'reconfigs a apt_package when specifying the identity attribute' do
+    expect(chef_run).to reconfig_apt_package('identity_attribute')
+  end
+end
diff --git a/examples/apt_package/spec/remove_spec.rb b/examples/apt_package/spec/remove_spec.rb
new file mode 100644
index 0000000..3d10925
--- /dev/null
+++ b/examples/apt_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'apt_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a apt_package with an explicit action' do
+    expect(chef_run).to remove_apt_package('explicit_action')
+    expect(chef_run).to_not remove_apt_package('not_explicit_action')
+  end
+
+  it 'removes a apt_package with attributes' do
+    expect(chef_run).to remove_apt_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_apt_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a apt_package when specifying the identity attribute' do
+    expect(chef_run).to remove_apt_package('identity_attribute')
+  end
+end
diff --git a/examples/apt_package/spec/upgrade_spec.rb b/examples/apt_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..84f39a4
--- /dev/null
+++ b/examples/apt_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'apt_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a apt_package with an explicit action' do
+    expect(chef_run).to upgrade_apt_package('explicit_action')
+    expect(chef_run).to_not upgrade_apt_package('not_explicit_action')
+  end
+
+  it 'upgrades a apt_package with attributes' do
+    expect(chef_run).to upgrade_apt_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_apt_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a apt_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_apt_package('identity_attribute')
+  end
+end
diff --git a/examples/attributes/attributes/default.rb b/examples/attributes/attributes/default.rb
new file mode 100644
index 0000000..45bb7be
--- /dev/null
+++ b/examples/attributes/attributes/default.rb
@@ -0,0 +1 @@
+default['attributes']['message'] = 'This is the default message'
diff --git a/examples/attributes/recipes/default.rb b/examples/attributes/recipes/default.rb
new file mode 100644
index 0000000..00dee37
--- /dev/null
+++ b/examples/attributes/recipes/default.rb
@@ -0,0 +1,4 @@
+log node['attributes']['message']
+
+# Ohai-automatic attribute
+log node['ipaddress']
diff --git a/examples/attributes/spec/default_spec.rb b/examples/attributes/spec/default_spec.rb
new file mode 100644
index 0000000..ef3b407
--- /dev/null
+++ b/examples/attributes/spec/default_spec.rb
@@ -0,0 +1,20 @@
+require 'chefspec'
+
+describe 'attributes::default' do
+  let(:chef_run) do
+    ChefSpec::SoloRunner.new do |node|
+      node.automatic['ipaddress'] = '500.500.500.500' # Intentionally not a real IP
+      node.set['attributes']['message'] = 'The new message is here'
+    end.converge(described_recipe)
+  end
+
+  it 'uses the overridden node attribute' do
+    expect(chef_run).to write_log('The new message is here')
+    expect(chef_run).to_not write_log('This is the default message')
+  end
+
+  it 'uses the overridden ohai attribute' do
+    expect(chef_run).to write_log('500.500.500.500')
+    expect(chef_run).to_not write_log('127.0.0.1')
+  end
+end
diff --git a/examples/batch/recipes/run.rb b/examples/batch/recipes/run.rb
new file mode 100644
index 0000000..32e8318
--- /dev/null
+++ b/examples/batch/recipes/run.rb
@@ -0,0 +1,13 @@
+batch 'default_action'
+
+batch 'explicit_action' do
+  action :run
+end
+
+batch 'with_attributes' do
+  flags '-f'
+end
+
+batch 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/batch/spec/run_spec.rb b/examples/batch/spec/run_spec.rb
new file mode 100644
index 0000000..ca6f43a
--- /dev/null
+++ b/examples/batch/spec/run_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'batch::run' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a batch with the default action' do
+    expect(chef_run).to run_batch('default_action')
+    expect(chef_run).to_not run_batch('not_default_action')
+  end
+
+  it 'runs a batch with an explicit action' do
+    expect(chef_run).to run_batch('explicit_action')
+  end
+
+  it 'runs a batch with attributes' do
+    expect(chef_run).to run_batch('with_attributes').with(flags: '-f')
+    expect(chef_run).to_not run_batch('with_attributes').with(flags: '-x')
+  end
+
+  it 'runs a batch when specifying the identity attribute' do
+    expect(chef_run).to run_batch('identity_attribute')
+  end
+end
diff --git a/examples/cached/recipes/default.rb b/examples/cached/recipes/default.rb
new file mode 100644
index 0000000..cc991e2
--- /dev/null
+++ b/examples/cached/recipes/default.rb
@@ -0,0 +1 @@
+log 'I am gonna be so cached!'
diff --git a/examples/cached/spec/default_spec.rb b/examples/cached/spec/default_spec.rb
new file mode 100644
index 0000000..2b76a1f
--- /dev/null
+++ b/examples/cached/spec/default_spec.rb
@@ -0,0 +1,13 @@
+require 'chefspec/cacher'
+
+RSpec.configure do |config|
+  config.extend(ChefSpec::Cacher)
+end
+
+describe 'cached::default' do
+  cached(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'does not raise an exception' do
+    expect { chef_run }.to_not raise_error
+  end
+end
diff --git a/examples/chef_gem/recipes/install.rb b/examples/chef_gem/recipes/install.rb
new file mode 100644
index 0000000..c0e5ada
--- /dev/null
+++ b/examples/chef_gem/recipes/install.rb
@@ -0,0 +1,18 @@
+chef_gem 'default_action' do
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'explicit_action' do
+  action       :install
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'with_attributes' do
+  version      '1.0.0'
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  compile_time false if respond_to?(:compile_time)
+end
diff --git a/examples/chef_gem/recipes/purge.rb b/examples/chef_gem/recipes/purge.rb
new file mode 100644
index 0000000..e7fd462
--- /dev/null
+++ b/examples/chef_gem/recipes/purge.rb
@@ -0,0 +1,16 @@
+chef_gem 'explicit_action' do
+  action       :purge
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'with_attributes' do
+  version      '1.0.0'
+  action       :purge
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+  compile_time false if respond_to?(:compile_time)
+end
diff --git a/examples/chef_gem/recipes/reconfig.rb b/examples/chef_gem/recipes/reconfig.rb
new file mode 100644
index 0000000..6029515
--- /dev/null
+++ b/examples/chef_gem/recipes/reconfig.rb
@@ -0,0 +1,16 @@
+chef_gem 'explicit_action' do
+  action       :reconfig
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'with_attributes' do
+  version      '1.0.0'
+  action       :reconfig
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :reconfig
+  compile_time false if respond_to?(:compile_time)
+end
diff --git a/examples/chef_gem/recipes/remove.rb b/examples/chef_gem/recipes/remove.rb
new file mode 100644
index 0000000..82f0702
--- /dev/null
+++ b/examples/chef_gem/recipes/remove.rb
@@ -0,0 +1,16 @@
+chef_gem 'explicit_action' do
+  action       :remove
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'with_attributes' do
+  version      '1.0.0'
+  action       :remove
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+  compile_time false if respond_to?(:compile_time)
+end
diff --git a/examples/chef_gem/recipes/upgrade.rb b/examples/chef_gem/recipes/upgrade.rb
new file mode 100644
index 0000000..ecd1aa4
--- /dev/null
+++ b/examples/chef_gem/recipes/upgrade.rb
@@ -0,0 +1,16 @@
+chef_gem 'explicit_action' do
+  action       :upgrade
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'with_attributes' do
+  version      '1.0.0'
+  action       :upgrade
+  compile_time false if respond_to?(:compile_time)
+end
+
+chef_gem 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+  compile_time false if respond_to?(:compile_time)
+end
diff --git a/examples/chef_gem/spec/install_spec.rb b/examples/chef_gem/spec/install_spec.rb
new file mode 100644
index 0000000..e4b9bb7
--- /dev/null
+++ b/examples/chef_gem/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'chef_gem::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a chef_gem with the default action' do
+    expect(chef_run).to install_chef_gem('default_action')
+    expect(chef_run).to_not install_chef_gem('not_default_action')
+  end
+
+  it 'installs a chef_gem with an explicit action' do
+    expect(chef_run).to install_chef_gem('explicit_action')
+  end
+
+  it 'installs a chef_gem with attributes' do
+    expect(chef_run).to install_chef_gem('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_chef_gem('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a chef_gem when specifying the identity attribute' do
+    expect(chef_run).to install_chef_gem('identity_attribute')
+  end
+end
diff --git a/examples/chef_gem/spec/purge_spec.rb b/examples/chef_gem/spec/purge_spec.rb
new file mode 100644
index 0000000..3696f62
--- /dev/null
+++ b/examples/chef_gem/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'chef_gem::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a chef_gem with an explicit action' do
+    expect(chef_run).to purge_chef_gem('explicit_action')
+    expect(chef_run).to_not purge_chef_gem('not_explicit_action')
+  end
+
+  it 'purges a chef_gem with attributes' do
+    expect(chef_run).to purge_chef_gem('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_chef_gem('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a chef_gem when specifying the identity attribute' do
+    expect(chef_run).to purge_chef_gem('identity_attribute')
+  end
+end
diff --git a/examples/chef_gem/spec/reconfig_spec.rb b/examples/chef_gem/spec/reconfig_spec.rb
new file mode 100644
index 0000000..21d010a
--- /dev/null
+++ b/examples/chef_gem/spec/reconfig_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'chef_gem::reconfig' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'reconfigs a chef_gem with an explicit action' do
+    expect(chef_run).to reconfig_chef_gem('explicit_action')
+    expect(chef_run).to_not reconfig_chef_gem('not_explicit_action')
+  end
+
+  it 'reconfigs a chef_gem with attributes' do
+    expect(chef_run).to reconfig_chef_gem('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not reconfig_chef_gem('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'reconfigs a chef_gem when specifying the identity attribute' do
+    expect(chef_run).to reconfig_chef_gem('identity_attribute')
+  end
+end
diff --git a/examples/chef_gem/spec/remove_spec.rb b/examples/chef_gem/spec/remove_spec.rb
new file mode 100644
index 0000000..9196af6
--- /dev/null
+++ b/examples/chef_gem/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'chef_gem::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a chef_gem with an explicit action' do
+    expect(chef_run).to remove_chef_gem('explicit_action')
+    expect(chef_run).to_not remove_chef_gem('not_explicit_action')
+  end
+
+  it 'removes a chef_gem with attributes' do
+    expect(chef_run).to remove_chef_gem('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_chef_gem('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a chef_gem when specifying the identity attribute' do
+    expect(chef_run).to remove_chef_gem('identity_attribute')
+  end
+end
diff --git a/examples/chef_gem/spec/upgrade_spec.rb b/examples/chef_gem/spec/upgrade_spec.rb
new file mode 100644
index 0000000..3f058ad
--- /dev/null
+++ b/examples/chef_gem/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'chef_gem::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a chef_gem with an explicit action' do
+    expect(chef_run).to upgrade_chef_gem('explicit_action')
+    expect(chef_run).to_not upgrade_chef_gem('not_explicit_action')
+  end
+
+  it 'upgrades a chef_gem with attributes' do
+    expect(chef_run).to upgrade_chef_gem('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_chef_gem('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a chef_gem when specifying the identity attribute' do
+    expect(chef_run).to upgrade_chef_gem('identity_attribute')
+  end
+end
diff --git a/examples/compile_time/recipes/default.rb b/examples/compile_time/recipes/default.rb
new file mode 100644
index 0000000..edbad63
--- /dev/null
+++ b/examples/compile_time/recipes/default.rb
@@ -0,0 +1,3 @@
+package('compile_time') { action :nothing }.run_action(:install)
+
+package 'converge_time'
diff --git a/examples/compile_time/spec/default_spec.rb b/examples/compile_time/spec/default_spec.rb
new file mode 100644
index 0000000..1e90674
--- /dev/null
+++ b/examples/compile_time/spec/default_spec.rb
@@ -0,0 +1,27 @@
+describe 'compile_time::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'matches without .at_compile_time' do
+    expect(chef_run).to install_package('compile_time')
+  end
+
+  it 'matches with .at_compile_time' do
+    expect(chef_run).to install_package('compile_time').at_compile_time
+  end
+
+  it 'does not match when the resource is not at compile time' do
+    expect(chef_run).to_not install_package('converge_time').at_compile_time
+  end
+
+  it 'matches without .at_converge_time' do
+    expect(chef_run).to install_package('converge_time')
+  end
+
+  it 'matches with .at_converge_time' do
+    expect(chef_run).to install_package('converge_time').at_converge_time
+  end
+
+  it 'does not match when the resource is not at converge time' do
+    expect(chef_run).to_not install_package('compile_time').at_converge_time
+  end
+end
diff --git a/examples/cookbook_file/recipes/create.rb b/examples/cookbook_file/recipes/create.rb
new file mode 100644
index 0000000..ce3dd8e
--- /dev/null
+++ b/examples/cookbook_file/recipes/create.rb
@@ -0,0 +1,15 @@
+cookbook_file '/tmp/default_action'
+
+cookbook_file '/tmp/explicit_action' do
+  action :create
+end
+
+cookbook_file '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+end
+
+cookbook_file 'specifying the identity attribute' do
+  path '/tmp/identity_attribute'
+end
diff --git a/examples/cookbook_file/recipes/create_if_missing.rb b/examples/cookbook_file/recipes/create_if_missing.rb
new file mode 100644
index 0000000..b1a1003
--- /dev/null
+++ b/examples/cookbook_file/recipes/create_if_missing.rb
@@ -0,0 +1,15 @@
+cookbook_file '/tmp/explicit_action' do
+  action :create_if_missing
+end
+
+cookbook_file '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :create_if_missing
+end
+
+cookbook_file 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :create_if_missing
+end
diff --git a/examples/cookbook_file/recipes/delete.rb b/examples/cookbook_file/recipes/delete.rb
new file mode 100644
index 0000000..e0d2123
--- /dev/null
+++ b/examples/cookbook_file/recipes/delete.rb
@@ -0,0 +1,15 @@
+cookbook_file '/tmp/explicit_action' do
+  action :delete
+end
+
+cookbook_file '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :delete
+end
+
+cookbook_file 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :delete
+end
diff --git a/examples/cookbook_file/recipes/touch.rb b/examples/cookbook_file/recipes/touch.rb
new file mode 100644
index 0000000..4096b12
--- /dev/null
+++ b/examples/cookbook_file/recipes/touch.rb
@@ -0,0 +1,15 @@
+cookbook_file '/tmp/explicit_action' do
+  action :touch
+end
+
+cookbook_file '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :touch
+end
+
+cookbook_file 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :touch
+end
diff --git a/examples/cookbook_file/spec/create_if_missing_spec.rb b/examples/cookbook_file/spec/create_if_missing_spec.rb
new file mode 100644
index 0000000..86a436b
--- /dev/null
+++ b/examples/cookbook_file/spec/create_if_missing_spec.rb
@@ -0,0 +1,28 @@
+require 'chefspec'
+
+describe 'cookbook_file::create_if_missing' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a cookbook_file with an explicit action' do
+    expect(chef_run).to create_cookbook_file_if_missing('/tmp/explicit_action')
+    expect(chef_run).to_not create_cookbook_file_if_missing('/tmp/not_explicit_action')
+  end
+
+  it 'creates a cookbook_file with attributes' do
+    expect(chef_run).to create_cookbook_file_if_missing('/tmp/with_attributes').with(
+      user:   'user',
+      group:  'group',
+      backup: false,
+    )
+
+    expect(chef_run).to_not create_cookbook_file_if_missing('/tmp/with_attributes').with(
+      user:   'bacon',
+      group:  'fat',
+      backup: true,
+    )
+  end
+
+  it 'creates a cookbook_file when specifying the identity attribute' do
+    expect(chef_run).to create_cookbook_file_if_missing('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/cookbook_file/spec/create_spec.rb b/examples/cookbook_file/spec/create_spec.rb
new file mode 100644
index 0000000..03c671b
--- /dev/null
+++ b/examples/cookbook_file/spec/create_spec.rb
@@ -0,0 +1,32 @@
+require 'chefspec'
+
+describe 'cookbook_file::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a cookbook_file with the default action' do
+    expect(chef_run).to create_cookbook_file('/tmp/default_action')
+    expect(chef_run).to_not create_cookbook_file('/tmp/not_default_action')
+  end
+
+  it 'creates a cookbook_file with an explicit action' do
+    expect(chef_run).to create_cookbook_file('/tmp/explicit_action')
+  end
+
+  it 'creates a cookbook_file with attributes' do
+    expect(chef_run).to create_cookbook_file('/tmp/with_attributes').with(
+      user:   'user',
+      group:  'group',
+      backup: false,
+    )
+
+    expect(chef_run).to_not create_cookbook_file_if_missing('/tmp/with_attributes').with(
+      user:   'bacon',
+      group:  'fat',
+      backup: true,
+    )
+  end
+
+  it 'creates a cookbook_file when specifying the identity attribute' do
+    expect(chef_run).to create_cookbook_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/cookbook_file/spec/delete_spec.rb b/examples/cookbook_file/spec/delete_spec.rb
new file mode 100644
index 0000000..24ff8cd
--- /dev/null
+++ b/examples/cookbook_file/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'cookbook_file::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a cookbook_file with an explicit action' do
+    expect(chef_run).to delete_cookbook_file('/tmp/explicit_action')
+    expect(chef_run).to_not delete_cookbook_file('/tmp/not_explicit_action')
+  end
+
+  it 'deletes a cookbook_file with attributes' do
+    expect(chef_run).to delete_cookbook_file('/tmp/with_attributes').with(backup: false)
+    expect(chef_run).to_not delete_cookbook_file('/tmp/with_attributes').with(backup: true)
+  end
+
+  it 'deletes a cookbook_file when specifying the identity attribute' do
+    expect(chef_run).to delete_cookbook_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/cookbook_file/spec/touch_spec.rb b/examples/cookbook_file/spec/touch_spec.rb
new file mode 100644
index 0000000..d40da12
--- /dev/null
+++ b/examples/cookbook_file/spec/touch_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'cookbook_file::touch' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'touches a cookbook_file with an explicit action' do
+    expect(chef_run).to touch_cookbook_file('/tmp/explicit_action')
+    expect(chef_run).to_not touch_cookbook_file('/tmp/not_explicit_action')
+  end
+
+  it 'touches a cookbook_file with attributes' do
+    expect(chef_run).to touch_cookbook_file('/tmp/with_attributes').with(backup: false)
+    expect(chef_run).to_not touch_cookbook_file('/tmp/with_attributes').with(backup: true)
+  end
+
+  it 'touches a cookbook_file when specifying the identity attribute' do
+    expect(chef_run).to touch_cookbook_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/cron/recipes/create.rb b/examples/cron/recipes/create.rb
new file mode 100644
index 0000000..b406d86
--- /dev/null
+++ b/examples/cron/recipes/create.rb
@@ -0,0 +1,10 @@
+cron 'default_action'
+
+cron 'explicit_action' do
+  action :create
+end
+
+cron 'with_attributes' do
+  minute '0'
+  hour   '20'
+end
diff --git a/examples/cron/recipes/delete.rb b/examples/cron/recipes/delete.rb
new file mode 100644
index 0000000..333000c
--- /dev/null
+++ b/examples/cron/recipes/delete.rb
@@ -0,0 +1,9 @@
+cron 'explicit_action' do
+  action :delete
+end
+
+cron 'with_attributes' do
+  minute '0'
+  hour   '20'
+  action :delete
+end
diff --git a/examples/cron/spec/create_spec.rb b/examples/cron/spec/create_spec.rb
new file mode 100644
index 0000000..4696654
--- /dev/null
+++ b/examples/cron/spec/create_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'cron::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a cron with the default action' do
+    expect(chef_run).to create_cron('default_action')
+    expect(chef_run).to_not create_cron('not_default_action')
+  end
+
+  it 'creates a cron with an explicit action' do
+    expect(chef_run).to create_cron('explicit_action')
+  end
+
+  it 'creates a cron with attributes' do
+    expect(chef_run).to create_cron('with_attributes').with(minute: '0', hour: '20')
+    expect(chef_run).to_not create_cron('with_attributes').with(minute: '10', hour: '30')
+  end
+end
diff --git a/examples/cron/spec/delete_spec.rb b/examples/cron/spec/delete_spec.rb
new file mode 100644
index 0000000..c76bd97
--- /dev/null
+++ b/examples/cron/spec/delete_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'cron::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a cron with an explicit action' do
+    expect(chef_run).to delete_cron('explicit_action')
+    expect(chef_run).to_not delete_cron('not_explicit_action')
+  end
+
+  it 'deletes a cron with attributes' do
+    expect(chef_run).to delete_cron('with_attributes').with(minute: '0', hour: '20')
+    expect(chef_run).to_not delete_cron('with_attributes').with(minute: '10', hour: '30')
+  end
+end
diff --git a/examples/custom_matcher/libraries/matcher.rb b/examples/custom_matcher/libraries/matcher.rb
new file mode 100644
index 0000000..ea9dcd4
--- /dev/null
+++ b/examples/custom_matcher/libraries/matcher.rb
@@ -0,0 +1,23 @@
+if defined?(ChefSpec)
+  ChefSpec.define_matcher :custom_matcher_thing
+
+  #
+  # When defining a custom LWRP matcher, you should always add some
+  # documentation indicating how to use the custom matcher.
+  #
+  # @example This is an example
+  #   expect(chef_run).to install_custom_matcher_thing('foo')
+  #
+  # @param [String] resource_name
+  #   the resource name
+  #
+  # @return [ChefSpec::Matchers::ResourceMatcher]
+  #
+  def install_custom_matcher_thing(resource_name)
+    ChefSpec::Matchers::ResourceMatcher.new(:custom_matcher_thing, :install, resource_name)
+  end
+
+  def remove_custom_matcher_thing(resource_name)
+    ChefSpec::Matchers::ResourceMatcher.new(:custom_matcher_thing, :remove, resource_name)
+  end
+end
diff --git a/examples/custom_matcher/providers/thing.rb b/examples/custom_matcher/providers/thing.rb
new file mode 100644
index 0000000..3a7e7a2
--- /dev/null
+++ b/examples/custom_matcher/providers/thing.rb
@@ -0,0 +1,2 @@
+action(:install)  {}
+action(:remove) {}
diff --git a/examples/custom_matcher/recipes/install.rb b/examples/custom_matcher/recipes/install.rb
new file mode 100644
index 0000000..5b4d588
--- /dev/null
+++ b/examples/custom_matcher/recipes/install.rb
@@ -0,0 +1,13 @@
+custom_matcher_thing 'default_action'
+
+custom_matcher_thing 'explicit_action' do
+  action :install
+end
+
+custom_matcher_thing 'with_attributes' do
+  config true
+end
+
+custom_matcher_thing 'specifying the identity attribute' do
+  name 'identity_attribute'
+end
diff --git a/examples/custom_matcher/recipes/remove.rb b/examples/custom_matcher/recipes/remove.rb
new file mode 100644
index 0000000..772ba33
--- /dev/null
+++ b/examples/custom_matcher/recipes/remove.rb
@@ -0,0 +1,13 @@
+custom_matcher_thing 'explicit_action' do
+  action :remove
+end
+
+custom_matcher_thing 'with_attributes' do
+  config true
+  action :remove
+end
+
+custom_matcher_thing 'specifying the identity attribute' do
+  name   'identity_attribute'
+  action :remove
+end
diff --git a/examples/custom_matcher/resources/thing.rb b/examples/custom_matcher/resources/thing.rb
new file mode 100644
index 0000000..e10a80a
--- /dev/null
+++ b/examples/custom_matcher/resources/thing.rb
@@ -0,0 +1,5 @@
+actions :install, :remove
+default_action :install
+
+attribute :name,   kind_of: String, name_attribute: true
+attribute :config, kind_of: [TrueClass, FalseClass]
diff --git a/examples/custom_matcher/spec/install_spec.rb b/examples/custom_matcher/spec/install_spec.rb
new file mode 100644
index 0000000..f2137b9
--- /dev/null
+++ b/examples/custom_matcher/spec/install_spec.rb
@@ -0,0 +1,27 @@
+require 'chefspec'
+
+describe 'custom_matcher::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a custom_matcher with the default action' do
+    expect(chef_run).to install_custom_matcher_thing('default_action')
+    expect(chef_run).to_not install_custom_matcher_thing('not_default_action')
+  end
+
+  it 'installs a custom_matcher with an explicit action' do
+    expect(chef_run).to install_custom_matcher_thing('explicit_action')
+  end
+
+  it 'installs a custom_matcher with attributes' do
+    expect(chef_run).to install_custom_matcher_thing('with_attributes').with(config: true)
+    expect(chef_run).to_not install_custom_matcher_thing('with_attributes').with(config: false)
+  end
+
+  it 'installs a custom_matcher when specifying the identity attribute' do
+    expect(chef_run).to install_custom_matcher_thing('identity_attribute')
+  end
+
+  it 'defines a runner method' do
+    expect(chef_run).to respond_to(:custom_matcher_thing)
+  end
+end
diff --git a/examples/custom_matcher/spec/remove_spec.rb b/examples/custom_matcher/spec/remove_spec.rb
new file mode 100644
index 0000000..4ecc2ff
--- /dev/null
+++ b/examples/custom_matcher/spec/remove_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'custom_matcher::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a custom_matcher with an explicit action' do
+    expect(chef_run).to remove_custom_matcher_thing('explicit_action')
+    expect(chef_run).to_not remove_custom_matcher_thing('not_explicit_action')
+  end
+
+  it 'removes a custom_matcher with attributes' do
+    expect(chef_run).to remove_custom_matcher_thing('with_attributes').with(config: true)
+    expect(chef_run).to_not remove_custom_matcher_thing('with_attributes').with(config: false)
+  end
+
+  it 'removes a custom_matcher when specifying the identity attribute' do
+    expect(chef_run).to remove_custom_matcher_thing('identity_attribute')
+  end
+
+  it 'defines a runner method' do
+    expect(chef_run).to respond_to(:custom_matcher_thing)
+  end
+end
diff --git a/examples/deploy/recipes/deploy.rb b/examples/deploy/recipes/deploy.rb
new file mode 100644
index 0000000..4da2659
--- /dev/null
+++ b/examples/deploy/recipes/deploy.rb
@@ -0,0 +1,10 @@
+deploy '/tmp/default_action'
+
+deploy '/tmp/explicit_action' do
+  action :deploy
+end
+
+deploy '/tmp/with_attributes' do
+  repo    'ssh://git.path'
+  migrate true
+end
diff --git a/examples/deploy/recipes/force_deploy.rb b/examples/deploy/recipes/force_deploy.rb
new file mode 100644
index 0000000..9cb0fb4
--- /dev/null
+++ b/examples/deploy/recipes/force_deploy.rb
@@ -0,0 +1,9 @@
+deploy '/tmp/explicit_action' do
+  action :force_deploy
+end
+
+deploy '/tmp/with_attributes' do
+  repo    'ssh://git.path'
+  migrate true
+  action  :force_deploy
+end
diff --git a/examples/deploy/recipes/rollback.rb b/examples/deploy/recipes/rollback.rb
new file mode 100644
index 0000000..ddf31f8
--- /dev/null
+++ b/examples/deploy/recipes/rollback.rb
@@ -0,0 +1,9 @@
+deploy '/tmp/explicit_action' do
+  action :rollback
+end
+
+deploy '/tmp/with_attributes' do
+  repo    'ssh://git.path'
+  migrate true
+  action  :rollback
+end
diff --git a/examples/deploy/spec/deploy_spec.rb b/examples/deploy/spec/deploy_spec.rb
new file mode 100644
index 0000000..d119253
--- /dev/null
+++ b/examples/deploy/spec/deploy_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'deploy::deploy' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deploys a deploy with an explicit action' do
+    expect(chef_run).to deploy_deploy('/tmp/explicit_action')
+    expect(chef_run).to_not deploy_deploy('/tmp/not_explicit_action')
+  end
+
+  it 'deploys a deploy with attributes' do
+    expect(chef_run).to deploy_deploy('/tmp/with_attributes').with(repo: 'ssh://git.path', migrate: true)
+    expect(chef_run).to_not deploy_deploy('/tmp/with_attributes').with(repo: 'ssh://git.other_path', migrate: false)
+  end
+end
diff --git a/examples/deploy/spec/force_deploy_spec.rb b/examples/deploy/spec/force_deploy_spec.rb
new file mode 100644
index 0000000..4d12cff
--- /dev/null
+++ b/examples/deploy/spec/force_deploy_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'deploy::force_deploy' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'force deploys a deploy with an explicit action' do
+    expect(chef_run).to force_deploy_deploy('/tmp/explicit_action')
+    expect(chef_run).to_not force_deploy_deploy('/tmp/not_explicit_action')
+  end
+
+  it 'force deploys a deploy with attributes' do
+    expect(chef_run).to force_deploy_deploy('/tmp/with_attributes').with(repo: 'ssh://git.path', migrate: true)
+    expect(chef_run).to_not force_deploy_deploy('/tmp/with_attributes').with(repo: 'ssh://git.other_path', migrate: false)
+  end
+end
diff --git a/examples/deploy/spec/rollback_spec.rb b/examples/deploy/spec/rollback_spec.rb
new file mode 100644
index 0000000..6fb12b5
--- /dev/null
+++ b/examples/deploy/spec/rollback_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'deploy::rollback' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'rollsback deploys a deploy with an explicit action' do
+    expect(chef_run).to rollback_deploy('/tmp/explicit_action')
+    expect(chef_run).to_not rollback_deploy('/tmp/not_explicit_action')
+  end
+
+  it 'rollsback deploys a deploy with attributes' do
+    expect(chef_run).to rollback_deploy('/tmp/with_attributes').with(repo: 'ssh://git.path', migrate: true)
+    expect(chef_run).to_not rollback_deploy('/tmp/with_attributes').with(repo: 'ssh://git.other_path', migrate: false)
+  end
+end
diff --git a/examples/directory/recipes/create.rb b/examples/directory/recipes/create.rb
new file mode 100644
index 0000000..6973f2d
--- /dev/null
+++ b/examples/directory/recipes/create.rb
@@ -0,0 +1,18 @@
+directory '/tmp/default_action'
+
+directory '/tmp/explicit_action' do
+  action :create
+end
+
+directory '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+end
+
+directory 'specifying the identity attribute' do
+  path '/tmp/identity_attribute'
+end
+
+directory 'c:\temp\with_windows_rights' do
+  rights :read_execute, 'Users', applies_to_children: true
+end
diff --git a/examples/directory/recipes/delete.rb b/examples/directory/recipes/delete.rb
new file mode 100644
index 0000000..245c279
--- /dev/null
+++ b/examples/directory/recipes/delete.rb
@@ -0,0 +1,14 @@
+directory '/tmp/explicit_action' do
+  action :delete
+end
+
+directory '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  action :delete
+end
+
+directory 'specifying the identity attribute' do
+  path '/tmp/identity_attribute'
+  action :delete
+end
diff --git a/examples/directory/spec/create_spec.rb b/examples/directory/spec/create_spec.rb
new file mode 100644
index 0000000..a2a83e3
--- /dev/null
+++ b/examples/directory/spec/create_spec.rb
@@ -0,0 +1,34 @@
+require 'chefspec'
+
+describe 'directory::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a directory with the default action' do
+    expect(chef_run).to create_directory('/tmp/default_action')
+    expect(chef_run).to_not create_directory('/tmp/not_default_action')
+  end
+
+  it 'creates a directory with an explicit action' do
+    expect(chef_run).to create_directory('/tmp/explicit_action')
+  end
+
+  it 'creates a directory with attributes' do
+    expect(chef_run).to create_directory('/tmp/with_attributes').with(
+      user:   'user',
+      group:  'group',
+    )
+
+    expect(chef_run).to_not create_directory('/tmp/with_attributes').with(
+      user:   'bacon',
+      group:  'fat',
+    )
+  end
+
+  it 'creates a directory with windows rights' do
+    expect(chef_run).to create_directory('c:\temp\with_windows_rights').with(rights: [{:permissions=>:read_execute, :principals=>"Users", :applies_to_children=>true}])
+  end
+
+  it 'creates a directory when specifying the identity attribute' do
+    expect(chef_run).to create_directory('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/directory/spec/delete_spec.rb b/examples/directory/spec/delete_spec.rb
new file mode 100644
index 0000000..1372baa
--- /dev/null
+++ b/examples/directory/spec/delete_spec.rb
@@ -0,0 +1,26 @@
+require 'chefspec'
+
+describe 'directory::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a directory with an explicit action' do
+    expect(chef_run).to delete_directory('/tmp/explicit_action')
+    expect(chef_run).to_not delete_directory('/tmp/not_explicit_action')
+  end
+
+  it 'deletes a directory with attributes' do
+    expect(chef_run).to delete_directory('/tmp/with_attributes').with(
+      user:   'user',
+      group:  'group',
+    )
+
+    expect(chef_run).to_not delete_directory('/tmp/with_attributes').with(
+      user:   'bacon',
+      group:  'fat',
+    )
+  end
+
+  it 'deletes a directory when specifying the identity attribute' do
+    expect(chef_run).to delete_directory('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/do_nothing/recipes/default.rb b/examples/do_nothing/recipes/default.rb
new file mode 100644
index 0000000..4dcbd6e
--- /dev/null
+++ b/examples/do_nothing/recipes/default.rb
@@ -0,0 +1,3 @@
+execute 'install' do
+  action :nothing
+end
diff --git a/examples/do_nothing/spec/default_spec.rb b/examples/do_nothing/spec/default_spec.rb
new file mode 100644
index 0000000..e69a853
--- /dev/null
+++ b/examples/do_nothing/spec/default_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'do_nothing::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'includes the `other` recipe' do
+    execute = chef_run.execute('install')
+    expect(execute).to do_nothing
+  end
+
+  it 'does not include the `not` recipe' do
+    execute = chef_run.execute('not_install')
+    expect(execute).to_not do_nothing
+  end
+end
diff --git a/examples/dpkg_package/recipes/install.rb b/examples/dpkg_package/recipes/install.rb
new file mode 100644
index 0000000..6b6ec1e
--- /dev/null
+++ b/examples/dpkg_package/recipes/install.rb
@@ -0,0 +1,13 @@
+dpkg_package 'default_action'
+
+dpkg_package 'explicit_action' do
+  action :install
+end
+
+dpkg_package 'with_attributes' do
+  version '1.0.0'
+end
+
+dpkg_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/dpkg_package/recipes/purge.rb b/examples/dpkg_package/recipes/purge.rb
new file mode 100644
index 0000000..3c57670
--- /dev/null
+++ b/examples/dpkg_package/recipes/purge.rb
@@ -0,0 +1,13 @@
+dpkg_package 'explicit_action' do
+  action :purge
+end
+
+dpkg_package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+dpkg_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/dpkg_package/recipes/remove.rb b/examples/dpkg_package/recipes/remove.rb
new file mode 100644
index 0000000..36c0ba4
--- /dev/null
+++ b/examples/dpkg_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+dpkg_package 'explicit_action' do
+  action :remove
+end
+
+dpkg_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+dpkg_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/dpkg_package/spec/install_spec.rb b/examples/dpkg_package/spec/install_spec.rb
new file mode 100644
index 0000000..bd5d2c2
--- /dev/null
+++ b/examples/dpkg_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'dpkg_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a dpkg_package with the default action' do
+    expect(chef_run).to install_dpkg_package('default_action')
+    expect(chef_run).to_not install_dpkg_package('not_default_action')
+  end
+
+  it 'installs a dpkg_package with an explicit action' do
+    expect(chef_run).to install_dpkg_package('explicit_action')
+  end
+
+  it 'installs a dpkg_package with attributes' do
+    expect(chef_run).to install_dpkg_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_dpkg_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a dpkg_package when specifying the identity attribute' do
+    expect(chef_run).to install_dpkg_package('identity_attribute')
+  end
+end
diff --git a/examples/dpkg_package/spec/purge_spec.rb b/examples/dpkg_package/spec/purge_spec.rb
new file mode 100644
index 0000000..d8de7a2
--- /dev/null
+++ b/examples/dpkg_package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'dpkg_package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a dpkg_package with an explicit action' do
+    expect(chef_run).to purge_dpkg_package('explicit_action')
+    expect(chef_run).to_not purge_dpkg_package('not_explicit_action')
+  end
+
+  it 'purges a dpkg_package with attributes' do
+    expect(chef_run).to purge_dpkg_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_dpkg_package('with_attributes').with(version: '1.2.3.')
+  end
+
+  it 'purges a dpkg_package when specifying the identity attribute' do
+    expect(chef_run).to purge_dpkg_package('identity_attribute')
+  end
+end
diff --git a/examples/dpkg_package/spec/remove_spec.rb b/examples/dpkg_package/spec/remove_spec.rb
new file mode 100644
index 0000000..0626de7
--- /dev/null
+++ b/examples/dpkg_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'dpkg_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a dpkg_package with an explicit action' do
+    expect(chef_run).to remove_dpkg_package('explicit_action')
+    expect(chef_run).to_not remove_dpkg_package('not_explicit_action')
+  end
+
+  it 'removes a dpkg_package with attributes' do
+    expect(chef_run).to remove_dpkg_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_dpkg_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a dpkg_package when specifying the identity attribute' do
+    expect(chef_run).to remove_dpkg_package('identity_attribute')
+  end
+end
diff --git a/examples/easy_install_package/recipes/install.rb b/examples/easy_install_package/recipes/install.rb
new file mode 100644
index 0000000..dbb665c
--- /dev/null
+++ b/examples/easy_install_package/recipes/install.rb
@@ -0,0 +1,13 @@
+easy_install_package 'default_action'
+
+easy_install_package 'explicit_action' do
+  action :install
+end
+
+easy_install_package 'with_attributes' do
+  version '1.0.0'
+end
+
+easy_install_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/easy_install_package/recipes/purge.rb b/examples/easy_install_package/recipes/purge.rb
new file mode 100644
index 0000000..938533c
--- /dev/null
+++ b/examples/easy_install_package/recipes/purge.rb
@@ -0,0 +1,13 @@
+easy_install_package 'explicit_action' do
+  action :purge
+end
+
+easy_install_package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+easy_install_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/easy_install_package/recipes/remove.rb b/examples/easy_install_package/recipes/remove.rb
new file mode 100644
index 0000000..a974843
--- /dev/null
+++ b/examples/easy_install_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+easy_install_package 'explicit_action' do
+  action :remove
+end
+
+easy_install_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+easy_install_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/easy_install_package/recipes/upgrade.rb b/examples/easy_install_package/recipes/upgrade.rb
new file mode 100644
index 0000000..5423bdc
--- /dev/null
+++ b/examples/easy_install_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+easy_install_package 'explicit_action' do
+  action :upgrade
+end
+
+easy_install_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+easy_install_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/easy_install_package/spec/install_spec.rb b/examples/easy_install_package/spec/install_spec.rb
new file mode 100644
index 0000000..74498a1
--- /dev/null
+++ b/examples/easy_install_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'easy_install_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a easy_install_package with the default action' do
+    expect(chef_run).to install_easy_install_package('default_action')
+    expect(chef_run).to_not install_easy_install_package('not_default_action')
+  end
+
+  it 'installs a easy_install_package with an explicit action' do
+    expect(chef_run).to install_easy_install_package('explicit_action')
+  end
+
+  it 'installs a easy_install_package with attributes' do
+    expect(chef_run).to install_easy_install_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_easy_install_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a easy_install_package when specifying the identity attribute' do
+    expect(chef_run).to install_easy_install_package('identity_attribute')
+  end
+end
diff --git a/examples/easy_install_package/spec/purge_spec.rb b/examples/easy_install_package/spec/purge_spec.rb
new file mode 100644
index 0000000..61f3e67
--- /dev/null
+++ b/examples/easy_install_package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'easy_install_package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a easy_install_package with an explicit action' do
+    expect(chef_run).to purge_easy_install_package('explicit_action')
+    expect(chef_run).to_not purge_easy_install_package('not_explicit_action')
+  end
+
+  it 'purges a easy_install_package with attributes' do
+    expect(chef_run).to purge_easy_install_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_easy_install_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a easy_install_package when specifying the identity attribute' do
+    expect(chef_run).to purge_easy_install_package('identity_attribute')
+  end
+end
diff --git a/examples/easy_install_package/spec/remove_spec.rb b/examples/easy_install_package/spec/remove_spec.rb
new file mode 100644
index 0000000..ae88cbf
--- /dev/null
+++ b/examples/easy_install_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'easy_install_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a easy_install_package with an explicit action' do
+    expect(chef_run).to remove_easy_install_package('explicit_action')
+    expect(chef_run).to_not remove_easy_install_package('not_explicit_action')
+  end
+
+  it 'removes a easy_install_package with attributes' do
+    expect(chef_run).to remove_easy_install_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_easy_install_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a easy_install_package when specifying the identity attribute' do
+    expect(chef_run).to remove_easy_install_package('identity_attribute')
+  end
+end
diff --git a/examples/easy_install_package/spec/upgrade_spec.rb b/examples/easy_install_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..17b21b6
--- /dev/null
+++ b/examples/easy_install_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'easy_install_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a easy_install_package with an explicit action' do
+    expect(chef_run).to upgrade_easy_install_package('explicit_action')
+    expect(chef_run).to_not upgrade_easy_install_package('not_explicit_action')
+  end
+
+  it 'upgrades a easy_install_package with attributes' do
+    expect(chef_run).to upgrade_easy_install_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_easy_install_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a easy_install_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_easy_install_package('identity_attribute')
+  end
+end
diff --git a/examples/env/recipes/create.rb b/examples/env/recipes/create.rb
new file mode 100644
index 0000000..4b57446
--- /dev/null
+++ b/examples/env/recipes/create.rb
@@ -0,0 +1,13 @@
+env 'default_action'
+
+env 'explicit_action' do
+  action :create
+end
+
+env 'with_attributes' do
+  value 'value'
+end
+
+env 'specifying the identity attribute' do
+  key_name 'identity_attribute'
+end
diff --git a/examples/env/recipes/delete.rb b/examples/env/recipes/delete.rb
new file mode 100644
index 0000000..2ac8f2e
--- /dev/null
+++ b/examples/env/recipes/delete.rb
@@ -0,0 +1,13 @@
+env 'explicit_action' do
+  action :delete
+end
+
+env 'with_attributes' do
+  value  'value'
+  action :delete
+end
+
+env 'specifying the identity attribute' do
+  key_name 'identity_attribute'
+  action   :delete
+end
diff --git a/examples/env/recipes/modify.rb b/examples/env/recipes/modify.rb
new file mode 100644
index 0000000..1358f01
--- /dev/null
+++ b/examples/env/recipes/modify.rb
@@ -0,0 +1,13 @@
+env 'explicit_action' do
+  action :modify
+end
+
+env 'with_attributes' do
+  value  'value'
+  action :modify
+end
+
+env 'specifying the identity attribute' do
+  key_name 'identity_attribute'
+  action   :modify
+end
diff --git a/examples/env/spec/create_spec.rb b/examples/env/spec/create_spec.rb
new file mode 100644
index 0000000..1499b66
--- /dev/null
+++ b/examples/env/spec/create_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'env::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a env with the default action' do
+    expect(chef_run).to create_env('default_action')
+    expect(chef_run).to_not create_env('not_default_action')
+  end
+
+  it 'creates a env with an explicit action' do
+    expect(chef_run).to create_env('explicit_action')
+  end
+
+  it 'creates a env with attributes' do
+    expect(chef_run).to create_env('with_attributes').with(value: 'value')
+    expect(chef_run).to_not create_env('with_attributes').with(value: 'not_value')
+  end
+
+  it 'creates a env when specifying the identity attribute' do
+    expect(chef_run).to create_env('identity_attribute')
+  end
+end
diff --git a/examples/env/spec/delete_spec.rb b/examples/env/spec/delete_spec.rb
new file mode 100644
index 0000000..1b01b11
--- /dev/null
+++ b/examples/env/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'env::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a env with an explicit action' do
+    expect(chef_run).to delete_env('explicit_action')
+    expect(chef_run).to_not delete_env('not_explicit_action')
+  end
+
+  it 'deletes a env with attributes' do
+    expect(chef_run).to delete_env('with_attributes').with(value: 'value')
+    expect(chef_run).to_not delete_env('with_attributes').with(value: 'not_value')
+  end
+
+  it 'deletes a env when specifying the identity attribute' do
+    expect(chef_run).to delete_env('identity_attribute')
+  end
+end
diff --git a/examples/env/spec/modify_spec.rb b/examples/env/spec/modify_spec.rb
new file mode 100644
index 0000000..c2e3f8c
--- /dev/null
+++ b/examples/env/spec/modify_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'env::modify' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'modifies a env with an explicit action' do
+    expect(chef_run).to modify_env('explicit_action')
+    expect(chef_run).to_not modify_env('not_explicit_action')
+  end
+
+  it 'modifies a env with attributes' do
+    expect(chef_run).to modify_env('with_attributes').with(value: 'value')
+    expect(chef_run).to_not modify_env('with_attributes').with(value: 'not_value')
+  end
+
+  it 'modifies a env when specifying the identity attribute' do
+    expect(chef_run).to modify_env('identity_attribute')
+  end
+end
diff --git a/examples/erl_call/recipes/run.rb b/examples/erl_call/recipes/run.rb
new file mode 100644
index 0000000..174dfdc
--- /dev/null
+++ b/examples/erl_call/recipes/run.rb
@@ -0,0 +1,13 @@
+erl_call 'default_action'
+
+erl_call 'explicit_action' do
+  action :run
+end
+
+erl_call 'with_attributes' do
+  code 'hello'
+end
+
+erl_call 'specifying the identity attribute' do
+  code 'identity_attribute'
+end
diff --git a/examples/erl_call/spec/run_spec.rb b/examples/erl_call/spec/run_spec.rb
new file mode 100644
index 0000000..1ea7fd0
--- /dev/null
+++ b/examples/erl_call/spec/run_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'erl_call::run' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a erl_call with the default action' do
+    expect(chef_run).to run_erl_call('default_action')
+    expect(chef_run).to_not run_erl_call('not_default_action')
+  end
+
+  it 'runs a erl_call with an explicit action' do
+    expect(chef_run).to run_erl_call('explicit_action')
+  end
+
+  it 'runs a erl_call with attributes' do
+    expect(chef_run).to run_erl_call('with_attributes').with(code: 'hello')
+    expect(chef_run).to_not run_erl_call('with_attributes').with(code: 'not_hello')
+  end
+
+  it 'runs a erl_call when specifying the identity attribute' do
+    expect(chef_run).to run_erl_call('identity_attribute')
+  end
+end
diff --git a/examples/execute/recipes/run.rb b/examples/execute/recipes/run.rb
new file mode 100644
index 0000000..68bf234
--- /dev/null
+++ b/examples/execute/recipes/run.rb
@@ -0,0 +1,13 @@
+execute 'default_action'
+
+execute 'explicit_action' do
+  action :run
+end
+
+execute 'with_attributes' do
+  user 'user'
+end
+
+execute 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/execute/spec/run_spec.rb b/examples/execute/spec/run_spec.rb
new file mode 100644
index 0000000..6762d11
--- /dev/null
+++ b/examples/execute/spec/run_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'execute::run' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a execute with the default action' do
+    expect(chef_run).to run_execute('default_action')
+    expect(chef_run).to_not run_execute('not_default_action')
+  end
+
+  it 'runs a execute with an explicit action' do
+    expect(chef_run).to run_execute('explicit_action')
+  end
+
+  it 'runs a execute with attributes' do
+    expect(chef_run).to run_execute('with_attributes').with(user: 'user')
+    expect(chef_run).to_not run_execute('with_attributes').with(user: 'not_user')
+  end
+
+  it 'runs a execute when specifying the identity attribute' do
+    expect(chef_run).to run_execute('identity_attribute')
+  end
+end
diff --git a/examples/expect_exception/recipes/compile_error.rb b/examples/expect_exception/recipes/compile_error.rb
new file mode 100644
index 0000000..eada8f2
--- /dev/null
+++ b/examples/expect_exception/recipes/compile_error.rb
@@ -0,0 +1 @@
+raise ArgumentError
diff --git a/examples/expect_exception/recipes/converge_error.rb b/examples/expect_exception/recipes/converge_error.rb
new file mode 100644
index 0000000..00bf235
--- /dev/null
+++ b/examples/expect_exception/recipes/converge_error.rb
@@ -0,0 +1,5 @@
+ruby_block do
+  block do
+    raise ArgumentError
+  end
+end
diff --git a/examples/expect_exception/recipes/no_error.rb b/examples/expect_exception/recipes/no_error.rb
new file mode 100644
index 0000000..c5fa136
--- /dev/null
+++ b/examples/expect_exception/recipes/no_error.rb
@@ -0,0 +1 @@
+# This does nothing
diff --git a/examples/expect_exception/spec/compile_error_spec.rb b/examples/expect_exception/spec/compile_error_spec.rb
new file mode 100644
index 0000000..1a9c6ef
--- /dev/null
+++ b/examples/expect_exception/spec/compile_error_spec.rb
@@ -0,0 +1,10 @@
+require 'chefspec'
+
+describe 'expect_exception::compile_error' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'raises an error' do
+    expect(Chef::Formatters::ErrorMapper).to_not receive(:file_load_failed)
+    expect { chef_run }.to raise_error(ArgumentError)
+  end
+end
diff --git a/examples/expect_exception/spec/converge_error_spec.rb b/examples/expect_exception/spec/converge_error_spec.rb
new file mode 100644
index 0000000..89612b2
--- /dev/null
+++ b/examples/expect_exception/spec/converge_error_spec.rb
@@ -0,0 +1,10 @@
+require 'chefspec'
+
+describe 'expect_exception::converge_error' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'raises an error' do
+    expect(Chef::Formatters::ErrorMapper).to_not receive(:file_load_failed)
+    expect { chef_run }.to raise_error(ArgumentError)
+  end
+end
diff --git a/examples/expect_exception/spec/no_error_spec.rb b/examples/expect_exception/spec/no_error_spec.rb
new file mode 100644
index 0000000..fc4a458
--- /dev/null
+++ b/examples/expect_exception/spec/no_error_spec.rb
@@ -0,0 +1,10 @@
+require 'chefspec'
+
+describe 'expect_exception::no_error' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'does not raise an error' do
+    expect(Chef::Formatters::ErrorMapper).to_not receive(:file_load_failed)
+    expect { chef_run }.to_not raise_error
+  end
+end
diff --git a/examples/file/recipes/create.rb b/examples/file/recipes/create.rb
new file mode 100644
index 0000000..e56b895
--- /dev/null
+++ b/examples/file/recipes/create.rb
@@ -0,0 +1,15 @@
+file '/tmp/default_action'
+
+file '/tmp/explicit_action' do
+  action :create
+end
+
+file '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+end
+
+file 'specifying the identity attribute' do
+  path '/tmp/identity_attribute'
+end
diff --git a/examples/file/recipes/create_if_missing.rb b/examples/file/recipes/create_if_missing.rb
new file mode 100644
index 0000000..d663075
--- /dev/null
+++ b/examples/file/recipes/create_if_missing.rb
@@ -0,0 +1,15 @@
+file '/tmp/explicit_action' do
+  action :create_if_missing
+end
+
+file '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :create_if_missing
+end
+
+file 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :create_if_missing
+end
diff --git a/examples/file/recipes/delete.rb b/examples/file/recipes/delete.rb
new file mode 100644
index 0000000..9797c18
--- /dev/null
+++ b/examples/file/recipes/delete.rb
@@ -0,0 +1,15 @@
+file '/tmp/explicit_action' do
+  action :delete
+end
+
+file '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :delete
+end
+
+file 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :delete
+end
diff --git a/examples/file/recipes/touch.rb b/examples/file/recipes/touch.rb
new file mode 100644
index 0000000..8b55efb
--- /dev/null
+++ b/examples/file/recipes/touch.rb
@@ -0,0 +1,15 @@
+file '/tmp/explicit_action' do
+  action :touch
+end
+
+file '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :touch
+end
+
+file 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :touch
+end
diff --git a/examples/file/spec/create_if_missing_spec.rb b/examples/file/spec/create_if_missing_spec.rb
new file mode 100644
index 0000000..31c681b
--- /dev/null
+++ b/examples/file/spec/create_if_missing_spec.rb
@@ -0,0 +1,28 @@
+require 'chefspec'
+
+describe 'file::create_if_missing' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a file with an explicit action' do
+    expect(chef_run).to create_file_if_missing('/tmp/explicit_action')
+    expect(chef_run).to_not create_file_if_missing('/tmp/not_explicit_action')
+  end
+
+  it 'creates a file with attributes' do
+    expect(chef_run).to create_file_if_missing('/tmp/with_attributes').with(
+      user:   'user',
+      group:  'group',
+      backup: false,
+    )
+
+    expect(chef_run).to_not create_file_if_missing('/tmp/with_attributes').with(
+      user:   'bacon',
+      group:  'fat',
+      backup: true,
+    )
+  end
+
+  it 'creates a file when specifying the identity attribute' do
+    expect(chef_run).to create_file_if_missing('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/file/spec/create_spec.rb b/examples/file/spec/create_spec.rb
new file mode 100644
index 0000000..cbbeec8
--- /dev/null
+++ b/examples/file/spec/create_spec.rb
@@ -0,0 +1,32 @@
+require 'chefspec'
+
+describe 'file::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a file with the default action' do
+    expect(chef_run).to create_file('/tmp/default_action')
+    expect(chef_run).to_not create_file('/tmp/not_default_action')
+  end
+
+  it 'creates a file with an explicit action' do
+    expect(chef_run).to create_file('/tmp/explicit_action')
+  end
+
+  it 'creates a file with attributes' do
+    expect(chef_run).to create_file('/tmp/with_attributes').with(
+      user:   'user',
+      group:  'group',
+      backup: false,
+    )
+
+    expect(chef_run).to_not create_file('/tmp/with_attributes').with(
+      user:   'bacon',
+      group:  'fat',
+      backup: true,
+    )
+  end
+
+  it 'creates a file when specifying the identity attribute' do
+    expect(chef_run).to create_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/file/spec/delete_spec.rb b/examples/file/spec/delete_spec.rb
new file mode 100644
index 0000000..eb8a771
--- /dev/null
+++ b/examples/file/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'file::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a file with an explicit action' do
+    expect(chef_run).to delete_file('/tmp/explicit_action')
+    expect(chef_run).to_not delete_file('/tmp/not_explicit_action')
+  end
+
+  it 'deletes a file with attributes' do
+    expect(chef_run).to delete_file('/tmp/with_attributes').with(backup: false)
+    expect(chef_run).to_not delete_file('/tmp/with_attributes').with(backup: true)
+  end
+
+  it 'deletes a file when specifying the identity attribute' do
+    expect(chef_run).to delete_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/file/spec/touch_spec.rb b/examples/file/spec/touch_spec.rb
new file mode 100644
index 0000000..56f78a3
--- /dev/null
+++ b/examples/file/spec/touch_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'file::touch' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'touches a file with an explicit action' do
+    expect(chef_run).to touch_file('/tmp/explicit_action')
+    expect(chef_run).to_not touch_file('/tmp/not_explicit_action')
+  end
+
+  it 'touches a file with attributes' do
+    expect(chef_run).to touch_file('/tmp/with_attributes').with(backup: false)
+    expect(chef_run).to_not touch_file('/tmp/with_attributes').with(backup: true)
+  end
+
+  it 'touches a file when specifying the identity attribute' do
+    expect(chef_run).to touch_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/freebsd_package/recipes/install.rb b/examples/freebsd_package/recipes/install.rb
new file mode 100644
index 0000000..130adf0
--- /dev/null
+++ b/examples/freebsd_package/recipes/install.rb
@@ -0,0 +1,13 @@
+freebsd_package 'default_action'
+
+freebsd_package 'explicit_action' do
+  action :install
+end
+
+freebsd_package 'with_attributes' do
+  version '1.0.0'
+end
+
+freebsd_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/freebsd_package/recipes/remove.rb b/examples/freebsd_package/recipes/remove.rb
new file mode 100644
index 0000000..6793a4c
--- /dev/null
+++ b/examples/freebsd_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+freebsd_package 'explicit_action' do
+  action :remove
+end
+
+freebsd_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+freebsd_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/freebsd_package/spec/install_spec.rb b/examples/freebsd_package/spec/install_spec.rb
new file mode 100644
index 0000000..27e9970
--- /dev/null
+++ b/examples/freebsd_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'freebsd_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a freebsd_package with the default action' do
+    expect(chef_run).to install_freebsd_package('default_action')
+    expect(chef_run).to_not install_freebsd_package('not_default_action')
+  end
+
+  it 'installs a freebsd_package with an explicit action' do
+    expect(chef_run).to install_freebsd_package('explicit_action')
+  end
+
+  it 'installs a freebsd_package with attributes' do
+    expect(chef_run).to install_freebsd_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_freebsd_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a freebsd_package when specifying the identity attribute' do
+    expect(chef_run).to install_freebsd_package('identity_attribute')
+  end
+end
diff --git a/examples/freebsd_package/spec/remove_spec.rb b/examples/freebsd_package/spec/remove_spec.rb
new file mode 100644
index 0000000..26c3386
--- /dev/null
+++ b/examples/freebsd_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'freebsd_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a freebsd_package with an explicit action' do
+    expect(chef_run).to remove_freebsd_package('explicit_action')
+    expect(chef_run).to_not remove_freebsd_package('not_explicit_action')
+  end
+
+  it 'removes a freebsd_package with attributes' do
+    expect(chef_run).to remove_freebsd_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_freebsd_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a freebsd_package when specifying the identity attribute' do
+    expect(chef_run).to remove_freebsd_package('identity_attribute')
+  end
+end
diff --git a/examples/gem_package/recipes/install.rb b/examples/gem_package/recipes/install.rb
new file mode 100644
index 0000000..c3d63a1
--- /dev/null
+++ b/examples/gem_package/recipes/install.rb
@@ -0,0 +1,13 @@
+gem_package 'default_action'
+
+gem_package 'explicit_action' do
+  action :install
+end
+
+gem_package 'with_attributes' do
+  version '1.0.0'
+end
+
+gem_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/gem_package/recipes/purge.rb b/examples/gem_package/recipes/purge.rb
new file mode 100644
index 0000000..10a7825
--- /dev/null
+++ b/examples/gem_package/recipes/purge.rb
@@ -0,0 +1,13 @@
+gem_package 'explicit_action' do
+  action :purge
+end
+
+gem_package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+gem_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/gem_package/recipes/reconfig.rb b/examples/gem_package/recipes/reconfig.rb
new file mode 100644
index 0000000..7def384
--- /dev/null
+++ b/examples/gem_package/recipes/reconfig.rb
@@ -0,0 +1,13 @@
+gem_package 'explicit_action' do
+  action :reconfig
+end
+
+gem_package 'with_attributes' do
+  version '1.0.0'
+  action  :reconfig
+end
+
+gem_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :reconfig
+end
diff --git a/examples/gem_package/recipes/remove.rb b/examples/gem_package/recipes/remove.rb
new file mode 100644
index 0000000..f768f68
--- /dev/null
+++ b/examples/gem_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+gem_package 'explicit_action' do
+  action :remove
+end
+
+gem_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+gem_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/gem_package/recipes/upgrade.rb b/examples/gem_package/recipes/upgrade.rb
new file mode 100644
index 0000000..27c6de0
--- /dev/null
+++ b/examples/gem_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+gem_package 'explicit_action' do
+  action :upgrade
+end
+
+gem_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+gem_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/gem_package/spec/install_spec.rb b/examples/gem_package/spec/install_spec.rb
new file mode 100644
index 0000000..6809ef8
--- /dev/null
+++ b/examples/gem_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'gem_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a gem_package with the default action' do
+    expect(chef_run).to install_gem_package('default_action')
+    expect(chef_run).to_not install_gem_package('not_default_action')
+  end
+
+  it 'installs a gem_package with an explicit action' do
+    expect(chef_run).to install_gem_package('explicit_action')
+  end
+
+  it 'installs a gem_package with attributes' do
+    expect(chef_run).to install_gem_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_gem_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a gem_package when specifying the identity attribute' do
+    expect(chef_run).to install_gem_package('identity_attribute')
+  end
+end
diff --git a/examples/gem_package/spec/purge_spec.rb b/examples/gem_package/spec/purge_spec.rb
new file mode 100644
index 0000000..7357bb4
--- /dev/null
+++ b/examples/gem_package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'gem_package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a gem_package with an explicit action' do
+    expect(chef_run).to purge_gem_package('explicit_action')
+    expect(chef_run).to_not purge_gem_package('not_explicit_action')
+  end
+
+  it 'purges a gem_package with attributes' do
+    expect(chef_run).to purge_gem_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_gem_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a gem_package when specifying the identity attribute' do
+    expect(chef_run).to purge_gem_package('identity_attribute')
+  end
+end
diff --git a/examples/gem_package/spec/reconfig_spec.rb b/examples/gem_package/spec/reconfig_spec.rb
new file mode 100644
index 0000000..c542c02
--- /dev/null
+++ b/examples/gem_package/spec/reconfig_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'gem_package::reconfig' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'reconfigs a gem_package with an explicit action' do
+    expect(chef_run).to reconfig_gem_package('explicit_action')
+    expect(chef_run).to_not reconfig_gem_package('not_explicit_action')
+  end
+
+  it 'reconfigs a gem_package with attributes' do
+    expect(chef_run).to reconfig_gem_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not reconfig_gem_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'reconfigs a gem_package when specifying the identity attribute' do
+    expect(chef_run).to reconfig_gem_package('identity_attribute')
+  end
+end
diff --git a/examples/gem_package/spec/remove_spec.rb b/examples/gem_package/spec/remove_spec.rb
new file mode 100644
index 0000000..a6483b0
--- /dev/null
+++ b/examples/gem_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'gem_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a gem_package with an explicit action' do
+    expect(chef_run).to remove_gem_package('explicit_action')
+    expect(chef_run).to_not remove_gem_package('not_explicit_action')
+  end
+
+  it 'removes a gem_package with attributes' do
+    expect(chef_run).to remove_gem_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_gem_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a gem_package when specifying the identity attribute' do
+    expect(chef_run).to remove_gem_package('identity_attribute')
+  end
+end
diff --git a/examples/gem_package/spec/upgrade_spec.rb b/examples/gem_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..007e0ca
--- /dev/null
+++ b/examples/gem_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'gem_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a gem_package with an explicit action' do
+    expect(chef_run).to upgrade_gem_package('explicit_action')
+    expect(chef_run).to_not upgrade_gem_package('not_explicit_action')
+  end
+
+  it 'upgrades a gem_package with attributes' do
+    expect(chef_run).to upgrade_gem_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_gem_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a gem_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_gem_package('identity_attribute')
+  end
+end
diff --git a/examples/git/recipes/checkout.rb b/examples/git/recipes/checkout.rb
new file mode 100644
index 0000000..2125c88
--- /dev/null
+++ b/examples/git/recipes/checkout.rb
@@ -0,0 +1,13 @@
+git '/tmp/explicit_action' do
+  action :checkout
+end
+
+git '/tmp/with_attributes' do
+  repository 'ssh://git.path'
+  action     :checkout
+end
+
+git 'specifying the identity attribute' do
+  destination '/tmp/identity_attribute'
+  action      :checkout
+end
diff --git a/examples/git/recipes/export.rb b/examples/git/recipes/export.rb
new file mode 100644
index 0000000..3117e7c
--- /dev/null
+++ b/examples/git/recipes/export.rb
@@ -0,0 +1,13 @@
+git '/tmp/explicit_action' do
+  action :export
+end
+
+git '/tmp/with_attributes' do
+  repository 'ssh://git.path'
+  action     :export
+end
+
+git 'specifying the identity attribute' do
+  destination '/tmp/identity_attribute'
+  action      :export
+end
diff --git a/examples/git/recipes/sync.rb b/examples/git/recipes/sync.rb
new file mode 100644
index 0000000..b94c064
--- /dev/null
+++ b/examples/git/recipes/sync.rb
@@ -0,0 +1,13 @@
+git '/tmp/default_action'
+
+git '/tmp/explicit_action' do
+  action :sync
+end
+
+git '/tmp/with_attributes' do
+  repository 'ssh://git.path'
+end
+
+git 'specifying the identity attribute' do
+  destination '/tmp/identity_attribute'
+end
diff --git a/examples/git/spec/checkout_spec.rb b/examples/git/spec/checkout_spec.rb
new file mode 100644
index 0000000..02c766d
--- /dev/null
+++ b/examples/git/spec/checkout_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'git::checkout' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'checkouts a git with an explicit action' do
+    expect(chef_run).to checkout_git('/tmp/explicit_action')
+    expect(chef_run).to_not checkout_git('/tmp/not_explicit_action')
+  end
+
+  it 'checkouts a git with attributes' do
+    expect(chef_run).to checkout_git('/tmp/with_attributes').with(repository: 'ssh://git.path')
+    expect(chef_run).to_not checkout_git('/tmp/with_attributes').with(repository: 'ssh://git.other_path')
+  end
+
+  it 'checkouts a git when specifying the identity attribute' do
+    expect(chef_run).to checkout_git('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/git/spec/export_spec.rb b/examples/git/spec/export_spec.rb
new file mode 100644
index 0000000..872adc8
--- /dev/null
+++ b/examples/git/spec/export_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'git::export' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'exports a git with an explicit action' do
+    expect(chef_run).to export_git('/tmp/explicit_action')
+    expect(chef_run).to_not export_git('/tmp/not_explicit_action')
+  end
+
+  it 'exports a git with attributes' do
+    expect(chef_run).to export_git('/tmp/with_attributes').with(repository: 'ssh://git.path')
+    expect(chef_run).to_not export_git('/tmp/with_attributes').with(repository: 'ssh://git.other_path')
+  end
+
+  it 'exports a git when specifying the identity attribute' do
+    expect(chef_run).to export_git('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/git/spec/sync_spec.rb b/examples/git/spec/sync_spec.rb
new file mode 100644
index 0000000..ce9bb96
--- /dev/null
+++ b/examples/git/spec/sync_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'git::sync' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'syncs a git with the default action' do
+    expect(chef_run).to sync_git('/tmp/default_action')
+    expect(chef_run).to_not sync_git('/tmp/not_default_action')
+  end
+
+  it 'syncs a git with an explicit action' do
+    expect(chef_run).to sync_git('/tmp/explicit_action')
+  end
+
+  it 'syncs a git with attributes' do
+    expect(chef_run).to sync_git('/tmp/with_attributes').with(repository: 'ssh://git.path')
+    expect(chef_run).to_not sync_git('/tmp/with_attributes').with(repository: 'ssh://git.other_path')
+  end
+
+  it 'syncs a git when specifying the identity attribute' do
+    expect(chef_run).to sync_git('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/group/recipes/create.rb b/examples/group/recipes/create.rb
new file mode 100644
index 0000000..bdd3622
--- /dev/null
+++ b/examples/group/recipes/create.rb
@@ -0,0 +1,13 @@
+group 'default_action'
+
+group 'explicit_action' do
+  action :create
+end
+
+group 'with_attributes' do
+  gid 1234
+end
+
+group 'specifying the identity attribute' do
+  group_name 'identity_attribute'
+end
diff --git a/examples/group/recipes/manage.rb b/examples/group/recipes/manage.rb
new file mode 100644
index 0000000..ffbdbaa
--- /dev/null
+++ b/examples/group/recipes/manage.rb
@@ -0,0 +1,13 @@
+group 'explicit_action' do
+  action :manage
+end
+
+group 'with_attributes' do
+  gid    1234
+  action :manage
+end
+
+group 'specifying the identity attribute' do
+  group_name 'identity_attribute'
+  action     :manage
+end
diff --git a/examples/group/recipes/modify.rb b/examples/group/recipes/modify.rb
new file mode 100644
index 0000000..04d5c16
--- /dev/null
+++ b/examples/group/recipes/modify.rb
@@ -0,0 +1,13 @@
+group 'explicit_action' do
+  action :modify
+end
+
+group 'with_attributes' do
+  gid    1234
+  action :modify
+end
+
+group 'specifying the identity attribute' do
+  group_name 'identity_attribute'
+  action     :modify
+end
diff --git a/examples/group/recipes/remove.rb b/examples/group/recipes/remove.rb
new file mode 100644
index 0000000..1de51e3
--- /dev/null
+++ b/examples/group/recipes/remove.rb
@@ -0,0 +1,13 @@
+group 'explicit_action' do
+  action :remove
+end
+
+group 'with_attributes' do
+  gid    1234
+  action :remove
+end
+
+group 'specifying the identity attribute' do
+  group_name 'identity_attribute'
+  action     :remove
+end
diff --git a/examples/group/spec/create_spec.rb b/examples/group/spec/create_spec.rb
new file mode 100644
index 0000000..59abb5f
--- /dev/null
+++ b/examples/group/spec/create_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'group::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a group with the default action' do
+    expect(chef_run).to create_group('default_action')
+    expect(chef_run).to_not create_group('not_default_action')
+  end
+
+  it 'creates a group with an explicit action' do
+    expect(chef_run).to create_group('explicit_action')
+  end
+
+  it 'creates a group with attributes' do
+    expect(chef_run).to create_group('with_attributes').with(gid: 1234)
+    expect(chef_run).to_not create_group('with_attributes').with(gid: 5678)
+  end
+
+  it 'creates a group when specifying the identity attribute' do
+    expect(chef_run).to create_group('identity_attribute')
+  end
+end
diff --git a/examples/group/spec/manage_spec.rb b/examples/group/spec/manage_spec.rb
new file mode 100644
index 0000000..151b75f
--- /dev/null
+++ b/examples/group/spec/manage_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'group::manage' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'manages a group with an explicit action' do
+    expect(chef_run).to manage_group('explicit_action')
+    expect(chef_run).to_not manage_group('not_explicit_action')
+  end
+
+  it 'manages a group with attributes' do
+    expect(chef_run).to manage_group('with_attributes').with(gid: 1234)
+    expect(chef_run).to_not manage_group('with_attributes').with(gid: 5678)
+  end
+
+  it 'manages a group when specifying the identity attribute' do
+    expect(chef_run).to manage_group('identity_attribute')
+  end
+end
diff --git a/examples/group/spec/modify_spec.rb b/examples/group/spec/modify_spec.rb
new file mode 100644
index 0000000..b335aef
--- /dev/null
+++ b/examples/group/spec/modify_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'group::modify' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'modifies a group with an explicit action' do
+    expect(chef_run).to modify_group('explicit_action')
+    expect(chef_run).to_not modify_group('not_explicit_action')
+  end
+
+  it 'modifies a group with attributes' do
+    expect(chef_run).to modify_group('with_attributes').with(gid: 1234)
+    expect(chef_run).to_not modify_group('with_attributes').with(gid: 5678)
+  end
+
+  it 'modifies a group when specifying the identity attribute' do
+    expect(chef_run).to modify_group('identity_attribute')
+  end
+end
diff --git a/examples/group/spec/remove_spec.rb b/examples/group/spec/remove_spec.rb
new file mode 100644
index 0000000..616d0d4
--- /dev/null
+++ b/examples/group/spec/remove_spec.rb
@@ -0,0 +1,18 @@
+require 'chefspec'
+
+describe 'group::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a group with an explicit action' do
+    expect(chef_run).to remove_group('explicit_action')
+    expect(chef_run).to_not remove_group('not_explicit_action')
+  end
+
+  it 'removes a group with attributes' do
+    expect(chef_run).to_not remove_group('with_attributes').with(gid: 5678)
+  end
+
+  it 'removes a group when specifying the identity attribute' do
+    expect(chef_run).to remove_group('identity_attribute')
+  end
+end
diff --git a/examples/guards/recipes/default.rb b/examples/guards/recipes/default.rb
new file mode 100644
index 0000000..031b16b
--- /dev/null
+++ b/examples/guards/recipes/default.rb
@@ -0,0 +1,13 @@
+service 'true_guard' do
+  action  :start
+  only_if { 1 == 1 }
+end
+
+service 'false_guard' do
+  action :start
+  not_if { 1 == 1 }
+end
+
+service 'action_nothing_guard' do
+  action :nothing
+end
diff --git a/examples/guards/spec/default_spec.rb b/examples/guards/spec/default_spec.rb
new file mode 100644
index 0000000..b8d1588
--- /dev/null
+++ b/examples/guards/spec/default_spec.rb
@@ -0,0 +1,17 @@
+require 'chefspec'
+
+describe 'guards::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'includes resource that have guards that evalute to true' do
+    expect(chef_run).to start_service('true_guard')
+  end
+
+  it 'excludes resources that have guards evaluated to false' do
+    expect(chef_run).to_not start_service('false_guard')
+  end
+
+  it 'excludes resource that have action :nothing' do
+    expect(chef_run).to_not start_service('action_nothing_guard')
+  end
+end
diff --git a/examples/heavy_provider_light_resource/libraries/resource_service.rb b/examples/heavy_provider_light_resource/libraries/resource_service.rb
new file mode 100644
index 0000000..9190441
--- /dev/null
+++ b/examples/heavy_provider_light_resource/libraries/resource_service.rb
@@ -0,0 +1,15 @@
+require 'chef/resource/service'
+
+class ::Chef
+  class Resource
+    class HeavyProviderLightResourceService < ::Chef::Resource::Service
+      attr_accessor :root
+      def initialize(service_name, run_context=nil)
+        super
+        @resource_name = :mixed_resource
+        @action = :configure
+        @allowed_actions += [:configure]
+      end
+    end
+  end
+end
diff --git a/examples/heavy_provider_light_resource/providers/service.rb b/examples/heavy_provider_light_resource/providers/service.rb
new file mode 100644
index 0000000..9ab73c9
--- /dev/null
+++ b/examples/heavy_provider_light_resource/providers/service.rb
@@ -0,0 +1,9 @@
+def whyrun_supported?
+  true
+end
+
+use_inline_resources
+
+action :configure do
+  true
+end
diff --git a/examples/heavy_provider_light_resource/recipes/default.rb b/examples/heavy_provider_light_resource/recipes/default.rb
new file mode 100644
index 0000000..1700d32
--- /dev/null
+++ b/examples/heavy_provider_light_resource/recipes/default.rb
@@ -0,0 +1,4 @@
+heavy_provider_light_resource_service "here" do
+  action 'configure'
+end
+
diff --git a/examples/heavy_provider_light_resource/spec/provider_service_spec.rb b/examples/heavy_provider_light_resource/spec/provider_service_spec.rb
new file mode 100644
index 0000000..42ae597
--- /dev/null
+++ b/examples/heavy_provider_light_resource/spec/provider_service_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../libraries/resource_service'
+
+describe 'heavy_provider_light_resource::default' do
+  it "does not bomb (because the resource was not wiped out when the provider was encountered)" do
+    ChefSpec::SoloRunner.converge(described_recipe)
+  end
+end
diff --git a/examples/http_request/recipes/delete.rb b/examples/http_request/recipes/delete.rb
new file mode 100644
index 0000000..5a8bb50
--- /dev/null
+++ b/examples/http_request/recipes/delete.rb
@@ -0,0 +1,13 @@
+http_request 'explicit_action' do
+  action :delete
+end
+
+http_request 'with_attributes' do
+  url    'http://my.url'
+  action :delete
+end
+
+http_request 'specifying the identity attribute' do
+  url    'identity_attribute'
+  action :delete
+end
diff --git a/examples/http_request/recipes/get.rb b/examples/http_request/recipes/get.rb
new file mode 100644
index 0000000..780ecc2
--- /dev/null
+++ b/examples/http_request/recipes/get.rb
@@ -0,0 +1,13 @@
+http_request 'default_action'
+
+http_request 'explicit_action' do
+  action :get
+end
+
+http_request 'with_attributes' do
+  url 'http://my.url'
+end
+
+http_request 'specifying the identity attribute' do
+  url 'identity_attribute'
+end
diff --git a/examples/http_request/recipes/head.rb b/examples/http_request/recipes/head.rb
new file mode 100644
index 0000000..200e777
--- /dev/null
+++ b/examples/http_request/recipes/head.rb
@@ -0,0 +1,13 @@
+http_request 'explicit_action' do
+  action :head
+end
+
+http_request 'with_attributes' do
+  url    'http://my.url'
+  action :head
+end
+
+http_request 'specifying the identity attribute' do
+  url    'identity_attribute'
+  action :head
+end
diff --git a/examples/http_request/recipes/options.rb b/examples/http_request/recipes/options.rb
new file mode 100644
index 0000000..f329f52
--- /dev/null
+++ b/examples/http_request/recipes/options.rb
@@ -0,0 +1,13 @@
+http_request 'explicit_action' do
+  action :options
+end
+
+http_request 'with_attributes' do
+  url    'http://my.url'
+  action :options
+end
+
+http_request 'specifying the identity attribute' do
+  url    'identity_attribute'
+  action :options
+end
diff --git a/examples/http_request/recipes/post.rb b/examples/http_request/recipes/post.rb
new file mode 100644
index 0000000..b39aed1
--- /dev/null
+++ b/examples/http_request/recipes/post.rb
@@ -0,0 +1,13 @@
+http_request 'explicit_action' do
+  action :post
+end
+
+http_request 'with_attributes' do
+  url    'http://my.url'
+  action :post
+end
+
+http_request 'specifying the identity attribute' do
+  url    'identity_attribute'
+  action :post
+end
diff --git a/examples/http_request/recipes/put.rb b/examples/http_request/recipes/put.rb
new file mode 100644
index 0000000..ff9bd7f
--- /dev/null
+++ b/examples/http_request/recipes/put.rb
@@ -0,0 +1,13 @@
+http_request 'explicit_action' do
+  action :put
+end
+
+http_request 'with_attributes' do
+  url    'http://my.url'
+  action :put
+end
+
+http_request 'specifying the identity attribute' do
+  url    'identity_attribute'
+  action :put
+end
diff --git a/examples/http_request/spec/delete_spec.rb b/examples/http_request/spec/delete_spec.rb
new file mode 100644
index 0000000..f25623e
--- /dev/null
+++ b/examples/http_request/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'http_request::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a http_request with an explicit action' do
+    expect(chef_run).to delete_http_request('explicit_action')
+    expect(chef_run).to_not delete_http_request('not_explicit_action')
+  end
+
+  it 'deletes a http_request with attributes' do
+    expect(chef_run).to delete_http_request('with_attributes').with(url: 'http://my.url')
+    expect(chef_run).to_not delete_http_request('with_attributes').with(url: 'http://my.other.url')
+  end
+
+  it 'deletes a http_request when specifying the identity attribute' do
+    expect(chef_run).to delete_http_request('identity_attribute')
+  end
+end
diff --git a/examples/http_request/spec/get_spec.rb b/examples/http_request/spec/get_spec.rb
new file mode 100644
index 0000000..2021e8a
--- /dev/null
+++ b/examples/http_request/spec/get_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'http_request::get' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'gets a http_request with the default action' do
+    expect(chef_run).to get_http_request('default_action')
+    expect(chef_run).to_not get_http_request('not_default_action')
+  end
+
+  it 'gets a http_request with an explicit action' do
+    expect(chef_run).to get_http_request('explicit_action')
+  end
+
+  it 'gets a http_request with attributes' do
+    expect(chef_run).to get_http_request('with_attributes').with(url: 'http://my.url')
+    expect(chef_run).to_not get_http_request('with_attributes').with(url: 'http://my.other.url')
+  end
+
+  it 'gets a http_request when specifying the identity attribute' do
+    expect(chef_run).to get_http_request('identity_attribute')
+  end
+end
diff --git a/examples/http_request/spec/head_spec.rb b/examples/http_request/spec/head_spec.rb
new file mode 100644
index 0000000..368f045
--- /dev/null
+++ b/examples/http_request/spec/head_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'http_request::head' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'heads a http_request with an explicit action' do
+    expect(chef_run).to head_http_request('explicit_action')
+    expect(chef_run).to_not head_http_request('not_explicit_action')
+  end
+
+  it 'heads a http_request with attributes' do
+    expect(chef_run).to head_http_request('with_attributes').with(url: 'http://my.url')
+    expect(chef_run).to_not head_http_request('with_attributes').with(url: 'http://my.other.url')
+  end
+
+  it 'heads a http_request when specifying the identity attribute' do
+    expect(chef_run).to head_http_request('identity_attribute')
+  end
+end
diff --git a/examples/http_request/spec/options_spec.rb b/examples/http_request/spec/options_spec.rb
new file mode 100644
index 0000000..4cc9788
--- /dev/null
+++ b/examples/http_request/spec/options_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'http_request::options' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'optionss a http_request with an explicit action' do
+    expect(chef_run).to options_http_request('explicit_action')
+    expect(chef_run).to_not options_http_request('not_explicit_action')
+  end
+
+  it 'optionss a http_request with attributes' do
+    expect(chef_run).to options_http_request('with_attributes').with(url: 'http://my.url')
+    expect(chef_run).to_not options_http_request('with_attributes').with(url: 'http://my.other.url')
+  end
+
+  it 'optionss a http_request when specifying the identity attribute' do
+    expect(chef_run).to options_http_request('identity_attribute')
+  end
+end
diff --git a/examples/http_request/spec/post_spec.rb b/examples/http_request/spec/post_spec.rb
new file mode 100644
index 0000000..6f34b9b
--- /dev/null
+++ b/examples/http_request/spec/post_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'http_request::post' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'posts a http_request with an explicit action' do
+    expect(chef_run).to post_http_request('explicit_action')
+    expect(chef_run).to_not post_http_request('not_explicit_action')
+  end
+
+  it 'posts a http_request with attributes' do
+    expect(chef_run).to post_http_request('with_attributes').with(url: 'http://my.url')
+    expect(chef_run).to_not post_http_request('with_attributes').with(url: 'http://my.other.url')
+  end
+
+  it 'posts a http_request when specifying the identity attribute' do
+    expect(chef_run).to post_http_request('identity_attribute')
+  end
+end
diff --git a/examples/http_request/spec/put_spec.rb b/examples/http_request/spec/put_spec.rb
new file mode 100644
index 0000000..7cf94c0
--- /dev/null
+++ b/examples/http_request/spec/put_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'http_request::put' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'puts a http_request with an explicit action' do
+    expect(chef_run).to put_http_request('explicit_action')
+    expect(chef_run).to_not put_http_request('not_explicit_action')
+  end
+
+  it 'puts a http_request with attributes' do
+    expect(chef_run).to put_http_request('with_attributes').with(url: 'http://my.url')
+    expect(chef_run).to_not put_http_request('with_attributes').with(url: 'http://my.other.url')
+  end
+
+  it 'puts a http_request when specifying the identity attribute' do
+    expect(chef_run).to put_http_request('identity_attribute')
+  end
+end
diff --git a/examples/ifconfig/recipes/add.rb b/examples/ifconfig/recipes/add.rb
new file mode 100644
index 0000000..bd237f1
--- /dev/null
+++ b/examples/ifconfig/recipes/add.rb
@@ -0,0 +1,9 @@
+ifconfig '10.0.0.1'
+
+ifconfig '10.0.0.2' do
+  action :add
+end
+
+ifconfig '10.0.0.3' do
+  device 'en0'
+end
diff --git a/examples/ifconfig/recipes/delete.rb b/examples/ifconfig/recipes/delete.rb
new file mode 100644
index 0000000..7669e49
--- /dev/null
+++ b/examples/ifconfig/recipes/delete.rb
@@ -0,0 +1,8 @@
+ifconfig '10.0.0.2' do
+  action :delete
+end
+
+ifconfig '10.0.0.3' do
+  device 'en0'
+  action :delete
+end
diff --git a/examples/ifconfig/recipes/disable.rb b/examples/ifconfig/recipes/disable.rb
new file mode 100644
index 0000000..e97c986
--- /dev/null
+++ b/examples/ifconfig/recipes/disable.rb
@@ -0,0 +1,8 @@
+ifconfig '10.0.0.2' do
+  action :disable
+end
+
+ifconfig '10.0.0.3' do
+  device 'en0'
+  action :disable
+end
diff --git a/examples/ifconfig/recipes/enable.rb b/examples/ifconfig/recipes/enable.rb
new file mode 100644
index 0000000..6e80ab0
--- /dev/null
+++ b/examples/ifconfig/recipes/enable.rb
@@ -0,0 +1,8 @@
+ifconfig '10.0.0.2' do
+  action :enable
+end
+
+ifconfig '10.0.0.3' do
+  device 'en0'
+  action :enable
+end
diff --git a/examples/ifconfig/spec/add_spec.rb b/examples/ifconfig/spec/add_spec.rb
new file mode 100644
index 0000000..6cfb7dd
--- /dev/null
+++ b/examples/ifconfig/spec/add_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'ifconfig::add' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'adds a ifconfig with the default action' do
+    expect(chef_run).to add_ifconfig('10.0.0.1')
+    expect(chef_run).to_not add_ifconfig('10.0.0.10')
+  end
+
+  it 'adds a ifconfig with an explicit action' do
+    expect(chef_run).to add_ifconfig('10.0.0.2')
+  end
+
+  it 'adds a ifconfig with attributes' do
+    expect(chef_run).to add_ifconfig('10.0.0.3').with(device: 'en0')
+    expect(chef_run).to_not add_ifconfig('10.0.0.3').with(device: 'en1')
+  end
+end
diff --git a/examples/ifconfig/spec/delete_spec.rb b/examples/ifconfig/spec/delete_spec.rb
new file mode 100644
index 0000000..c2f151b
--- /dev/null
+++ b/examples/ifconfig/spec/delete_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'ifconfig::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a ifconfig with an explicit action' do
+    expect(chef_run).to delete_ifconfig('10.0.0.2')
+    expect(chef_run).to_not delete_ifconfig('10.0.0.10')
+  end
+
+  it 'deletes a ifconfig with attributes' do
+    expect(chef_run).to delete_ifconfig('10.0.0.3').with(device: 'en0')
+    expect(chef_run).to_not delete_ifconfig('10.0.0.3').with(device: 'en1')
+  end
+end
diff --git a/examples/ifconfig/spec/disable_spec.rb b/examples/ifconfig/spec/disable_spec.rb
new file mode 100644
index 0000000..6c47ba2
--- /dev/null
+++ b/examples/ifconfig/spec/disable_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'ifconfig::disable' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'disables a ifconfig with an explicit action' do
+    expect(chef_run).to disable_ifconfig('10.0.0.2')
+    expect(chef_run).to_not disable_ifconfig('10.0.0.10')
+  end
+
+  it 'disables a ifconfig with attributes' do
+    expect(chef_run).to disable_ifconfig('10.0.0.3').with(device: 'en0')
+    expect(chef_run).to_not disable_ifconfig('10.0.0.3').with(device: 'en1')
+  end
+end
diff --git a/examples/ifconfig/spec/enable_spec.rb b/examples/ifconfig/spec/enable_spec.rb
new file mode 100644
index 0000000..e4604ef
--- /dev/null
+++ b/examples/ifconfig/spec/enable_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'ifconfig::enable' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'enables a ifconfig with an explicit action' do
+    expect(chef_run).to enable_ifconfig('10.0.0.2')
+    expect(chef_run).to_not enable_ifconfig('10.0.0.10')
+  end
+
+  it 'enables a ifconfig with attributes' do
+    expect(chef_run).to enable_ifconfig('10.0.0.3').with(device: 'en0')
+    expect(chef_run).to_not enable_ifconfig('10.0.0.3').with(device: 'en1')
+  end
+end
diff --git a/examples/include_recipe/recipes/default.rb b/examples/include_recipe/recipes/default.rb
new file mode 100644
index 0000000..34bd0ed
--- /dev/null
+++ b/examples/include_recipe/recipes/default.rb
@@ -0,0 +1 @@
+include_recipe 'include_recipe::other'
diff --git a/examples/include_recipe/recipes/not.rb b/examples/include_recipe/recipes/not.rb
new file mode 100644
index 0000000..e69de29
diff --git a/examples/include_recipe/recipes/other.rb b/examples/include_recipe/recipes/other.rb
new file mode 100644
index 0000000..e69de29
diff --git a/examples/include_recipe/spec/default_spec.rb b/examples/include_recipe/spec/default_spec.rb
new file mode 100644
index 0000000..feefc3e
--- /dev/null
+++ b/examples/include_recipe/spec/default_spec.rb
@@ -0,0 +1,13 @@
+require 'chefspec'
+
+describe 'include_recipe::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'includes the `other` recipe' do
+    expect(chef_run).to include_recipe('include_recipe::other')
+  end
+
+  it 'does not include the `not` recipe' do
+    expect(chef_run).to_not include_recipe('include_recipe::not')
+  end
+end
diff --git a/examples/ips_package/recipes/install.rb b/examples/ips_package/recipes/install.rb
new file mode 100644
index 0000000..a18aa9d
--- /dev/null
+++ b/examples/ips_package/recipes/install.rb
@@ -0,0 +1,13 @@
+ips_package 'default_action'
+
+ips_package 'explicit_action' do
+  action :install
+end
+
+ips_package 'with_attributes' do
+  version '1.0.0'
+end
+
+ips_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/ips_package/recipes/remove.rb b/examples/ips_package/recipes/remove.rb
new file mode 100644
index 0000000..bccc6b7
--- /dev/null
+++ b/examples/ips_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+ips_package 'explicit_action' do
+  action :remove
+end
+
+ips_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+ips_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/ips_package/recipes/upgrade.rb b/examples/ips_package/recipes/upgrade.rb
new file mode 100644
index 0000000..5636217
--- /dev/null
+++ b/examples/ips_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+ips_package 'explicit_action' do
+  action :upgrade
+end
+
+ips_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+ips_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/ips_package/spec/install_spec.rb b/examples/ips_package/spec/install_spec.rb
new file mode 100644
index 0000000..2d9091e
--- /dev/null
+++ b/examples/ips_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'ips_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a ips_package with the default action' do
+    expect(chef_run).to install_ips_package('default_action')
+    expect(chef_run).to_not install_ips_package('not_default_action')
+  end
+
+  it 'installs a ips_package with an explicit action' do
+    expect(chef_run).to install_ips_package('explicit_action')
+  end
+
+  it 'installs a ips_package with attributes' do
+    expect(chef_run).to install_ips_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_ips_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a ips_package when specifying the identity attribute' do
+    expect(chef_run).to install_ips_package('identity_attribute')
+  end
+end
diff --git a/examples/ips_package/spec/remove_spec.rb b/examples/ips_package/spec/remove_spec.rb
new file mode 100644
index 0000000..27065e6
--- /dev/null
+++ b/examples/ips_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'ips_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a ips_package with an explicit action' do
+    expect(chef_run).to remove_ips_package('explicit_action')
+    expect(chef_run).to_not remove_ips_package('not_explicit_action')
+  end
+
+  it 'removes a ips_package with attributes' do
+    expect(chef_run).to remove_ips_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_ips_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a ips_package when specifying the identity attribute' do
+    expect(chef_run).to remove_ips_package('identity_attribute')
+  end
+end
diff --git a/examples/ips_package/spec/upgrade_spec.rb b/examples/ips_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..2db3963
--- /dev/null
+++ b/examples/ips_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'ips_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a ips_package with an explicit action' do
+    expect(chef_run).to upgrade_ips_package('explicit_action')
+    expect(chef_run).to_not upgrade_ips_package('not_explicit_action')
+  end
+
+  it 'upgrades a ips_package with attributes' do
+    expect(chef_run).to upgrade_ips_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_ips_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a ips_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_ips_package('identity_attribute')
+  end
+end
diff --git a/examples/link/recipes/create.rb b/examples/link/recipes/create.rb
new file mode 100644
index 0000000..3feb71c
--- /dev/null
+++ b/examples/link/recipes/create.rb
@@ -0,0 +1,13 @@
+link '/tmp/default_action'
+
+link '/tmp/explicit_action' do
+  action :create
+end
+
+link '/tmp/with_attributes' do
+  to 'destination'
+end
+
+link 'specifying the identity attribute' do
+  target_file '/tmp/identity_attribute'
+end
diff --git a/examples/link/recipes/delete.rb b/examples/link/recipes/delete.rb
new file mode 100644
index 0000000..d8c107e
--- /dev/null
+++ b/examples/link/recipes/delete.rb
@@ -0,0 +1,13 @@
+link '/tmp/explicit_action' do
+  action :delete
+end
+
+link '/tmp/with_attributes' do
+  to     'destination'
+  action :delete
+end
+
+link 'specifying the identity attribute' do
+  target_file '/tmp/identity_attribute'
+  action      :delete
+end
diff --git a/examples/link/recipes/link_to.rb b/examples/link/recipes/link_to.rb
new file mode 100644
index 0000000..9745b3d
--- /dev/null
+++ b/examples/link/recipes/link_to.rb
@@ -0,0 +1,3 @@
+link '/tmp/path' do
+  to 'destination'
+end
diff --git a/examples/link/spec/create_spec.rb b/examples/link/spec/create_spec.rb
new file mode 100644
index 0000000..c2950f1
--- /dev/null
+++ b/examples/link/spec/create_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'link::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a link with the default action' do
+    expect(chef_run).to create_link('/tmp/default_action')
+    expect(chef_run).to_not create_link('/tmp/not_default_action')
+  end
+
+  it 'creates a link with an explicit action' do
+    expect(chef_run).to create_link('/tmp/explicit_action')
+  end
+
+  it 'creates a link with attributes' do
+    expect(chef_run).to create_link('/tmp/with_attributes').with(to: 'destination')
+    expect(chef_run).to_not create_link('/tmp/with_attributes').with(to: 'other_destination')
+  end
+
+  it 'creates a link when specifying the identity attribute' do
+    expect(chef_run).to create_link('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/link/spec/delete_spec.rb b/examples/link/spec/delete_spec.rb
new file mode 100644
index 0000000..739bcc5
--- /dev/null
+++ b/examples/link/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'link::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a link with an explicit action' do
+    expect(chef_run).to delete_link('/tmp/explicit_action')
+    expect(chef_run).to_not delete_link('/tmp/not_explicit_action')
+  end
+
+  it 'deletes a link with attributes' do
+    expect(chef_run).to delete_link('/tmp/with_attributes').with(to: 'destination')
+    expect(chef_run).to_not delete_link('/tmp/with_attributes').with(to: 'other_destination')
+  end
+
+  it 'deletes a link when specifying the identity attribute' do
+    expect(chef_run).to delete_link('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/link/spec/link_to_spec.rb b/examples/link/spec/link_to_spec.rb
new file mode 100644
index 0000000..194fd16
--- /dev/null
+++ b/examples/link/spec/link_to_spec.rb
@@ -0,0 +1,11 @@
+require 'chefspec'
+
+describe 'link::link_to' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a link to the specified target' do
+    link = chef_run.link('/tmp/path')
+    expect(link).to link_to('destination')
+    expect(link).to_not link_to('other_destination')
+  end
+end
diff --git a/examples/log/recipes/write.rb b/examples/log/recipes/write.rb
new file mode 100644
index 0000000..0519ecb
--- /dev/null
+++ b/examples/log/recipes/write.rb
@@ -0,0 +1,14 @@
+log 'default_action'
+
+# CHEF-4561
+# log 'explicit_action' do
+#   action :write
+# end
+
+log 'with_attributes' do
+  level :debug
+end
+
+log 'specifying the identity attribute' do
+  message 'identity_attribute'
+end
diff --git a/examples/log/spec/write_spec.rb b/examples/log/spec/write_spec.rb
new file mode 100644
index 0000000..fbc8c68
--- /dev/null
+++ b/examples/log/spec/write_spec.rb
@@ -0,0 +1,24 @@
+require 'chefspec'
+
+describe 'log::write' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'writes a log with the default action' do
+    expect(chef_run).to write_log('default_action')
+    expect(chef_run).to_not write_log('not_default_action')
+  end
+
+  # CHEF-4561
+  # it 'writes a log with an explicit action' do
+  #   expect(chef_run).to write_log('explicit_action')
+  # end
+
+  it 'writes a log with attributes' do
+    expect(chef_run).to write_log('with_attributes').with(level: :debug)
+    expect(chef_run).to_not write_log('with_attributes').with(level: :info)
+  end
+
+  it 'writes a log when specifying the identity attribute' do
+    expect(chef_run).to write_log('identity_attribute')
+  end
+end
diff --git a/examples/macports_package/recipes/install.rb b/examples/macports_package/recipes/install.rb
new file mode 100644
index 0000000..a75ba23
--- /dev/null
+++ b/examples/macports_package/recipes/install.rb
@@ -0,0 +1,13 @@
+macports_package 'default_action'
+
+macports_package 'explicit_action' do
+  action :install
+end
+
+macports_package 'with_attributes' do
+  version '1.0.0'
+end
+
+macports_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/macports_package/recipes/purge.rb b/examples/macports_package/recipes/purge.rb
new file mode 100644
index 0000000..beb753b
--- /dev/null
+++ b/examples/macports_package/recipes/purge.rb
@@ -0,0 +1,13 @@
+macports_package 'explicit_action' do
+  action :purge
+end
+
+macports_package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+macports_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/macports_package/recipes/remove.rb b/examples/macports_package/recipes/remove.rb
new file mode 100644
index 0000000..76d46ff
--- /dev/null
+++ b/examples/macports_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+macports_package 'explicit_action' do
+  action :remove
+end
+
+macports_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+macports_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/macports_package/recipes/upgrade.rb b/examples/macports_package/recipes/upgrade.rb
new file mode 100644
index 0000000..8cbd53e
--- /dev/null
+++ b/examples/macports_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+macports_package 'explicit_action' do
+  action :upgrade
+end
+
+macports_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+macports_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/macports_package/spec/install_spec.rb b/examples/macports_package/spec/install_spec.rb
new file mode 100644
index 0000000..c8c379d
--- /dev/null
+++ b/examples/macports_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'macports_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a macports_package with the default action' do
+    expect(chef_run).to install_macports_package('default_action')
+    expect(chef_run).to_not install_macports_package('not_default_action')
+  end
+
+  it 'installs a macports_package with an explicit action' do
+    expect(chef_run).to install_macports_package('explicit_action')
+  end
+
+  it 'installs a macports_package with attributes' do
+    expect(chef_run).to install_macports_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_macports_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a macports_package when specifying the identity attribute' do
+    expect(chef_run).to install_macports_package('identity_attribute')
+  end
+end
diff --git a/examples/macports_package/spec/purge_spec.rb b/examples/macports_package/spec/purge_spec.rb
new file mode 100644
index 0000000..7805b63
--- /dev/null
+++ b/examples/macports_package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'macports_package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a macports_package with an explicit action' do
+    expect(chef_run).to purge_macports_package('explicit_action')
+    expect(chef_run).to_not purge_macports_package('not_explicit_action')
+  end
+
+  it 'purges a macports_package with attributes' do
+    expect(chef_run).to purge_macports_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_macports_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a macports_package when specifying the identity attribute' do
+    expect(chef_run).to purge_macports_package('identity_attribute')
+  end
+end
diff --git a/examples/macports_package/spec/remove_spec.rb b/examples/macports_package/spec/remove_spec.rb
new file mode 100644
index 0000000..a6caef6
--- /dev/null
+++ b/examples/macports_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'macports_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a macports_package with an explicit action' do
+    expect(chef_run).to remove_macports_package('explicit_action')
+    expect(chef_run).to_not remove_macports_package('not_explicit_action')
+  end
+
+  it 'removes a macports_package with attributes' do
+    expect(chef_run).to remove_macports_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_macports_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a macports_package when specifying the identity attribute' do
+    expect(chef_run).to remove_macports_package('identity_attribute')
+  end
+end
diff --git a/examples/macports_package/spec/upgrade_spec.rb b/examples/macports_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..b1b8b05
--- /dev/null
+++ b/examples/macports_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'macports_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a macports_package with an explicit action' do
+    expect(chef_run).to upgrade_macports_package('explicit_action')
+    expect(chef_run).to_not upgrade_macports_package('not_explicit_action')
+  end
+
+  it 'upgrades a macports_package with attributes' do
+    expect(chef_run).to upgrade_macports_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_macports_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a macports_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_macports_package('identity_attribute')
+  end
+end
diff --git a/examples/mdadm/recipes/assemble.rb b/examples/mdadm/recipes/assemble.rb
new file mode 100644
index 0000000..00b8334
--- /dev/null
+++ b/examples/mdadm/recipes/assemble.rb
@@ -0,0 +1,13 @@
+mdadm 'explicit_action' do
+  action :assemble
+end
+
+mdadm 'with_attributes' do
+  chunk  8
+  action :assemble
+end
+
+mdadm 'specifying the identity attribute' do
+  raid_device 'identity_attribute'
+  action      :assemble
+end
diff --git a/examples/mdadm/recipes/create.rb b/examples/mdadm/recipes/create.rb
new file mode 100644
index 0000000..8355b20
--- /dev/null
+++ b/examples/mdadm/recipes/create.rb
@@ -0,0 +1,13 @@
+mdadm 'default_action'
+
+mdadm 'explicit_action' do
+  action :create
+end
+
+mdadm 'with_attributes' do
+  chunk 8
+end
+
+mdadm 'specifying the identity attribute' do
+  raid_device 'identity_attribute'
+end
diff --git a/examples/mdadm/recipes/stop.rb b/examples/mdadm/recipes/stop.rb
new file mode 100644
index 0000000..32ed071
--- /dev/null
+++ b/examples/mdadm/recipes/stop.rb
@@ -0,0 +1,13 @@
+mdadm 'explicit_action' do
+  action :stop
+end
+
+mdadm 'with_attributes' do
+  chunk  8
+  action :stop
+end
+
+mdadm 'specifying the identity attribute' do
+  raid_device 'identity_attribute'
+  action      :stop
+end
diff --git a/examples/mdadm/spec/assemble_spec.rb b/examples/mdadm/spec/assemble_spec.rb
new file mode 100644
index 0000000..d33be94
--- /dev/null
+++ b/examples/mdadm/spec/assemble_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'mdadm::assemble' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'assembles a mdadm with an explicit action' do
+    expect(chef_run).to assemble_mdadm('explicit_action')
+    expect(chef_run).to_not assemble_mdadm('not_explicit_action')
+  end
+
+  it 'assembles a mdadm with attributes' do
+    expect(chef_run).to assemble_mdadm('with_attributes').with(chunk: 8)
+    expect(chef_run).to_not assemble_mdadm('with_attributes').with(chunk: 3)
+  end
+
+  it 'assembles a mdadm when specifying the identity attribute' do
+    expect(chef_run).to assemble_mdadm('identity_attribute')
+  end
+end
diff --git a/examples/mdadm/spec/create_spec.rb b/examples/mdadm/spec/create_spec.rb
new file mode 100644
index 0000000..4c38cab
--- /dev/null
+++ b/examples/mdadm/spec/create_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'mdadm::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a mdadm with the default action' do
+    expect(chef_run).to create_mdadm('default_action')
+    expect(chef_run).to_not create_mdadm('not_default_action')
+  end
+
+  it 'creates a mdadm with an explicit action' do
+    expect(chef_run).to create_mdadm('explicit_action')
+  end
+
+  it 'creates a mdadm with attributes' do
+    expect(chef_run).to create_mdadm('with_attributes').with(chunk: 8)
+    expect(chef_run).to_not create_mdadm('with_attributes').with(chunk: 3)
+  end
+
+  it 'creates a mdadm when specifying the identity attribute' do
+    expect(chef_run).to create_mdadm('identity_attribute')
+  end
+end
diff --git a/examples/mdadm/spec/stop_spec.rb b/examples/mdadm/spec/stop_spec.rb
new file mode 100644
index 0000000..5c254e6
--- /dev/null
+++ b/examples/mdadm/spec/stop_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'mdadm::stop' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'stops a mdadm with an explicit action' do
+    expect(chef_run).to stop_mdadm('explicit_action')
+    expect(chef_run).to_not stop_mdadm('not_explicit_action')
+  end
+
+  it 'stops a mdadm with attributes' do
+    expect(chef_run).to stop_mdadm('with_attributes').with(chunk: 8)
+    expect(chef_run).to_not stop_mdadm('with_attributes').with(chunk: 3)
+  end
+
+  it 'stops a mdadm when specifying the identity attribute' do
+    expect(chef_run).to stop_mdadm('identity_attribute')
+  end
+end
diff --git a/examples/mount/recipes/disable.rb b/examples/mount/recipes/disable.rb
new file mode 100644
index 0000000..1bb362e
--- /dev/null
+++ b/examples/mount/recipes/disable.rb
@@ -0,0 +1,8 @@
+mount '/tmp/explicit_action' do
+  action :disable
+end
+
+mount '/tmp/with_attributes' do
+  dump   3
+  action :disable
+end
diff --git a/examples/mount/recipes/enable.rb b/examples/mount/recipes/enable.rb
new file mode 100644
index 0000000..3b3fbf2
--- /dev/null
+++ b/examples/mount/recipes/enable.rb
@@ -0,0 +1,8 @@
+mount '/tmp/explicit_action' do
+  action :enable
+end
+
+mount '/tmp/with_attributes' do
+  dump   3
+  action :enable
+end
diff --git a/examples/mount/recipes/mount.rb b/examples/mount/recipes/mount.rb
new file mode 100644
index 0000000..e44e6f9
--- /dev/null
+++ b/examples/mount/recipes/mount.rb
@@ -0,0 +1,9 @@
+mount '/tmp/default_action'
+
+mount '/tmp/explicit_action' do
+  action :mount
+end
+
+mount '/tmp/with_attributes' do
+  dump 3
+end
diff --git a/examples/mount/recipes/remount.rb b/examples/mount/recipes/remount.rb
new file mode 100644
index 0000000..0108ab4
--- /dev/null
+++ b/examples/mount/recipes/remount.rb
@@ -0,0 +1,8 @@
+mount '/tmp/explicit_action' do
+  action :remount
+end
+
+mount '/tmp/with_attributes' do
+  dump   3
+  action :remount
+end
diff --git a/examples/mount/recipes/umount.rb b/examples/mount/recipes/umount.rb
new file mode 100644
index 0000000..8506845
--- /dev/null
+++ b/examples/mount/recipes/umount.rb
@@ -0,0 +1,8 @@
+mount '/tmp/explicit_action' do
+  action :umount
+end
+
+mount '/tmp/with_attributes' do
+  dump   3
+  action :umount
+end
diff --git a/examples/mount/spec/disable_spec.rb b/examples/mount/spec/disable_spec.rb
new file mode 100644
index 0000000..b524480
--- /dev/null
+++ b/examples/mount/spec/disable_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'mount::disable' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'disables a mount with an explicit action' do
+    expect(chef_run).to disable_mount('/tmp/explicit_action')
+    expect(chef_run).to_not disable_mount('/tmp/not_explicit_action')
+  end
+
+  it 'disables a mount with attributes' do
+    expect(chef_run).to disable_mount('/tmp/with_attributes').with(dump: 3)
+    expect(chef_run).to_not disable_mount('/tmp/with_attributes').with(dump: 5)
+  end
+end
diff --git a/examples/mount/spec/enable_spec.rb b/examples/mount/spec/enable_spec.rb
new file mode 100644
index 0000000..aa78cc6
--- /dev/null
+++ b/examples/mount/spec/enable_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'mount::enable' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'enables a mount with an explicit action' do
+    expect(chef_run).to enable_mount('/tmp/explicit_action')
+    expect(chef_run).to_not enable_mount('/tmp/not_explicit_action')
+  end
+
+  it 'enables a mount with attributes' do
+    expect(chef_run).to enable_mount('/tmp/with_attributes').with(dump: 3)
+    expect(chef_run).to_not enable_mount('/tmp/with_attributes').with(dump: 5)
+  end
+end
diff --git a/examples/mount/spec/mount_spec.rb b/examples/mount/spec/mount_spec.rb
new file mode 100644
index 0000000..0647170
--- /dev/null
+++ b/examples/mount/spec/mount_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'mount::mount' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'mounts a mount with the default action' do
+    expect(chef_run).to mount_mount('/tmp/default_action')
+    expect(chef_run).to_not mount_mount('/tmp/not_default_action')
+  end
+
+  it 'mounts a mount with an explicit action' do
+    expect(chef_run).to mount_mount('/tmp/explicit_action')
+  end
+
+  it 'mounts a mount with attributes' do
+    expect(chef_run).to mount_mount('/tmp/with_attributes').with(dump: 3)
+    expect(chef_run).to_not mount_mount('/tmp/with_attributes').with(dump: 5)
+  end
+end
diff --git a/examples/mount/spec/remount_spec.rb b/examples/mount/spec/remount_spec.rb
new file mode 100644
index 0000000..9c5bb30
--- /dev/null
+++ b/examples/mount/spec/remount_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'mount::remount' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'remounts a mount with an explicit action' do
+    expect(chef_run).to remount_mount('/tmp/explicit_action')
+    expect(chef_run).to_not remount_mount('/tmp/not_explicit_action')
+  end
+
+  it 'remounts a mount with attributes' do
+    expect(chef_run).to remount_mount('/tmp/with_attributes').with(dump: 3)
+    expect(chef_run).to_not remount_mount('/tmp/with_attributes').with(dump: 5)
+  end
+end
diff --git a/examples/mount/spec/umount_spec.rb b/examples/mount/spec/umount_spec.rb
new file mode 100644
index 0000000..efddd5f
--- /dev/null
+++ b/examples/mount/spec/umount_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+
+describe 'mount::umount' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'umounts a mount with an explicit action' do
+    expect(chef_run).to umount_mount('/tmp/explicit_action')
+    expect(chef_run).to_not umount_mount('/tmp/not_explicit_action')
+  end
+
+  it 'umounts a mount with attributes' do
+    expect(chef_run).to umount_mount('/tmp/with_attributes').with(dump: 3)
+    expect(chef_run).to_not umount_mount('/tmp/with_attributes').with(dump: 5)
+  end
+end
diff --git a/examples/multiple_actions/recipes/default.rb b/examples/multiple_actions/recipes/default.rb
new file mode 100644
index 0000000..3093b77
--- /dev/null
+++ b/examples/multiple_actions/recipes/default.rb
@@ -0,0 +1,3 @@
+service 'resource' do
+  action [:enable, :start]
+end
diff --git a/examples/multiple_actions/recipes/sequential.rb b/examples/multiple_actions/recipes/sequential.rb
new file mode 100644
index 0000000..72dfb29
--- /dev/null
+++ b/examples/multiple_actions/recipes/sequential.rb
@@ -0,0 +1,7 @@
+service 'resource' do
+  action :start
+end
+
+service 'resource' do
+  action :nothing
+end
diff --git a/examples/multiple_actions/spec/default_spec.rb b/examples/multiple_actions/spec/default_spec.rb
new file mode 100644
index 0000000..353ff5c
--- /dev/null
+++ b/examples/multiple_actions/spec/default_spec.rb
@@ -0,0 +1,14 @@
+require 'chefspec'
+
+describe 'multiple_actions::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'executes both actions' do
+    expect(chef_run).to enable_service('resource')
+    expect(chef_run).to start_service('resource')
+  end
+
+  it 'does not match other actions' do
+    expect(chef_run).to_not disable_service('resource')
+  end
+end
diff --git a/examples/multiple_actions/spec/sequential_spec.rb b/examples/multiple_actions/spec/sequential_spec.rb
new file mode 100644
index 0000000..acd138f
--- /dev/null
+++ b/examples/multiple_actions/spec/sequential_spec.rb
@@ -0,0 +1,16 @@
+require 'chefspec'
+
+describe 'multiple_actions::sequential' do
+  let(:chef_run) do
+    ChefSpec::SoloRunner.new(log_level: :fatal)
+      .converge(described_recipe)
+  end
+
+  it 'executes both actions' do
+    expect(chef_run).to start_service('resource')
+  end
+
+  it 'does not match other actions' do
+    expect(chef_run).to_not disable_service('resource')
+  end
+end
diff --git a/examples/multiple_run_action/recipes/default.rb b/examples/multiple_run_action/recipes/default.rb
new file mode 100644
index 0000000..6e8ba5c
--- /dev/null
+++ b/examples/multiple_run_action/recipes/default.rb
@@ -0,0 +1,5 @@
+resource = template('/tmp/resource') do
+             action :create
+           end
+
+resource.run_action(:touch)
diff --git a/examples/multiple_run_action/spec/default_spec.rb b/examples/multiple_run_action/spec/default_spec.rb
new file mode 100644
index 0000000..c147211
--- /dev/null
+++ b/examples/multiple_run_action/spec/default_spec.rb
@@ -0,0 +1,18 @@
+require 'chefspec'
+
+describe 'multiple_run_action::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'includes the action explicitly given to the resource' do
+    expect(chef_run).to create_template('/tmp/resource')
+  end
+
+  it 'includes an action specific called in #run_action' do
+    expect(chef_run).to touch_template('/tmp/resource')
+  end
+
+  it 'does not include something random' do
+    template = chef_run.template('/tmp/resource')
+    expect(template.performed_actions).to_not include(:bacon)
+  end
+end
diff --git a/examples/notifications/recipes/chained.rb b/examples/notifications/recipes/chained.rb
new file mode 100644
index 0000000..7b96efd
--- /dev/null
+++ b/examples/notifications/recipes/chained.rb
@@ -0,0 +1,12 @@
+template 'template' do
+  notifies :restart, 'service[service]'
+end
+
+service 'service' do
+  action :nothing
+  notifies :write, 'log[log]'
+end
+
+log 'log' do
+  action :nothing
+end
diff --git a/examples/notifications/recipes/default.rb b/examples/notifications/recipes/default.rb
new file mode 100644
index 0000000..b8221bf
--- /dev/null
+++ b/examples/notifications/recipes/default.rb
@@ -0,0 +1,7 @@
+template '/tmp/notifying_resource' do
+  notifies :restart, 'service[receiving_resource]'
+end
+
+service 'receiving_resource' do
+  action :nothing
+end
diff --git a/examples/notifications/recipes/delayed.rb b/examples/notifications/recipes/delayed.rb
new file mode 100644
index 0000000..04702c7
--- /dev/null
+++ b/examples/notifications/recipes/delayed.rb
@@ -0,0 +1,7 @@
+template '/tmp/notifying_resource' do
+  notifies :restart, 'service[receiving_resource]', :delayed
+end
+
+service 'receiving_resource' do
+  action :nothing
+end
diff --git a/examples/notifications/recipes/immediately.rb b/examples/notifications/recipes/immediately.rb
new file mode 100644
index 0000000..c6cfd37
--- /dev/null
+++ b/examples/notifications/recipes/immediately.rb
@@ -0,0 +1,7 @@
+template '/tmp/notifying_resource' do
+  notifies :restart, 'service[receiving_resource]', :immediately
+end
+
+service 'receiving_resource' do
+  action :nothing
+end
diff --git a/examples/notifications/spec/chained_spec.rb b/examples/notifications/spec/chained_spec.rb
new file mode 100644
index 0000000..2ecdb9d
--- /dev/null
+++ b/examples/notifications/spec/chained_spec.rb
@@ -0,0 +1,21 @@
+require 'chefspec'
+
+describe 'notifications::chained' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:template) { chef_run.template('template') }
+  let(:service)  { chef_run.service('service') }
+
+  it 'sends a notification to the first service' do
+    expect(template).to notify('service[service]')
+    expect(template).to_not notify('service[not_service]')
+  end
+
+  it 'sends a notification to the second service through the first' do
+    expect(service).to notify('log[log]')
+  end
+
+  it 'sends the specific notification to the serivce' do
+    expect(template).to notify('service[service]').to(:restart)
+    expect(template).to_not notify('service[service]').to(:stop)
+  end
+end
diff --git a/examples/notifications/spec/default_spec.rb b/examples/notifications/spec/default_spec.rb
new file mode 100644
index 0000000..715c059
--- /dev/null
+++ b/examples/notifications/spec/default_spec.rb
@@ -0,0 +1,16 @@
+require 'chefspec'
+
+describe 'notifications::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:template) { chef_run.template('/tmp/notifying_resource') }
+
+  it 'sends a notification to the service' do
+    expect(template).to notify('service[receiving_resource]')
+    expect(template).to_not notify('service[not_receiving_resource]')
+  end
+
+  it 'sends the specific notification to the serivce' do
+    expect(template).to notify('service[receiving_resource]').to(:restart)
+    expect(template).to_not notify('service[receiving_resource]').to(:stop)
+  end
+end
diff --git a/examples/notifications/spec/delayed_spec.rb b/examples/notifications/spec/delayed_spec.rb
new file mode 100644
index 0000000..ae2d869
--- /dev/null
+++ b/examples/notifications/spec/delayed_spec.rb
@@ -0,0 +1,16 @@
+require 'chefspec'
+
+describe 'notifications::delayed' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:template) { chef_run.template('/tmp/notifying_resource') }
+
+  it 'sends a notification to the service' do
+    expect(template).to notify('service[receiving_resource]').delayed
+    expect(template).to_not notify('service[not_receiving_resource]').delayed
+  end
+
+  it 'sends the specific notification to the serivce delayed' do
+    expect(template).to notify('service[receiving_resource]').to(:restart).delayed
+    expect(template).to_not notify('service[receiving_resource]').to(:restart).immediately
+  end
+end
diff --git a/examples/notifications/spec/immediately_spec.rb b/examples/notifications/spec/immediately_spec.rb
new file mode 100644
index 0000000..35a3d2d
--- /dev/null
+++ b/examples/notifications/spec/immediately_spec.rb
@@ -0,0 +1,16 @@
+require 'chefspec'
+
+describe 'notifications::immediately' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:template) { chef_run.template('/tmp/notifying_resource') }
+
+  it 'sends a notification to the service' do
+    expect(template).to notify('service[receiving_resource]').immediately
+    expect(template).to_not notify('service[not_receiving_resource]').immediately
+  end
+
+  it 'sends the specific notification to the serivce immediately' do
+    expect(template).to notify('service[receiving_resource]').to(:restart).immediately
+    expect(template).to_not notify('service[receiving_resource]').to(:restart).delayed
+  end
+end
diff --git a/examples/ohai/recipes/reload.rb b/examples/ohai/recipes/reload.rb
new file mode 100644
index 0000000..25e2bf6
--- /dev/null
+++ b/examples/ohai/recipes/reload.rb
@@ -0,0 +1,13 @@
+ohai 'default_action'
+
+ohai 'explicit_action' do
+  action :reload
+end
+
+ohai 'with_attributes' do
+  plugin 'plugin'
+end
+
+ohai 'specifying the identity attribute' do
+  name 'identity_attribute'
+end
diff --git a/examples/ohai/spec/reload_spec.rb b/examples/ohai/spec/reload_spec.rb
new file mode 100644
index 0000000..005654c
--- /dev/null
+++ b/examples/ohai/spec/reload_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'ohai::reload' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'reloads a ohai with the default action' do
+    expect(chef_run).to reload_ohai('default_action')
+    expect(chef_run).to_not reload_ohai('not_default_action')
+  end
+
+  it 'reloads a ohai with an explicit action' do
+    expect(chef_run).to reload_ohai('explicit_action')
+  end
+
+  it 'reloads a ohai with attributes' do
+    expect(chef_run).to reload_ohai('with_attributes').with(plugin: 'plugin')
+    expect(chef_run).to_not reload_ohai('with_attributes').with(plugin: 'not_plugin')
+  end
+
+  it 'reloads a ohai when specifying the identity attribute' do
+    expect(chef_run).to reload_ohai('identity_attribute')
+  end
+end
diff --git a/examples/package/recipes/install.rb b/examples/package/recipes/install.rb
new file mode 100644
index 0000000..047629a
--- /dev/null
+++ b/examples/package/recipes/install.rb
@@ -0,0 +1,13 @@
+package 'default_action'
+
+package 'explicit_action' do
+  action :install
+end
+
+package 'with_attributes' do
+  version '1.0.0'
+end
+
+package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/package/recipes/purge.rb b/examples/package/recipes/purge.rb
new file mode 100644
index 0000000..45d38f9
--- /dev/null
+++ b/examples/package/recipes/purge.rb
@@ -0,0 +1,13 @@
+package 'explicit_action' do
+  action :purge
+end
+
+package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/package/recipes/reconfig.rb b/examples/package/recipes/reconfig.rb
new file mode 100644
index 0000000..35045d5
--- /dev/null
+++ b/examples/package/recipes/reconfig.rb
@@ -0,0 +1,13 @@
+package 'explicit_action' do
+  action :reconfig
+end
+
+package 'with_attributes' do
+  version '1.0.0'
+  action  :reconfig
+end
+
+package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :reconfig
+end
diff --git a/examples/package/recipes/remove.rb b/examples/package/recipes/remove.rb
new file mode 100644
index 0000000..882a72d
--- /dev/null
+++ b/examples/package/recipes/remove.rb
@@ -0,0 +1,13 @@
+package 'explicit_action' do
+  action :remove
+end
+
+package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/package/recipes/upgrade.rb b/examples/package/recipes/upgrade.rb
new file mode 100644
index 0000000..b007b45
--- /dev/null
+++ b/examples/package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+package 'explicit_action' do
+  action :upgrade
+end
+
+package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/package/spec/install_spec.rb b/examples/package/spec/install_spec.rb
new file mode 100644
index 0000000..f8377c0
--- /dev/null
+++ b/examples/package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a package with the default action' do
+    expect(chef_run).to install_package('default_action')
+    expect(chef_run).to_not install_package('not_default_action')
+  end
+
+  it 'installs a package with an explicit action' do
+    expect(chef_run).to install_package('explicit_action')
+  end
+
+  it 'installs a package with attributes' do
+    expect(chef_run).to install_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a package when specifying the identity attribute' do
+    expect(chef_run).to install_package('identity_attribute')
+  end
+end
diff --git a/examples/package/spec/purge_spec.rb b/examples/package/spec/purge_spec.rb
new file mode 100644
index 0000000..8835746
--- /dev/null
+++ b/examples/package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a package with an explicit action' do
+    expect(chef_run).to purge_package('explicit_action')
+    expect(chef_run).to_not purge_package('not_explicit_action')
+  end
+
+  it 'purges a package with attributes' do
+    expect(chef_run).to purge_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a package when specifying the identity attribute' do
+    expect(chef_run).to purge_package('identity_attribute')
+  end
+end
diff --git a/examples/package/spec/reconfig_spec.rb b/examples/package/spec/reconfig_spec.rb
new file mode 100644
index 0000000..3c8d146
--- /dev/null
+++ b/examples/package/spec/reconfig_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'package::reconfig' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'reconfigs a package with an explicit action' do
+    expect(chef_run).to reconfig_package('explicit_action')
+    expect(chef_run).to_not reconfig_package('not_explicit_action')
+  end
+
+  it 'reconfigs a package with attributes' do
+    expect(chef_run).to reconfig_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not reconfig_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'reconfigs a package when specifying the identity attribute' do
+    expect(chef_run).to reconfig_package('identity_attribute')
+  end
+end
diff --git a/examples/package/spec/remove_spec.rb b/examples/package/spec/remove_spec.rb
new file mode 100644
index 0000000..877e248
--- /dev/null
+++ b/examples/package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a package with an explicit action' do
+    expect(chef_run).to remove_package('explicit_action')
+    expect(chef_run).to_not remove_package('not_explicit_action')
+  end
+
+  it 'removes a package with attributes' do
+    expect(chef_run).to remove_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a package when specifying the identity attribute' do
+    expect(chef_run).to remove_package('identity_attribute')
+  end
+end
diff --git a/examples/package/spec/upgrade_spec.rb b/examples/package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..8bb0e57
--- /dev/null
+++ b/examples/package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a package with an explicit action' do
+    expect(chef_run).to upgrade_package('explicit_action')
+    expect(chef_run).to_not upgrade_package('not_explicit_action')
+  end
+
+  it 'upgrades a package with attributes' do
+    expect(chef_run).to upgrade_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_package('identity_attribute')
+  end
+end
diff --git a/examples/pacman_package/recipes/install.rb b/examples/pacman_package/recipes/install.rb
new file mode 100644
index 0000000..5b241af
--- /dev/null
+++ b/examples/pacman_package/recipes/install.rb
@@ -0,0 +1,13 @@
+pacman_package 'default_action'
+
+pacman_package 'explicit_action' do
+  action :install
+end
+
+pacman_package 'with_attributes' do
+  version '1.0.0'
+end
+
+pacman_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/pacman_package/recipes/purge.rb b/examples/pacman_package/recipes/purge.rb
new file mode 100644
index 0000000..b794cec
--- /dev/null
+++ b/examples/pacman_package/recipes/purge.rb
@@ -0,0 +1,13 @@
+pacman_package 'explicit_action' do
+  action :purge
+end
+
+pacman_package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+pacman_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/pacman_package/recipes/remove.rb b/examples/pacman_package/recipes/remove.rb
new file mode 100644
index 0000000..63d781f
--- /dev/null
+++ b/examples/pacman_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+pacman_package 'explicit_action' do
+  action :remove
+end
+
+pacman_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+pacman_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/pacman_package/recipes/upgrade.rb b/examples/pacman_package/recipes/upgrade.rb
new file mode 100644
index 0000000..d8a4d91
--- /dev/null
+++ b/examples/pacman_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+pacman_package 'explicit_action' do
+  action :upgrade
+end
+
+pacman_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+pacman_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/pacman_package/spec/install_spec.rb b/examples/pacman_package/spec/install_spec.rb
new file mode 100644
index 0000000..64f7fda
--- /dev/null
+++ b/examples/pacman_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'pacman_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a pacman_package with the default action' do
+    expect(chef_run).to install_pacman_package('default_action')
+    expect(chef_run).to_not install_pacman_package('not_default_action')
+  end
+
+  it 'installs a pacman_package with an explicit action' do
+    expect(chef_run).to install_pacman_package('explicit_action')
+  end
+
+  it 'installs a pacman_package with attributes' do
+    expect(chef_run).to install_pacman_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_pacman_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a pacman_package when specifying the identity attribute' do
+    expect(chef_run).to install_pacman_package('identity_attribute')
+  end
+end
diff --git a/examples/pacman_package/spec/purge_spec.rb b/examples/pacman_package/spec/purge_spec.rb
new file mode 100644
index 0000000..4a708e6
--- /dev/null
+++ b/examples/pacman_package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'pacman_package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a pacman_package with an explicit action' do
+    expect(chef_run).to purge_pacman_package('explicit_action')
+    expect(chef_run).to_not purge_pacman_package('not_explicit_action')
+  end
+
+  it 'purges a pacman_package with attributes' do
+    expect(chef_run).to purge_pacman_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_pacman_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a pacman_package when specifying the identity attribute' do
+    expect(chef_run).to purge_pacman_package('identity_attribute')
+  end
+end
diff --git a/examples/pacman_package/spec/remove_spec.rb b/examples/pacman_package/spec/remove_spec.rb
new file mode 100644
index 0000000..8373539
--- /dev/null
+++ b/examples/pacman_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'pacman_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a pacman_package with an explicit action' do
+    expect(chef_run).to remove_pacman_package('explicit_action')
+    expect(chef_run).to_not remove_pacman_package('not_explicit_action')
+  end
+
+  it 'removes a pacman_package with attributes' do
+    expect(chef_run).to remove_pacman_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_pacman_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a pacman_package when specifying the identity attribute' do
+    expect(chef_run).to remove_pacman_package('identity_attribute')
+  end
+end
diff --git a/examples/pacman_package/spec/upgrade_spec.rb b/examples/pacman_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..1e3876c
--- /dev/null
+++ b/examples/pacman_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'pacman_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a pacman_package with an explicit action' do
+    expect(chef_run).to upgrade_pacman_package('explicit_action')
+    expect(chef_run).to_not upgrade_pacman_package('not_explicit_action')
+  end
+
+  it 'upgrades a pacman_package with attributes' do
+    expect(chef_run).to upgrade_pacman_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_pacman_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a pacman_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_pacman_package('identity_attribute')
+  end
+end
diff --git a/examples/portage_package/recipes/install.rb b/examples/portage_package/recipes/install.rb
new file mode 100644
index 0000000..241011b
--- /dev/null
+++ b/examples/portage_package/recipes/install.rb
@@ -0,0 +1,13 @@
+portage_package 'default_action'
+
+portage_package 'explicit_action' do
+  action :install
+end
+
+portage_package 'with_attributes' do
+  version '1.0.0'
+end
+
+portage_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/portage_package/recipes/purge.rb b/examples/portage_package/recipes/purge.rb
new file mode 100644
index 0000000..66ce35c
--- /dev/null
+++ b/examples/portage_package/recipes/purge.rb
@@ -0,0 +1,13 @@
+portage_package 'explicit_action' do
+  action :purge
+end
+
+portage_package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+portage_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/portage_package/recipes/remove.rb b/examples/portage_package/recipes/remove.rb
new file mode 100644
index 0000000..627de0a
--- /dev/null
+++ b/examples/portage_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+portage_package 'explicit_action' do
+  action :remove
+end
+
+portage_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+portage_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/portage_package/recipes/upgrade.rb b/examples/portage_package/recipes/upgrade.rb
new file mode 100644
index 0000000..0a51534
--- /dev/null
+++ b/examples/portage_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+portage_package 'explicit_action' do
+  action :upgrade
+end
+
+portage_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+portage_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/portage_package/spec/install_spec.rb b/examples/portage_package/spec/install_spec.rb
new file mode 100644
index 0000000..2d3d68f
--- /dev/null
+++ b/examples/portage_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'portage_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a portage_package with the default action' do
+    expect(chef_run).to install_portage_package('default_action')
+    expect(chef_run).to_not install_portage_package('not_default_action')
+  end
+
+  it 'installs a portage_package with an explicit action' do
+    expect(chef_run).to install_portage_package('explicit_action')
+  end
+
+  it 'installs a portage_package with attributes' do
+    expect(chef_run).to install_portage_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_portage_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a portage_package when specifying the identity attribute' do
+    expect(chef_run).to install_portage_package('identity_attribute')
+  end
+end
diff --git a/examples/portage_package/spec/purge_spec.rb b/examples/portage_package/spec/purge_spec.rb
new file mode 100644
index 0000000..32e022b
--- /dev/null
+++ b/examples/portage_package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'portage_package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a portage_package with an explicit action' do
+    expect(chef_run).to purge_portage_package('explicit_action')
+    expect(chef_run).to_not purge_portage_package('not_explicit_action')
+  end
+
+  it 'purges a portage_package with attributes' do
+    expect(chef_run).to purge_portage_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_portage_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a portage_package when specifying the identity attribute' do
+    expect(chef_run).to purge_portage_package('identity_attribute')
+  end
+end
diff --git a/examples/portage_package/spec/remove_spec.rb b/examples/portage_package/spec/remove_spec.rb
new file mode 100644
index 0000000..535851a
--- /dev/null
+++ b/examples/portage_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'portage_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a portage_package with an explicit action' do
+    expect(chef_run).to remove_portage_package('explicit_action')
+    expect(chef_run).to_not remove_portage_package('not_explicit_action')
+  end
+
+  it 'removes a portage_package with attributes' do
+    expect(chef_run).to remove_portage_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_portage_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a portage_package when specifying the identity attribute' do
+    expect(chef_run).to remove_portage_package('identity_attribute')
+  end
+end
diff --git a/examples/portage_package/spec/upgrade_spec.rb b/examples/portage_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..ae5eb52
--- /dev/null
+++ b/examples/portage_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'portage_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a portage_package with an explicit action' do
+    expect(chef_run).to upgrade_portage_package('explicit_action')
+    expect(chef_run).to_not upgrade_portage_package('not_explicit_action')
+  end
+
+  it 'upgrades a portage_package with attributes' do
+    expect(chef_run).to upgrade_portage_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_portage_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a portage_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_portage_package('identity_attribute')
+  end
+end
diff --git a/examples/powershell_script/recipes/run.rb b/examples/powershell_script/recipes/run.rb
new file mode 100644
index 0000000..0e5bb7b
--- /dev/null
+++ b/examples/powershell_script/recipes/run.rb
@@ -0,0 +1,13 @@
+powershell_script 'default_action'
+
+powershell_script 'explicit_action' do
+  action :run
+end
+
+powershell_script 'with_attributes' do
+  flags '--flags'
+end
+
+powershell_script 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/powershell_script/spec/run_spec.rb b/examples/powershell_script/spec/run_spec.rb
new file mode 100644
index 0000000..d6e2b06
--- /dev/null
+++ b/examples/powershell_script/spec/run_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'powershell_script::run' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a powershell_script with the default action' do
+    expect(chef_run).to run_powershell_script('default_action')
+    expect(chef_run).to_not run_powershell_script('not_default_action')
+  end
+
+  it 'runs a powershell_script with an explicit action' do
+    expect(chef_run).to run_powershell_script('explicit_action')
+  end
+
+  it 'runs a powershell_script with attributes' do
+    expect(chef_run).to run_powershell_script('with_attributes').with(flags: '--flags')
+    expect(chef_run).to_not run_powershell_script('with_attributes').with(flags: '--not-flags')
+  end
+
+  it 'runs a powershell_script when specifying the identity attribute' do
+    expect(chef_run).to run_powershell_script('identity_attribute')
+  end
+end
diff --git a/examples/reboot/recipes/cancel.rb b/examples/reboot/recipes/cancel.rb
new file mode 100644
index 0000000..3cb1fea
--- /dev/null
+++ b/examples/reboot/recipes/cancel.rb
@@ -0,0 +1,3 @@
+reboot 'explicit cancel' do
+  action :cancel
+end
diff --git a/examples/reboot/recipes/now.rb b/examples/reboot/recipes/now.rb
new file mode 100644
index 0000000..01bc9cd
--- /dev/null
+++ b/examples/reboot/recipes/now.rb
@@ -0,0 +1,3 @@
+reboot 'explicit_action' do
+  action :reboot_now
+end
diff --git a/examples/reboot/recipes/request.rb b/examples/reboot/recipes/request.rb
new file mode 100644
index 0000000..0dce291
--- /dev/null
+++ b/examples/reboot/recipes/request.rb
@@ -0,0 +1,3 @@
+reboot 'explicit_action' do
+  action :request_reboot
+end
diff --git a/examples/reboot/spec/cancel_spec.rb b/examples/reboot/spec/cancel_spec.rb
new file mode 100644
index 0000000..81a1c0c
--- /dev/null
+++ b/examples/reboot/spec/cancel_spec.rb
@@ -0,0 +1,10 @@
+require 'chefspec'
+
+describe 'reboot::cancel' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a cancel_reboot' do
+    expect(chef_run).to cancel_reboot('explicit cancel')
+  end
+end
+
diff --git a/examples/reboot/spec/now_spec.rb b/examples/reboot/spec/now_spec.rb
new file mode 100644
index 0000000..a2b729a
--- /dev/null
+++ b/examples/reboot/spec/now_spec.rb
@@ -0,0 +1,10 @@
+require 'chefspec'
+
+describe 'reboot::now' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a reboot_now when specifying action' do
+    expect(chef_run).to now_reboot('explicit_action')
+  end
+end
+
diff --git a/examples/reboot/spec/request_spec.rb b/examples/reboot/spec/request_spec.rb
new file mode 100644
index 0000000..d7460f1
--- /dev/null
+++ b/examples/reboot/spec/request_spec.rb
@@ -0,0 +1,10 @@
+require 'chefspec'
+
+describe 'reboot::request' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a request_reboot' do
+    expect(chef_run).to request_reboot('explicit_action')
+  end
+end
+
diff --git a/examples/registry_key/recipes/create.rb b/examples/registry_key/recipes/create.rb
new file mode 100644
index 0000000..32e488d
--- /dev/null
+++ b/examples/registry_key/recipes/create.rb
@@ -0,0 +1,13 @@
+registry_key 'HKEY_LOCAL_MACHINE\default_action'
+
+registry_key 'HKEY_LOCAL_MACHINE\explicit_action' do
+  action :create
+end
+
+registry_key 'HKEY_LOCAL_MACHINE\with_attributes' do
+  recursive true
+end
+
+registry_key 'specifying the identity attribute' do
+  key 'HKEY_LOCAL_MACHINE\identity_attribute'
+end
diff --git a/examples/registry_key/recipes/create_if_missing.rb b/examples/registry_key/recipes/create_if_missing.rb
new file mode 100644
index 0000000..4e97ac4
--- /dev/null
+++ b/examples/registry_key/recipes/create_if_missing.rb
@@ -0,0 +1,13 @@
+registry_key 'HKEY_LOCAL_MACHINE\explicit_action' do
+  action :create_if_missing
+end
+
+registry_key 'HKEY_LOCAL_MACHINE\with_attributes' do
+  recursive true
+  action    :create_if_missing
+end
+
+registry_key 'specifying the identity attribute' do
+  key    'HKEY_LOCAL_MACHINE\identity_attribute'
+  action :create_if_missing
+end
diff --git a/examples/registry_key/recipes/delete.rb b/examples/registry_key/recipes/delete.rb
new file mode 100644
index 0000000..67dafc8
--- /dev/null
+++ b/examples/registry_key/recipes/delete.rb
@@ -0,0 +1,13 @@
+registry_key 'HKEY_LOCAL_MACHINE\explicit_action' do
+  action :delete
+end
+
+registry_key 'HKEY_LOCAL_MACHINE\with_attributes' do
+  recursive true
+  action    :delete
+end
+
+registry_key 'specifying the identity attribute' do
+  key    'HKEY_LOCAL_MACHINE\identity_attribute'
+  action :delete
+end
diff --git a/examples/registry_key/recipes/delete_key.rb b/examples/registry_key/recipes/delete_key.rb
new file mode 100644
index 0000000..e3faf83
--- /dev/null
+++ b/examples/registry_key/recipes/delete_key.rb
@@ -0,0 +1,13 @@
+registry_key 'HKEY_LOCAL_MACHINE\explicit_action' do
+  action :delete_key
+end
+
+registry_key 'HKEY_LOCAL_MACHINE\with_attributes' do
+  recursive true
+  action    :delete_key
+end
+
+registry_key 'specifying the identity attribute' do
+  key    'HKEY_LOCAL_MACHINE\identity_attribute'
+  action :delete_key
+end
diff --git a/examples/registry_key/spec/create_if_missing_spec.rb b/examples/registry_key/spec/create_if_missing_spec.rb
new file mode 100644
index 0000000..baf578e
--- /dev/null
+++ b/examples/registry_key/spec/create_if_missing_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'registry_key::create_if_missing' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a registry_key with an explicit action' do
+    expect(chef_run).to create_registry_key_if_missing('HKEY_LOCAL_MACHINE\explicit_action')
+    expect(chef_run).to_not create_registry_key_if_missing('HKEY_LOCAL_MACHINE\not_explicit_action')
+  end
+
+  it 'creates a registry_key with attributes' do
+    expect(chef_run).to create_registry_key_if_missing('HKEY_LOCAL_MACHINE\with_attributes').with(recursive: true)
+    expect(chef_run).to_not create_registry_key_if_missing('HKEY_LOCAL_MACHINE\with_attributes').with(recursive: false)
+  end
+
+  it 'creates a registry_key when specifying the identity attribute' do
+    expect(chef_run).to create_registry_key_if_missing('HKEY_LOCAL_MACHINE\identity_attribute')
+  end
+end
diff --git a/examples/registry_key/spec/create_spec.rb b/examples/registry_key/spec/create_spec.rb
new file mode 100644
index 0000000..8458f97
--- /dev/null
+++ b/examples/registry_key/spec/create_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'registry_key::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a registry_key with the default action' do
+    expect(chef_run).to create_registry_key('HKEY_LOCAL_MACHINE\default_action')
+    expect(chef_run).to_not create_registry_key('HKEY_LOCAL_MACHINE\not_default_action')
+  end
+
+  it 'creates a registry_key with an explicit action' do
+    expect(chef_run).to create_registry_key('HKEY_LOCAL_MACHINE\explicit_action')
+  end
+
+  it 'creates a registry_key with attributes' do
+    expect(chef_run).to create_registry_key('HKEY_LOCAL_MACHINE\with_attributes').with(recursive: true)
+    expect(chef_run).to_not create_registry_key('HKEY_LOCAL_MACHINE\with_attributes').with(recursive: false)
+  end
+
+  it 'creates a registry_key when specifying the identity attribute' do
+    expect(chef_run).to create_registry_key('HKEY_LOCAL_MACHINE\identity_attribute')
+  end
+end
diff --git a/examples/registry_key/spec/delete_key_spec.rb b/examples/registry_key/spec/delete_key_spec.rb
new file mode 100644
index 0000000..207213a
--- /dev/null
+++ b/examples/registry_key/spec/delete_key_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'registry_key::delete_key' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'delete_keyes a registry_key with an explicit action' do
+    expect(chef_run).to delete_key_registry_key('HKEY_LOCAL_MACHINE\explicit_action')
+    expect(chef_run).to_not delete_key_registry_key('HKEY_LOCAL_MACHINE\not_explicit_action')
+  end
+
+  it 'delete_keyes a registry_key with attributes' do
+    expect(chef_run).to delete_key_registry_key('HKEY_LOCAL_MACHINE\with_attributes').with(recursive: true)
+    expect(chef_run).to_not delete_key_registry_key('HKEY_LOCAL_MACHINE\with_attributes').with(recursive: false)
+  end
+
+  it 'delete_keyes a registry_key when specifying the identity attribute' do
+    expect(chef_run).to delete_key_registry_key('HKEY_LOCAL_MACHINE\identity_attribute')
+  end
+end
diff --git a/examples/registry_key/spec/delete_spec.rb b/examples/registry_key/spec/delete_spec.rb
new file mode 100644
index 0000000..9456a43
--- /dev/null
+++ b/examples/registry_key/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'registry_key::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a registry_key with an explicit action' do
+    expect(chef_run).to delete_registry_key('HKEY_LOCAL_MACHINE\explicit_action')
+    expect(chef_run).to_not delete_registry_key('HKEY_LOCAL_MACHINE\not_explicit_action')
+  end
+
+  it 'deletes a registry_key with attributes' do
+    expect(chef_run).to delete_registry_key('HKEY_LOCAL_MACHINE\with_attributes').with(recursive: true)
+    expect(chef_run).to_not delete_registry_key('HKEY_LOCAL_MACHINE\with_attributes').with(recursive: false)
+  end
+
+  it 'deletes a registry_key when specifying the identity attribute' do
+    expect(chef_run).to delete_registry_key('HKEY_LOCAL_MACHINE\identity_attribute')
+  end
+end
diff --git a/examples/remote_directory/recipes/create.rb b/examples/remote_directory/recipes/create.rb
new file mode 100644
index 0000000..a71c752
--- /dev/null
+++ b/examples/remote_directory/recipes/create.rb
@@ -0,0 +1,13 @@
+remote_directory '/tmp/default_action'
+
+remote_directory '/tmp/explicit_action' do
+  action :create
+end
+
+remote_directory '/tmp/with_attributes' do
+  owner 'owner'
+end
+
+remote_directory 'specifying the identity attribute' do
+  path '/tmp/identity_attribute'
+end
diff --git a/examples/remote_directory/recipes/create_if_missing.rb b/examples/remote_directory/recipes/create_if_missing.rb
new file mode 100644
index 0000000..16440bb
--- /dev/null
+++ b/examples/remote_directory/recipes/create_if_missing.rb
@@ -0,0 +1,13 @@
+remote_directory '/tmp/explicit_action' do
+  action :create_if_missing
+end
+
+remote_directory '/tmp/with_attributes' do
+  owner 'owner'
+  action :create_if_missing
+end
+
+remote_directory 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :create_if_missing
+end
diff --git a/examples/remote_directory/recipes/delete.rb b/examples/remote_directory/recipes/delete.rb
new file mode 100644
index 0000000..d7e7108
--- /dev/null
+++ b/examples/remote_directory/recipes/delete.rb
@@ -0,0 +1,13 @@
+remote_directory '/tmp/explicit_action' do
+  action :delete
+end
+
+remote_directory '/tmp/with_attributes' do
+  owner 'owner'
+  action :delete
+end
+
+remote_directory 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :delete
+end
diff --git a/examples/remote_directory/spec/create_if_missing_spec.rb b/examples/remote_directory/spec/create_if_missing_spec.rb
new file mode 100644
index 0000000..7467063
--- /dev/null
+++ b/examples/remote_directory/spec/create_if_missing_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'remote_directory::create_if_missing' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a remote_directory with an explicit action' do
+    expect(chef_run).to create_remote_directory_if_missing('/tmp/explicit_action')
+    expect(chef_run).to_not create_remote_directory_if_missing('/tmp/not_explicit_action')
+  end
+
+  it 'creates a remote_directory with attributes' do
+    expect(chef_run).to create_remote_directory_if_missing('/tmp/with_attributes').with(owner: 'owner')
+    expect(chef_run).to_not create_remote_directory_if_missing('/tmp/with_attributes').with(owner: 'bacon')
+  end
+
+  it 'creates a remote_directory when specifying the identity attribute' do
+    expect(chef_run).to create_remote_directory_if_missing('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/remote_directory/spec/create_spec.rb b/examples/remote_directory/spec/create_spec.rb
new file mode 100644
index 0000000..2808044
--- /dev/null
+++ b/examples/remote_directory/spec/create_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'remote_directory::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a remote_directory with the default action' do
+    expect(chef_run).to create_remote_directory('/tmp/default_action')
+    expect(chef_run).to_not create_remote_directory('/tmp/not_default_action')
+  end
+
+  it 'creates a remote_directory with an explicit action' do
+    expect(chef_run).to create_remote_directory('/tmp/explicit_action')
+  end
+
+  it 'creates a remote_directory with attributes' do
+    expect(chef_run).to create_remote_directory('/tmp/with_attributes').with(owner: 'owner')
+    expect(chef_run).to_not create_remote_directory('/tmp/with_attributes').with(owner: 'bacon')
+  end
+
+  it 'creates a remote_directory when specifying the identity attribute' do
+    expect(chef_run).to create_remote_directory('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/remote_directory/spec/delete_spec.rb b/examples/remote_directory/spec/delete_spec.rb
new file mode 100644
index 0000000..ff3d56c
--- /dev/null
+++ b/examples/remote_directory/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'remote_directory::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a remote_directory with an explicit action' do
+    expect(chef_run).to delete_remote_directory('/tmp/explicit_action')
+    expect(chef_run).to_not delete_remote_directory('/tmp/not_explicit_action')
+  end
+
+  it 'deletes a remote_directory with attributes' do
+    expect(chef_run).to delete_remote_directory('/tmp/with_attributes').with(owner: 'owner')
+    expect(chef_run).to_not delete_remote_directory('/tmp/with_attributes').with(owner: 'bacon')
+  end
+
+  it 'deletes a remote_directory when specifying the identity attribute' do
+    expect(chef_run).to delete_remote_directory('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/remote_file/recipes/create.rb b/examples/remote_file/recipes/create.rb
new file mode 100644
index 0000000..febaf87
--- /dev/null
+++ b/examples/remote_file/recipes/create.rb
@@ -0,0 +1,18 @@
+remote_file '/tmp/default_action' do
+  source 'http://not-real'
+end
+
+remote_file '/tmp/explicit_action' do
+  source 'http://not-real'
+  action :create
+end
+
+remote_file '/tmp/with_attributes' do
+  source 'http://not-real'
+  owner 'owner'
+end
+
+remote_file 'specifying the identity attribute' do
+  source 'http://not-real'
+  path '/tmp/identity_attribute'
+end
diff --git a/examples/remote_file/recipes/create_if_missing.rb b/examples/remote_file/recipes/create_if_missing.rb
new file mode 100644
index 0000000..b81b057
--- /dev/null
+++ b/examples/remote_file/recipes/create_if_missing.rb
@@ -0,0 +1,16 @@
+remote_file '/tmp/explicit_action' do
+  source 'http://not-real'
+  action :create_if_missing
+end
+
+remote_file '/tmp/with_attributes' do
+  source 'http://not-real'
+  owner  'owner'
+  action :create_if_missing
+end
+
+remote_file 'specifying the identity attribute' do
+  source 'http://not-real'
+  path   '/tmp/identity_attribute'
+  action :create_if_missing
+end
diff --git a/examples/remote_file/recipes/delete.rb b/examples/remote_file/recipes/delete.rb
new file mode 100644
index 0000000..8d56a16
--- /dev/null
+++ b/examples/remote_file/recipes/delete.rb
@@ -0,0 +1,16 @@
+remote_file '/tmp/explicit_action' do
+  source 'http://not-real'
+  action :delete
+end
+
+remote_file '/tmp/with_attributes' do
+  source 'http://not-real'
+  owner  'owner'
+  action :delete
+end
+
+remote_file 'specifying the identity attribute' do
+  source 'http://not-real'
+  path   '/tmp/identity_attribute'
+  action :delete
+end
diff --git a/examples/remote_file/recipes/touch.rb b/examples/remote_file/recipes/touch.rb
new file mode 100644
index 0000000..ccf6a8c
--- /dev/null
+++ b/examples/remote_file/recipes/touch.rb
@@ -0,0 +1,16 @@
+remote_file '/tmp/explicit_action' do
+  source 'http://not-real'
+  action :touch
+end
+
+remote_file '/tmp/with_attributes' do
+  source 'http://not-real'
+  owner  'owner'
+  action :touch
+end
+
+remote_file 'specifying the identity attribute' do
+  source 'http://not-real'
+  path   '/tmp/identity_attribute'
+  action :touch
+end
diff --git a/examples/remote_file/spec/create_if_missing_spec.rb b/examples/remote_file/spec/create_if_missing_spec.rb
new file mode 100644
index 0000000..8bd420a
--- /dev/null
+++ b/examples/remote_file/spec/create_if_missing_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'remote_file::create_if_missing' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a remote_file with an explicit action' do
+    expect(chef_run).to create_remote_file_if_missing('/tmp/explicit_action')
+    expect(chef_run).to_not create_remote_file_if_missing('/tmp/not_explicit_action')
+  end
+
+  it 'creates a remote_file with attributes' do
+    expect(chef_run).to create_remote_file_if_missing('/tmp/with_attributes').with(owner: 'owner')
+    expect(chef_run).to_not create_remote_file_if_missing('/tmp/with_attributes').with(owner: 'bacon')
+  end
+
+  it 'creates a remote_file when specifying the identity attribute' do
+    expect(chef_run).to create_remote_file_if_missing('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/remote_file/spec/create_spec.rb b/examples/remote_file/spec/create_spec.rb
new file mode 100644
index 0000000..57ddf4f
--- /dev/null
+++ b/examples/remote_file/spec/create_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'remote_file::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a remote_file with the default action' do
+    expect(chef_run).to create_remote_file('/tmp/default_action')
+    expect(chef_run).to_not create_remote_file('/tmp/not_default_action')
+  end
+
+  it 'creates a remote_file with an explicit action' do
+    expect(chef_run).to create_remote_file('/tmp/explicit_action')
+  end
+
+  it 'creates a remote_file with attributes' do
+    expect(chef_run).to create_remote_file('/tmp/with_attributes').with(owner: 'owner')
+    expect(chef_run).to_not create_remote_file('/tmp/with_attributes').with(owner: 'bacon')
+  end
+
+  it 'creates a remote_file when specifying the identity attribute' do
+    expect(chef_run).to create_remote_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/remote_file/spec/delete_spec.rb b/examples/remote_file/spec/delete_spec.rb
new file mode 100644
index 0000000..000390d
--- /dev/null
+++ b/examples/remote_file/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'remote_file::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a remote_file with an explicit action' do
+    expect(chef_run).to delete_remote_file('/tmp/explicit_action')
+    expect(chef_run).to_not delete_remote_file('/tmp/not_explicit_action')
+  end
+
+  it 'deletes a remote_file with attributes' do
+    expect(chef_run).to delete_remote_file('/tmp/with_attributes').with(owner: 'owner')
+    expect(chef_run).to_not delete_remote_file('/tmp/with_attributes').with(owner: 'bacon')
+  end
+
+  it 'deletes a remote_file when specifying the identity attribute' do
+    expect(chef_run).to delete_remote_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/remote_file/spec/touch_spec.rb b/examples/remote_file/spec/touch_spec.rb
new file mode 100644
index 0000000..13d1e74
--- /dev/null
+++ b/examples/remote_file/spec/touch_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'remote_file::touch' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'touchs a remote_file with an explicit action' do
+    expect(chef_run).to touch_remote_file('/tmp/explicit_action')
+    expect(chef_run).to_not touch_remote_file('/tmp/not_explicit_action')
+  end
+
+  it 'touchs a remote_file with attributes' do
+    expect(chef_run).to touch_remote_file('/tmp/with_attributes').with(owner: 'owner')
+    expect(chef_run).to_not touch_remote_file('/tmp/with_attributes').with(owner: 'bacon')
+  end
+
+  it 'touchs a remote_file when specifying the identity attribute' do
+    expect(chef_run).to touch_remote_file('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/render_file/files/default/cookbook_file b/examples/render_file/files/default/cookbook_file
new file mode 100644
index 0000000..9997538
--- /dev/null
+++ b/examples/render_file/files/default/cookbook_file
@@ -0,0 +1 @@
+This is content!
diff --git a/examples/render_file/recipes/default.rb b/examples/render_file/recipes/default.rb
new file mode 100644
index 0000000..c761390
--- /dev/null
+++ b/examples/render_file/recipes/default.rb
@@ -0,0 +1,15 @@
+file '/tmp/file' do
+  content 'This is content!'
+end
+
+cookbook_file '/tmp/cookbook_file' do
+  source 'cookbook_file'
+end
+
+template '/tmp/template' do
+  source 'template.erb'
+end
+
+template '/tmp/partial' do
+  source 'partial.erb'
+end
diff --git a/examples/render_file/recipes/template_helpers.rb b/examples/render_file/recipes/template_helpers.rb
new file mode 100644
index 0000000..235ef93
--- /dev/null
+++ b/examples/render_file/recipes/template_helpers.rb
@@ -0,0 +1,9 @@
+module TestHelper
+  def helper_method
+    'hello'
+  end
+end
+
+template '/tmp/template_with_helper' do
+  helpers TestHelper
+end
diff --git a/examples/render_file/spec/default_spec.rb b/examples/render_file/spec/default_spec.rb
new file mode 100644
index 0000000..b39ae5c
--- /dev/null
+++ b/examples/render_file/spec/default_spec.rb
@@ -0,0 +1,155 @@
+require 'chefspec'
+
+describe 'render_file::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  context 'file' do
+    it 'renders the file' do
+      expect(chef_run).to render_file('/tmp/file')
+      expect(chef_run).to_not render_file('/tmp/not_file')
+    end
+
+    it 'renders the file with content' do
+      expect(chef_run).to render_file('/tmp/file').with_content('This is content!')
+      expect(chef_run).to_not render_file('/tmp/file').with_content('This is not content!')
+    end
+
+    it 'renders the file with matching content' do
+      expect(chef_run).to render_file('/tmp/file').with_content(/^This(.+)$/)
+      expect(chef_run).to_not render_file('/tmp/file').with_content(/^Not(.+)$/)
+    end
+
+    it 'renders the file when given a block' do
+      expect(chef_run).to render_file('/tmp/file').with_content { |content|
+        expect(content).to include('This is content!')
+      }
+
+      expect(chef_run).to render_file('/tmp/file').with_content { |content|
+        expect(content).to_not include('This is not content!')
+      }
+    end
+
+    it 'renders the file with content matching arbitrary matcher' do
+      expect(chef_run).to render_file('/tmp/file').with_content(
+        start_with('This')
+      )
+      expect(chef_run).to_not render_file('/tmp/file').with_content(
+        end_with('not')
+      )
+    end
+  end
+
+  context 'cookbook_file' do
+    shared_examples 'renders file' do
+      it 'renders the file' do
+        expect(chef_run).to render_file('/tmp/cookbook_file')
+        expect(chef_run).to_not render_file('/tmp/not_cookbook_file')
+      end
+
+      it 'renders the file with content' do
+        expect(chef_run).to render_file('/tmp/cookbook_file').with_content('This is content!')
+        expect(chef_run).to_not render_file('/tmp/cookbook_file').with_content('This is not content!')
+      end
+
+      it 'renders the file with matching content' do
+        expect(chef_run).to render_file('/tmp/cookbook_file').with_content(/^This(.+)$/)
+        expect(chef_run).to_not render_file('/tmp/cookbook_file').with_content(/^Not(.+)$/)
+      end
+
+      it 'renders the file when given a block' do
+        expect(chef_run).to render_file('/tmp/cookbook_file').with_content { |content|
+          expect(content).to include('This is content!')
+        }
+
+        expect(chef_run).to render_file('/tmp/cookbook_file').with_content { |content|
+          expect(content).to_not include('This is not content!')
+        }
+      end
+
+      it 'renders the file with content matching arbitrary matcher' do
+        expect(chef_run).to render_file('/tmp/cookbook_file').with_content(
+          start_with('This')
+        )
+        expect(chef_run).to_not render_file('/tmp/cookbook_file').with_content(
+          end_with('not')
+        )
+      end
+    end
+
+    context 'with a pristine filesystem' do
+      it_behaves_like 'renders file'
+    end
+
+    context 'with a same rendered file on filesystem' do
+      before do
+        allow(File).to receive(:read).and_call_original
+        allow(File).to receive(:read).with('/tmp/cookbook_file', 'rb').and_yield('This is content!')
+      end
+
+      it_behaves_like 'renders file'
+    end
+  end
+
+  context 'template' do
+    it 'renders the file' do
+      expect(chef_run).to render_file('/tmp/template')
+      expect(chef_run).to_not render_file('/tmp/not_template')
+    end
+
+    it 'renders the file with content' do
+      expect(chef_run).to render_file('/tmp/template').with_content('This is content!')
+      expect(chef_run).to_not render_file('/tmp/template').with_content('This is not content!')
+    end
+
+    it 'renders the file with matching content' do
+      expect(chef_run).to render_file('/tmp/template').with_content(/^This(.+)$/)
+      expect(chef_run).to_not render_file('/tmp/template').with_content(/^Not(.+)$/)
+    end
+
+    it 'renders the file when given a block' do
+      expect(chef_run).to render_file('/tmp/template').with_content { |content|
+        expect(content).to include('This is content!')
+      }
+
+      expect(chef_run).to render_file('/tmp/template').with_content { |content|
+        expect(content).to_not include('This is not content!')
+      }
+    end
+
+    it 'renders the file with content matching arbitrary matcher' do
+      expect(chef_run).to render_file('/tmp/template').with_content(
+        start_with('This')
+      )
+      expect(chef_run).to_not render_file('/tmp/template').with_content(
+        end_with('not')
+      )
+    end
+  end
+
+  context 'template with render' do
+    it 'renders the file' do
+      expect(chef_run).to render_file('/tmp/partial')
+      expect(chef_run).to_not render_file('/tmp/not_partial')
+    end
+
+    it 'renders the file with content' do
+      expect(chef_run).to render_file('/tmp/partial').with_content('This template has a partial: This is a template partial!')
+      expect(chef_run).to_not render_file('/tmp/partial').with_content('This template has a partial: This is not a template partial!')
+    end
+
+    it 'renders the file when given a block' do
+      expect(chef_run).to render_file('/tmp/partial').with_content { |content|
+        expect(content).to include('has a partial')
+      }
+
+      expect(chef_run).to render_file('/tmp/partial').with_content { |content|
+        expect(content).to_not include('not a template partial')
+      }
+    end
+
+    it 'renders the file with matching content' do
+      expect(chef_run).to render_file('/tmp/partial').with_content(/^This(.+)$/)
+      expect(chef_run).to_not render_file('/tmp/partial').with_content(/^Not(.+)$/)
+    end
+  end
+end
diff --git a/examples/render_file/spec/template_helpers_spec.rb b/examples/render_file/spec/template_helpers_spec.rb
new file mode 100644
index 0000000..ea81a82
--- /dev/null
+++ b/examples/render_file/spec/template_helpers_spec.rb
@@ -0,0 +1,10 @@
+require 'chefspec'
+
+describe 'render_file::template_helpers' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'renders the file using a helper' do
+    expect(chef_run).to render_file('/tmp/template_with_helper')
+      .with_content(/^helper result: hello$/)
+  end
+end
diff --git a/examples/render_file/templates/default/_partial.erb b/examples/render_file/templates/default/_partial.erb
new file mode 100644
index 0000000..65318bd
--- /dev/null
+++ b/examples/render_file/templates/default/_partial.erb
@@ -0,0 +1 @@
+This is a template <%= 'partial' %>!
diff --git a/examples/render_file/templates/default/partial.erb b/examples/render_file/templates/default/partial.erb
new file mode 100644
index 0000000..e06762d
--- /dev/null
+++ b/examples/render_file/templates/default/partial.erb
@@ -0,0 +1 @@
+This template has a partial: <%= render '_partial.erb' %>
diff --git a/examples/render_file/templates/default/template.erb b/examples/render_file/templates/default/template.erb
new file mode 100644
index 0000000..9997538
--- /dev/null
+++ b/examples/render_file/templates/default/template.erb
@@ -0,0 +1 @@
+This is content!
diff --git a/examples/render_file/templates/default/template_with_helper.erb b/examples/render_file/templates/default/template_with_helper.erb
new file mode 100644
index 0000000..928701a
--- /dev/null
+++ b/examples/render_file/templates/default/template_with_helper.erb
@@ -0,0 +1 @@
+helper result: <%= helper_method %>
diff --git a/examples/roles/recipes/another.rb b/examples/roles/recipes/another.rb
new file mode 100644
index 0000000..b756456
--- /dev/null
+++ b/examples/roles/recipes/another.rb
@@ -0,0 +1,3 @@
+service 'resource' do
+  action :start
+end
diff --git a/examples/roles/recipes/default.rb b/examples/roles/recipes/default.rb
new file mode 100644
index 0000000..f50a16d
--- /dev/null
+++ b/examples/roles/recipes/default.rb
@@ -0,0 +1 @@
+package 'resource'
diff --git a/examples/roles/roles/role.rb b/examples/roles/roles/role.rb
new file mode 100644
index 0000000..9cafdd3
--- /dev/null
+++ b/examples/roles/roles/role.rb
@@ -0,0 +1,9 @@
+default_attributes(
+  'roles' => {
+    'attribute' => 'new_value'
+  }
+)
+run_list([
+  'recipe[roles::default]',
+  'recipe[roles::another]'
+])
diff --git a/examples/roles/spec/default_spec.rb b/examples/roles/spec/default_spec.rb
new file mode 100644
index 0000000..1e58559
--- /dev/null
+++ b/examples/roles/spec/default_spec.rb
@@ -0,0 +1,21 @@
+require 'chefspec'
+
+describe 'roles' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge('role[role]') }
+
+  it 'expands the run_list' do
+    expect(chef_run).to include_recipe('roles::default')
+    expect(chef_run).to include_recipe('roles::another')
+    expect(chef_run).to_not include_recipe('roles::not_recipe')
+  end
+
+  it 'installs the package' do
+    expect(chef_run).to install_package('resource')
+    expect(chef_run).to_not install_package('not_resource')
+  end
+
+  it 'installs the service' do
+    expect(chef_run).to start_service('resource')
+    expect(chef_run).to_not start_service('not_resource')
+  end
+end
diff --git a/examples/route/recipes/add.rb b/examples/route/recipes/add.rb
new file mode 100644
index 0000000..ef28ed9
--- /dev/null
+++ b/examples/route/recipes/add.rb
@@ -0,0 +1,13 @@
+route '10.0.0.1'
+
+route '10.0.0.2' do
+  action :add
+end
+
+route '10.0.0.3' do
+  gateway '10.0.0.0'
+end
+
+route 'specifying the identity attribute' do
+  target '10.0.0.4'
+end
diff --git a/examples/route/recipes/delete.rb b/examples/route/recipes/delete.rb
new file mode 100644
index 0000000..8861451
--- /dev/null
+++ b/examples/route/recipes/delete.rb
@@ -0,0 +1,13 @@
+route '10.0.0.2' do
+  action :delete
+end
+
+route '10.0.0.3' do
+  gateway '10.0.0.0'
+  action  :delete
+end
+
+route 'specifying the identity attribute' do
+  target '10.0.0.4'
+  action :delete
+end
diff --git a/examples/route/spec/add_spec.rb b/examples/route/spec/add_spec.rb
new file mode 100644
index 0000000..bcad911
--- /dev/null
+++ b/examples/route/spec/add_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'route::add' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'adds a route with the default action' do
+    expect(chef_run).to add_route('10.0.0.1')
+    expect(chef_run).to_not add_route('10.0.0.10')
+  end
+
+  it 'adds a route with an explicit action' do
+    expect(chef_run).to add_route('10.0.0.2')
+  end
+
+  it 'adds a route with attributes' do
+    expect(chef_run).to add_route('10.0.0.3').with(gateway: '10.0.0.0')
+    expect(chef_run).to_not add_route('10.0.0.3').with(gateway: '10.0.0.100')
+  end
+
+  it 'adds a route when specifying the identity attribute' do
+    expect(chef_run).to add_route('10.0.0.4')
+  end
+end
diff --git a/examples/route/spec/delete_spec.rb b/examples/route/spec/delete_spec.rb
new file mode 100644
index 0000000..353d193
--- /dev/null
+++ b/examples/route/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'route::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a route with an explicit action' do
+    expect(chef_run).to delete_route('10.0.0.2')
+    expect(chef_run).to_not delete_route('10.0.0.10')
+  end
+
+  it 'deletes a route with attributes' do
+    expect(chef_run).to delete_route('10.0.0.3').with(gateway: '10.0.0.0')
+    expect(chef_run).to_not delete_route('10.0.0.3').with(gateway: '10.0.0.100')
+  end
+
+  it 'deletes a route when specifying the identity attribute' do
+    expect(chef_run).to delete_route('10.0.0.4')
+  end
+end
diff --git a/examples/rpm_package/recipes/install.rb b/examples/rpm_package/recipes/install.rb
new file mode 100644
index 0000000..698dc6e
--- /dev/null
+++ b/examples/rpm_package/recipes/install.rb
@@ -0,0 +1,13 @@
+rpm_package 'default_action'
+
+rpm_package 'explicit_action' do
+  action :install
+end
+
+rpm_package 'with_attributes' do
+  version '1.0.0'
+end
+
+rpm_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/rpm_package/recipes/remove.rb b/examples/rpm_package/recipes/remove.rb
new file mode 100644
index 0000000..f2e6205
--- /dev/null
+++ b/examples/rpm_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+rpm_package 'explicit_action' do
+  action :remove
+end
+
+rpm_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+rpm_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/rpm_package/recipes/upgrade.rb b/examples/rpm_package/recipes/upgrade.rb
new file mode 100644
index 0000000..b06a411
--- /dev/null
+++ b/examples/rpm_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+rpm_package 'explicit_action' do
+  action :upgrade
+end
+
+rpm_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+rpm_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/rpm_package/spec/install_spec.rb b/examples/rpm_package/spec/install_spec.rb
new file mode 100644
index 0000000..887a6b3
--- /dev/null
+++ b/examples/rpm_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'rpm_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a rpm_package with the default action' do
+    expect(chef_run).to install_rpm_package('default_action')
+    expect(chef_run).to_not install_rpm_package('not_default_action')
+  end
+
+  it 'installs a rpm_package with an explicit action' do
+    expect(chef_run).to install_rpm_package('explicit_action')
+  end
+
+  it 'installs a rpm_package with attributes' do
+    expect(chef_run).to install_rpm_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_rpm_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a rpm_package when specifying the identity attribute' do
+    expect(chef_run).to install_rpm_package('identity_attribute')
+  end
+end
diff --git a/examples/rpm_package/spec/remove_spec.rb b/examples/rpm_package/spec/remove_spec.rb
new file mode 100644
index 0000000..f592f0b
--- /dev/null
+++ b/examples/rpm_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'rpm_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a rpm_package with an explicit action' do
+    expect(chef_run).to remove_rpm_package('explicit_action')
+    expect(chef_run).to_not remove_rpm_package('not_explicit_action')
+  end
+
+  it 'removes a rpm_package with attributes' do
+    expect(chef_run).to remove_rpm_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_rpm_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a rpm_package when specifying the identity attribute' do
+    expect(chef_run).to remove_rpm_package('identity_attribute')
+  end
+end
diff --git a/examples/rpm_package/spec/upgrade_spec.rb b/examples/rpm_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..a4e7db3
--- /dev/null
+++ b/examples/rpm_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'rpm_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a rpm_package with an explicit action' do
+    expect(chef_run).to upgrade_rpm_package('explicit_action')
+    expect(chef_run).to_not upgrade_rpm_package('not_explicit_action')
+  end
+
+  it 'upgrades a rpm_package with attributes' do
+    expect(chef_run).to upgrade_rpm_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_rpm_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a rpm_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_rpm_package('identity_attribute')
+  end
+end
diff --git a/examples/ruby_block/recipes/run.rb b/examples/ruby_block/recipes/run.rb
new file mode 100644
index 0000000..3277f26
--- /dev/null
+++ b/examples/ruby_block/recipes/run.rb
@@ -0,0 +1,9 @@
+ruby_block 'default_action'
+
+ruby_block 'explicit_action' do
+  action :run
+end
+
+ruby_block 'specifying the identity attribute' do
+  block_name 'identity_attribute'
+end
diff --git a/examples/ruby_block/spec/run_spec.rb b/examples/ruby_block/spec/run_spec.rb
new file mode 100644
index 0000000..ffc0745
--- /dev/null
+++ b/examples/ruby_block/spec/run_spec.rb
@@ -0,0 +1,18 @@
+require 'chefspec'
+
+describe 'ruby_block::run' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a ruby_block with the default action' do
+    expect(chef_run).to run_ruby_block('default_action')
+    expect(chef_run).to_not run_ruby_block('not_default_action')
+  end
+
+  it 'runs a ruby_block with an explicit action' do
+    expect(chef_run).to run_ruby_block('explicit_action')
+  end
+
+  it 'runs a ruby_block when specifying the identity attribute' do
+    expect(chef_run).to run_ruby_block('identity_attribute')
+  end
+end
diff --git a/examples/script/recipes/run_bash.rb b/examples/script/recipes/run_bash.rb
new file mode 100644
index 0000000..4e2eb86
--- /dev/null
+++ b/examples/script/recipes/run_bash.rb
@@ -0,0 +1,13 @@
+bash 'default_action'
+
+bash 'explicit_action' do
+  action :run
+end
+
+bash 'with_attributes' do
+  creates 'creates'
+end
+
+bash 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/script/recipes/run_csh.rb b/examples/script/recipes/run_csh.rb
new file mode 100644
index 0000000..f0dfd4c
--- /dev/null
+++ b/examples/script/recipes/run_csh.rb
@@ -0,0 +1,13 @@
+csh 'default_action'
+
+csh 'explicit_action' do
+  action :run
+end
+
+csh 'with_attributes' do
+  creates 'creates'
+end
+
+csh 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/script/recipes/run_perl.rb b/examples/script/recipes/run_perl.rb
new file mode 100644
index 0000000..c6c3f46
--- /dev/null
+++ b/examples/script/recipes/run_perl.rb
@@ -0,0 +1,13 @@
+perl 'default_action'
+
+perl 'explicit_action' do
+  action :run
+end
+
+perl 'with_attributes' do
+  creates 'creates'
+end
+
+perl 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/script/recipes/run_python.rb b/examples/script/recipes/run_python.rb
new file mode 100644
index 0000000..a24a2fd
--- /dev/null
+++ b/examples/script/recipes/run_python.rb
@@ -0,0 +1,13 @@
+python 'default_action'
+
+python 'explicit_action' do
+  action :run
+end
+
+python 'with_attributes' do
+  creates 'creates'
+end
+
+python 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/script/recipes/run_ruby.rb b/examples/script/recipes/run_ruby.rb
new file mode 100644
index 0000000..7ba546d
--- /dev/null
+++ b/examples/script/recipes/run_ruby.rb
@@ -0,0 +1,13 @@
+ruby 'default_action'
+
+ruby 'explicit_action' do
+  action :run
+end
+
+ruby 'with_attributes' do
+  creates 'creates'
+end
+
+ruby 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/script/recipes/run_script.rb b/examples/script/recipes/run_script.rb
new file mode 100644
index 0000000..45fbc8f
--- /dev/null
+++ b/examples/script/recipes/run_script.rb
@@ -0,0 +1,13 @@
+script 'default_action'
+
+script 'explicit_action' do
+  action :run
+end
+
+script 'with_attributes' do
+  creates 'creates'
+end
+
+script 'specifying the identity attribute' do
+  command 'identity_attribute'
+end
diff --git a/examples/script/spec/run_bash_spec.rb b/examples/script/spec/run_bash_spec.rb
new file mode 100644
index 0000000..bd2c81d
--- /dev/null
+++ b/examples/script/spec/run_bash_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'script::run_bash' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a bash script with the default action' do
+    expect(chef_run).to run_bash('default_action')
+    expect(chef_run).to_not run_bash('not_default_action')
+  end
+
+  it 'runs a bash script with an explicit action' do
+    expect(chef_run).to run_bash('explicit_action')
+  end
+
+  it 'runs a bash script with attributes' do
+    expect(chef_run).to run_bash('with_attributes').with(creates: 'creates')
+    expect(chef_run).to_not run_bash('with_attributes').with(creates: 'bacon')
+  end
+
+  it 'runs a bash script when specifying the identity attribute' do
+    expect(chef_run).to run_bash('identity_attribute')
+  end
+end
diff --git a/examples/script/spec/run_csh_spec.rb b/examples/script/spec/run_csh_spec.rb
new file mode 100644
index 0000000..bfd4978
--- /dev/null
+++ b/examples/script/spec/run_csh_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'script::run_csh' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a csh script with the default action' do
+    expect(chef_run).to run_csh('default_action')
+    expect(chef_run).to_not run_csh('not_default_action')
+  end
+
+  it 'runs a csh script with an explicit action' do
+    expect(chef_run).to run_csh('explicit_action')
+  end
+
+  it 'runs a csh script with attributes' do
+    expect(chef_run).to run_csh('with_attributes').with(creates: 'creates')
+    expect(chef_run).to_not run_csh('with_attributes').with(creates: 'bacon')
+  end
+
+  it 'runs a csh script when specifying the identity attribute' do
+    expect(chef_run).to run_csh('identity_attribute')
+  end
+end
diff --git a/examples/script/spec/run_perl_spec.rb b/examples/script/spec/run_perl_spec.rb
new file mode 100644
index 0000000..1a1f22d
--- /dev/null
+++ b/examples/script/spec/run_perl_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'script::run_perl' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a perl script with the default action' do
+    expect(chef_run).to run_perl('default_action')
+    expect(chef_run).to_not run_perl('not_default_action')
+  end
+
+  it 'runs a perl script with an explicit action' do
+    expect(chef_run).to run_perl('explicit_action')
+  end
+
+  it 'runs a perl script with attributes' do
+    expect(chef_run).to run_perl('with_attributes').with(creates: 'creates')
+    expect(chef_run).to_not run_perl('with_attributes').with(creates: 'bacon')
+  end
+
+  it 'runs a perl script when specifying the identity attribute' do
+    expect(chef_run).to run_perl('identity_attribute')
+  end
+end
diff --git a/examples/script/spec/run_python_spec.rb b/examples/script/spec/run_python_spec.rb
new file mode 100644
index 0000000..cb692c6
--- /dev/null
+++ b/examples/script/spec/run_python_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'script::run_python' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a python script with the default action' do
+    expect(chef_run).to run_python('default_action')
+    expect(chef_run).to_not run_python('not_default_action')
+  end
+
+  it 'runs a python script with an explicit action' do
+    expect(chef_run).to run_python('explicit_action')
+  end
+
+  it 'runs a python script with attributes' do
+    expect(chef_run).to run_python('with_attributes').with(creates: 'creates')
+    expect(chef_run).to_not run_python('with_attributes').with(creates: 'bacon')
+  end
+
+  it 'runs a python script when specifying the identity attribute' do
+    expect(chef_run).to run_python('identity_attribute')
+  end
+end
diff --git a/examples/script/spec/run_ruby_spec.rb b/examples/script/spec/run_ruby_spec.rb
new file mode 100644
index 0000000..1de1ca8
--- /dev/null
+++ b/examples/script/spec/run_ruby_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'script::run_ruby' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a ruby script with the default action' do
+    expect(chef_run).to run_ruby('default_action')
+    expect(chef_run).to_not run_ruby('not_default_action')
+  end
+
+  it 'runs a ruby script with an explicit action' do
+    expect(chef_run).to run_ruby('explicit_action')
+  end
+
+  it 'runs a ruby script with attributes' do
+    expect(chef_run).to run_ruby('with_attributes').with(creates: 'creates')
+    expect(chef_run).to_not run_ruby('with_attributes').with(creates: 'bacon')
+  end
+
+  it 'runs a ruby script when specifying the identity attribute' do
+    expect(chef_run).to run_ruby('identity_attribute')
+  end
+end
diff --git a/examples/script/spec/run_script_spec.rb b/examples/script/spec/run_script_spec.rb
new file mode 100644
index 0000000..d874d56
--- /dev/null
+++ b/examples/script/spec/run_script_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'script::run_script' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'runs a script with the default action' do
+    expect(chef_run).to run_script('default_action')
+    expect(chef_run).to_not run_script('not_default_action')
+  end
+
+  it 'runs a script with an explicit action' do
+    expect(chef_run).to run_script('explicit_action')
+  end
+
+  it 'runs a script with attributes' do
+    expect(chef_run).to run_script('with_attributes').with(creates: 'creates')
+    expect(chef_run).to_not run_script('with_attributes').with(creates: 'bacon')
+  end
+
+  it 'runs a script when specifying the identity attribute' do
+    expect(chef_run).to run_script('identity_attribute')
+  end
+end
diff --git a/examples/server/recipes/client.rb b/examples/server/recipes/client.rb
new file mode 100644
index 0000000..2b82eac
--- /dev/null
+++ b/examples/server/recipes/client.rb
@@ -0,0 +1,6 @@
+clients = search(:client, '*:*')
+clients = clients.map(&:to_s).sort.join(', ')
+
+log 'clients' do
+  message clients
+end
diff --git a/examples/server/recipes/data_bag.rb b/examples/server/recipes/data_bag.rb
new file mode 100644
index 0000000..9ac94df
--- /dev/null
+++ b/examples/server/recipes/data_bag.rb
@@ -0,0 +1,7 @@
+accounts = data_bag('accounts').map do |account|
+  data_bag_item('accounts', account).to_s
+end.sort.join(', ')
+
+log 'accounts' do
+  message accounts
+end
diff --git a/examples/server/recipes/environment.rb b/examples/server/recipes/environment.rb
new file mode 100644
index 0000000..b7b8552
--- /dev/null
+++ b/examples/server/recipes/environment.rb
@@ -0,0 +1,6 @@
+environments = search(:environment, '*:*')
+environments = environments.map(&:to_s).sort.join(', ')
+
+log 'environments' do
+  message environments
+end
diff --git a/examples/server/recipes/node.rb b/examples/server/recipes/node.rb
new file mode 100644
index 0000000..6b66434
--- /dev/null
+++ b/examples/server/recipes/node.rb
@@ -0,0 +1,6 @@
+nodes = search(:node, '*:*')
+nodes = nodes.map(&:to_s).sort.join(', ')
+
+log 'nodes' do
+  message nodes
+end
diff --git a/examples/server/recipes/render_with_cached.rb b/examples/server/recipes/render_with_cached.rb
new file mode 100644
index 0000000..e095852
--- /dev/null
+++ b/examples/server/recipes/render_with_cached.rb
@@ -0,0 +1,3 @@
+file '/tmp/file' do
+  content 'This is content!'
+end
diff --git a/examples/server/recipes/role.rb b/examples/server/recipes/role.rb
new file mode 100644
index 0000000..ebf8093
--- /dev/null
+++ b/examples/server/recipes/role.rb
@@ -0,0 +1,6 @@
+roles = search(:role, '*:*')
+roles = roles.map(&:to_s).sort.join(', ')
+
+log 'roles' do
+  message roles
+end
diff --git a/examples/server/recipes/search.rb b/examples/server/recipes/search.rb
new file mode 100644
index 0000000..0fe77ee
--- /dev/null
+++ b/examples/server/recipes/search.rb
@@ -0,0 +1,5 @@
+# Helper method for formatting the node
+nodes = search(:node, 'bar:*')
+log 'nodes with an attribute' do
+  message nodes.map { |node| "#{node.name}, FQDN: #{node['fqdn']}, hostname: #{node['hostname']}" }.join("\n")
+end
diff --git a/examples/server/spec/client_spec.rb b/examples/server/spec/client_spec.rb
new file mode 100644
index 0000000..329847b
--- /dev/null
+++ b/examples/server/spec/client_spec.rb
@@ -0,0 +1,18 @@
+require 'chefspec'
+
+describe 'server::client' do
+  let(:chef_run) do
+    ChefSpec::ServerRunner.new do |node, server|
+      server.create_client('bacon', { name: 'bacon' })
+    end.converge(described_recipe)
+  end
+
+  it 'does not raise an exception' do
+    expect { chef_run }.to_not raise_error
+  end
+
+  it 'searches the Chef Server for clients' do
+    expect(chef_run).to write_log('clients')
+      .with_message('client[bacon], client[chef-validator], client[chef-webui]')
+  end
+end
diff --git a/examples/server/spec/data_bag_spec.rb b/examples/server/spec/data_bag_spec.rb
new file mode 100644
index 0000000..156f376
--- /dev/null
+++ b/examples/server/spec/data_bag_spec.rb
@@ -0,0 +1,27 @@
+require 'chefspec'
+
+describe 'server::data_bag' do
+  let(:chef_run) do
+    ChefSpec::ServerRunner.new do |node, server|
+      server.create_data_bag('accounts', {
+      'github' => {
+        'username' => 'sethvargo',
+        'password' => 'p at ssW0rd!',
+      },
+      'twitter' => {
+        'username' => 'sethvargo',
+        'password' => '0th3r',
+      },
+    })
+    end.converge(described_recipe)
+  end
+
+  it 'does not raise an exception' do
+    expect { chef_run }.to_not raise_error
+  end
+
+  it 'searches the Chef Server for the data bag' do
+    expect(chef_run).to write_log('accounts')
+      .with_message('data_bag_item[github], data_bag_item[twitter]')
+  end
+end
diff --git a/examples/server/spec/environment_spec.rb b/examples/server/spec/environment_spec.rb
new file mode 100644
index 0000000..9c3f825
--- /dev/null
+++ b/examples/server/spec/environment_spec.rb
@@ -0,0 +1,18 @@
+require 'chefspec'
+
+describe 'server::environment' do
+  let(:chef_run) do
+    ChefSpec::ServerRunner.new do |node, server|
+      server.create_environment('production')
+    end.converge(described_recipe)
+  end
+
+  it 'does not raise an exception' do
+    expect { chef_run }.to_not raise_error
+  end
+
+  it 'searches the Chef Server for environments' do
+    expect(chef_run).to write_log('environments')
+      .with_message('_default, production')
+  end
+end
diff --git a/examples/server/spec/node_spec.rb b/examples/server/spec/node_spec.rb
new file mode 100644
index 0000000..60d3c31
--- /dev/null
+++ b/examples/server/spec/node_spec.rb
@@ -0,0 +1,49 @@
+require 'chefspec'
+
+describe 'server::node' do
+  let(:chef_run) do
+    ChefSpec::ServerRunner.new do |node, server|
+      server.create_node('bacon', { name: 'bacon' })
+    end.converge(described_recipe)
+  end
+
+  it 'does not raise an exception' do
+    expect { chef_run }.to_not raise_error
+  end
+
+  it 'searches the Chef Server for nodes' do
+    expect(chef_run).to write_log('nodes')
+      .with_message('node[bacon], node[chefspec]')
+  end
+
+  context 'with custom Ohai data' do
+    let(:chef_run) do
+      ChefSpec::ServerRunner.new(platform: 'ubuntu', version: '12.04')
+        .converge(described_recipe)
+    end
+
+    it 'has the node data' do
+      expect(chef_run).to have_node('chefspec')
+
+      node = chef_run.get_node('chefspec')
+      expect(node['kernel']['name']).to eq('Linux')
+      expect(node['kernel']['release']).to eq('3.2.0-26-generic')
+      expect(node['kernel']['machine']).to eq('x86_64')
+    end
+  end
+
+  context 'with overridden node data' do
+    let(:chef_run) do
+      ChefSpec::ServerRunner.new do |node, server|
+        node.set['breakfast']['bacon'] = true
+      end.converge(described_recipe)
+    end
+
+    it 'has the node data' do
+      expect(chef_run).to have_node('chefspec')
+
+      node = chef_run.get_node('chefspec')
+      expect(node['breakfast']['bacon']).to be true
+    end
+  end
+end
diff --git a/examples/server/spec/render_with_cached_spec.rb b/examples/server/spec/render_with_cached_spec.rb
new file mode 100644
index 0000000..89cf849
--- /dev/null
+++ b/examples/server/spec/render_with_cached_spec.rb
@@ -0,0 +1,15 @@
+require 'chefspec'
+require 'chefspec/cacher'
+
+RSpec.configure do |config|
+  config.extend(ChefSpec::Cacher)
+end
+
+describe 'server::render_with_cached' do
+  cached(:chef_run) { ChefSpec::ServerRunner.converge(described_recipe) }
+
+  it 'does not cache file requests' do
+    expect(chef_run).to render_file('/tmp/file')
+    expect(chef_run).to render_file('/tmp/file').with_content('This is content!')
+  end
+end
diff --git a/examples/server/spec/role_spec.rb b/examples/server/spec/role_spec.rb
new file mode 100644
index 0000000..8767308
--- /dev/null
+++ b/examples/server/spec/role_spec.rb
@@ -0,0 +1,18 @@
+require 'chefspec'
+
+describe 'server::role' do
+  let(:chef_run) do
+    ChefSpec::ServerRunner.new do |node, server|
+      server.create_role('webserver')
+    end.converge(described_recipe)
+  end
+
+  it 'does not raise an exception' do
+    expect { chef_run }.to_not raise_error
+  end
+
+  it 'searches the Chef Server for roles' do
+    expect(chef_run).to write_log('roles')
+      .with_message('role[webserver]')
+  end
+end
diff --git a/examples/server/spec/search_spec.rb b/examples/server/spec/search_spec.rb
new file mode 100644
index 0000000..27e9a1b
--- /dev/null
+++ b/examples/server/spec/search_spec.rb
@@ -0,0 +1,61 @@
+require 'chefspec'
+
+describe 'server::search' do
+  let(:node_1) do
+    stub_node('node_1') do |node|
+      node.automatic['hostname'] = 'node_1'
+      node.automatic['fqdn'] = 'node_1.example.com'
+      node.set['bar'] = true
+    end
+  end
+
+  let(:node_2) do
+    stub_node('node_2') do |node|
+      node.automatic['hostname'] = 'node_2'
+      node.automatic['fqdn'] = 'node_2.example.com'
+      node.set['bar'] = true
+    end
+  end
+
+  let(:node_3) do
+    stub_node('node_3') do |node|
+      node.automatic['hostname'] = 'node_3'
+      node.automatic['fqdn'] = 'node_3.example.com'
+      node.set['bar'] = true
+    end
+  end
+
+  let(:node_4) do
+    stub_node('node_4') do |node|
+      node.automatic['hostname'] = 'node_4'
+      node.automatic['fqdn'] = 'node_4.example.com'
+    end
+  end
+
+  let(:chef_run) do
+    ChefSpec::ServerRunner.new do |node, server|
+      node.set['bar'] = true
+      server.update_node(node)
+
+      server.create_node(node_1)
+      server.create_node(node_2)
+      server.create_node(node_3)
+      server.create_node(node_4)
+    end.converge(described_recipe)
+  end
+
+  it 'finds all nodes with the bar attribute' do
+    expect(chef_run).to write_log('nodes with an attribute')
+      .with_message(<<-EOH.gsub(/^ {8}/, '').strip)
+        chefspec, FQDN: chefspec.local, hostname: chefspec
+        node_1, FQDN: node_1.example.com, hostname: node_1
+        node_2, FQDN: node_2.example.com, hostname: node_2
+        node_3, FQDN: node_3.example.com, hostname: node_3
+      EOH
+  end
+
+  it 'does not find the node without the bar attribute' do
+    expect(chef_run).to_not write_log('nodes with an attribute')
+      .with_message(/node_4/)
+  end
+end
diff --git a/examples/service/recipes/disable.rb b/examples/service/recipes/disable.rb
new file mode 100644
index 0000000..f1aa8cb
--- /dev/null
+++ b/examples/service/recipes/disable.rb
@@ -0,0 +1,13 @@
+service 'explicit_action' do
+  action :disable
+end
+
+service 'with_attributes' do
+  pattern 'pattern'
+  action :disable
+end
+
+service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action      :disable
+end
diff --git a/examples/service/recipes/enable.rb b/examples/service/recipes/enable.rb
new file mode 100644
index 0000000..a729a81
--- /dev/null
+++ b/examples/service/recipes/enable.rb
@@ -0,0 +1,13 @@
+service 'explicit_action' do
+  action :enable
+end
+
+service 'with_attributes' do
+  pattern 'pattern'
+  action :enable
+end
+
+service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action      :enable
+end
diff --git a/examples/service/recipes/reload.rb b/examples/service/recipes/reload.rb
new file mode 100644
index 0000000..9dce03f
--- /dev/null
+++ b/examples/service/recipes/reload.rb
@@ -0,0 +1,13 @@
+service 'explicit_action' do
+  action :reload
+end
+
+service 'with_attributes' do
+  pattern 'pattern'
+  action :reload
+end
+
+service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action      :reload
+end
diff --git a/examples/service/recipes/restart.rb b/examples/service/recipes/restart.rb
new file mode 100644
index 0000000..0e1a7e8
--- /dev/null
+++ b/examples/service/recipes/restart.rb
@@ -0,0 +1,13 @@
+service 'explicit_action' do
+  action :restart
+end
+
+service 'with_attributes' do
+  pattern 'pattern'
+  action :restart
+end
+
+service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action      :restart
+end
diff --git a/examples/service/recipes/start.rb b/examples/service/recipes/start.rb
new file mode 100644
index 0000000..ef81e6a
--- /dev/null
+++ b/examples/service/recipes/start.rb
@@ -0,0 +1,13 @@
+service 'explicit_action' do
+  action :start
+end
+
+service 'with_attributes' do
+  pattern 'pattern'
+  action :start
+end
+
+service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action      :start
+end
diff --git a/examples/service/recipes/stop.rb b/examples/service/recipes/stop.rb
new file mode 100644
index 0000000..10dde1c
--- /dev/null
+++ b/examples/service/recipes/stop.rb
@@ -0,0 +1,13 @@
+service 'explicit_action' do
+  action :stop
+end
+
+service 'with_attributes' do
+  pattern 'pattern'
+  action :stop
+end
+
+service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action      :stop
+end
diff --git a/examples/service/spec/disable_spec.rb b/examples/service/spec/disable_spec.rb
new file mode 100644
index 0000000..926bd15
--- /dev/null
+++ b/examples/service/spec/disable_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'service::disable' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'disables a service with an explicit action' do
+    expect(chef_run).to disable_service('explicit_action')
+    expect(chef_run).to_not disable_service('not_explicit_action')
+  end
+
+  it 'disables a service with attributes' do
+    expect(chef_run).to disable_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not disable_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'disables a service when specifying the identity attribute' do
+    expect(chef_run).to disable_service('identity_attribute')
+  end
+end
diff --git a/examples/service/spec/enable_spec.rb b/examples/service/spec/enable_spec.rb
new file mode 100644
index 0000000..0b5ed04
--- /dev/null
+++ b/examples/service/spec/enable_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'service::enable' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'enables a service with an explicit action' do
+    expect(chef_run).to enable_service('explicit_action')
+    expect(chef_run).to_not enable_service('not_explicit_action')
+  end
+
+  it 'enables a service with attributes' do
+    expect(chef_run).to enable_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not enable_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'enables a service when specifying the identity attribute' do
+    expect(chef_run).to enable_service('identity_attribute')
+  end
+end
diff --git a/examples/service/spec/reload_spec.rb b/examples/service/spec/reload_spec.rb
new file mode 100644
index 0000000..cc481ea
--- /dev/null
+++ b/examples/service/spec/reload_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'service::reload' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'reloads a service with an explicit action' do
+    expect(chef_run).to reload_service('explicit_action')
+    expect(chef_run).to_not reload_service('not_explicit_action')
+  end
+
+  it 'reloads a service with attributes' do
+    expect(chef_run).to reload_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not reload_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'reloads a service when specifying the identity attribute' do
+    expect(chef_run).to reload_service('identity_attribute')
+  end
+end
diff --git a/examples/service/spec/restart_spec.rb b/examples/service/spec/restart_spec.rb
new file mode 100644
index 0000000..bc56552
--- /dev/null
+++ b/examples/service/spec/restart_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'service::restart' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'restarts a service with an explicit action' do
+    expect(chef_run).to restart_service('explicit_action')
+    expect(chef_run).to_not restart_service('not_explicit_action')
+  end
+
+  it 'restarts a service with attributes' do
+    expect(chef_run).to restart_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not restart_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'restarts a service when specifying the identity attribute' do
+    expect(chef_run).to restart_service('identity_attribute')
+  end
+end
diff --git a/examples/service/spec/start_spec.rb b/examples/service/spec/start_spec.rb
new file mode 100644
index 0000000..b081e67
--- /dev/null
+++ b/examples/service/spec/start_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'service::start' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'starts a service with an explicit action' do
+    expect(chef_run).to start_service('explicit_action')
+    expect(chef_run).to_not start_service('not_explicit_action')
+  end
+
+  it 'starts a service with attributes' do
+    expect(chef_run).to start_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not start_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'starts a service when specifying the identity attribute' do
+    expect(chef_run).to start_service('identity_attribute')
+  end
+end
diff --git a/examples/service/spec/stop_spec.rb b/examples/service/spec/stop_spec.rb
new file mode 100644
index 0000000..c79f56b
--- /dev/null
+++ b/examples/service/spec/stop_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'service::stop' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'stops a service with an explicit action' do
+    expect(chef_run).to stop_service('explicit_action')
+    expect(chef_run).to_not stop_service('not_explicit_action')
+  end
+
+  it 'stops a service with attributes' do
+    expect(chef_run).to stop_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not stop_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'stops a service when specifying the identity attribute' do
+    expect(chef_run).to stop_service('identity_attribute')
+  end
+end
diff --git a/examples/smartos_package/recipes/install.rb b/examples/smartos_package/recipes/install.rb
new file mode 100644
index 0000000..52c5d78
--- /dev/null
+++ b/examples/smartos_package/recipes/install.rb
@@ -0,0 +1,13 @@
+smartos_package 'default_action'
+
+smartos_package 'explicit_action' do
+  action :install
+end
+
+smartos_package 'with_attributes' do
+  version '1.0.0'
+end
+
+smartos_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/smartos_package/recipes/remove.rb b/examples/smartos_package/recipes/remove.rb
new file mode 100644
index 0000000..4eb73b7
--- /dev/null
+++ b/examples/smartos_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+smartos_package 'explicit_action' do
+  action :remove
+end
+
+smartos_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+smartos_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/smartos_package/recipes/upgrade.rb b/examples/smartos_package/recipes/upgrade.rb
new file mode 100644
index 0000000..3580f91
--- /dev/null
+++ b/examples/smartos_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+smartos_package 'explicit_action' do
+  action :upgrade
+end
+
+smartos_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+smartos_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/smartos_package/spec/install_spec.rb b/examples/smartos_package/spec/install_spec.rb
new file mode 100644
index 0000000..c7774ce
--- /dev/null
+++ b/examples/smartos_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'smartos_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a smartos_package with the default action' do
+    expect(chef_run).to install_smartos_package('default_action')
+    expect(chef_run).to_not install_smartos_package('not_default_action')
+  end
+
+  it 'installs a smartos_package with an explicit action' do
+    expect(chef_run).to install_smartos_package('explicit_action')
+  end
+
+  it 'installs a smartos_package with attributes' do
+    expect(chef_run).to install_smartos_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_smartos_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a smartos_package when specifying the identity attribute' do
+    expect(chef_run).to install_smartos_package('identity_attribute')
+  end
+end
diff --git a/examples/smartos_package/spec/remove_spec.rb b/examples/smartos_package/spec/remove_spec.rb
new file mode 100644
index 0000000..8761eca
--- /dev/null
+++ b/examples/smartos_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'smartos_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a smartos_package with an explicit action' do
+    expect(chef_run).to remove_smartos_package('explicit_action')
+    expect(chef_run).to_not remove_smartos_package('not_explicit_action')
+  end
+
+  it 'removes a smartos_package with attributes' do
+    expect(chef_run).to remove_smartos_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_smartos_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a smartos_package when specifying the identity attribute' do
+    expect(chef_run).to remove_smartos_package('identity_attribute')
+  end
+end
diff --git a/examples/smartos_package/spec/upgrade_spec.rb b/examples/smartos_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..51c9291
--- /dev/null
+++ b/examples/smartos_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'smartos_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a smartos_package with an explicit action' do
+    expect(chef_run).to upgrade_smartos_package('explicit_action')
+    expect(chef_run).to_not upgrade_smartos_package('not_explicit_action')
+  end
+
+  it 'upgrades a smartos_package with attributes' do
+    expect(chef_run).to upgrade_smartos_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_smartos_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a smartos_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_smartos_package('identity_attribute')
+  end
+end
diff --git a/examples/solaris_package/recipes/install.rb b/examples/solaris_package/recipes/install.rb
new file mode 100644
index 0000000..307dbe8
--- /dev/null
+++ b/examples/solaris_package/recipes/install.rb
@@ -0,0 +1,13 @@
+solaris_package 'default_action'
+
+solaris_package 'explicit_action' do
+  action :install
+end
+
+solaris_package 'with_attributes' do
+  version '1.0.0'
+end
+
+solaris_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/solaris_package/recipes/remove.rb b/examples/solaris_package/recipes/remove.rb
new file mode 100644
index 0000000..b80cf52
--- /dev/null
+++ b/examples/solaris_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+solaris_package 'explicit_action' do
+  action :remove
+end
+
+solaris_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+solaris_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/solaris_package/spec/install_spec.rb b/examples/solaris_package/spec/install_spec.rb
new file mode 100644
index 0000000..bacd738
--- /dev/null
+++ b/examples/solaris_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'solaris_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a solaris_package with the default action' do
+    expect(chef_run).to install_solaris_package('default_action')
+    expect(chef_run).to_not install_solaris_package('not_default_action')
+  end
+
+  it 'installs a solaris_package with an explicit action' do
+    expect(chef_run).to install_solaris_package('explicit_action')
+  end
+
+  it 'installs a solaris_package with attributes' do
+    expect(chef_run).to install_solaris_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_solaris_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a solaris_package when specifying the identity attribute' do
+    expect(chef_run).to install_solaris_package('identity_attribute')
+  end
+end
diff --git a/examples/solaris_package/spec/remove_spec.rb b/examples/solaris_package/spec/remove_spec.rb
new file mode 100644
index 0000000..368165a
--- /dev/null
+++ b/examples/solaris_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'solaris_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a solaris_package with an explicit action' do
+    expect(chef_run).to remove_solaris_package('explicit_action')
+    expect(chef_run).to_not remove_solaris_package('not_explicit_action')
+  end
+
+  it 'removes a solaris_package with attributes' do
+    expect(chef_run).to remove_solaris_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_solaris_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a solaris_package when specifying the identity attribute' do
+    expect(chef_run).to remove_solaris_package('identity_attribute')
+  end
+end
diff --git a/examples/state_attrs/providers/lwrp.rb b/examples/state_attrs/providers/lwrp.rb
new file mode 100644
index 0000000..33674d9
--- /dev/null
+++ b/examples/state_attrs/providers/lwrp.rb
@@ -0,0 +1,3 @@
+action :run do
+
+end
diff --git a/examples/state_attrs/recipes/default.rb b/examples/state_attrs/recipes/default.rb
new file mode 100644
index 0000000..6238ba2
--- /dev/null
+++ b/examples/state_attrs/recipes/default.rb
@@ -0,0 +1,3 @@
+state_attrs_lwrp 'name' do
+  time '50'
+end
diff --git a/examples/state_attrs/resources/lwrp.rb b/examples/state_attrs/resources/lwrp.rb
new file mode 100644
index 0000000..f72ccf5
--- /dev/null
+++ b/examples/state_attrs/resources/lwrp.rb
@@ -0,0 +1,6 @@
+actions :run
+default_action :run
+state_attrs :name, :time
+
+attribute :name, name_attribute: true
+attribute :time
diff --git a/examples/state_attrs/spec/default_spec.rb b/examples/state_attrs/spec/default_spec.rb
new file mode 100644
index 0000000..0f8fd0a
--- /dev/null
+++ b/examples/state_attrs/spec/default_spec.rb
@@ -0,0 +1,12 @@
+require 'chefspec'
+
+ChefSpec.define_matcher(:state_attrs_lwrp)
+
+describe 'state_attrs::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:lwrp) { chef_run.state_attrs_lwrp('name') }
+
+  it 'has the correct state attributes' do
+    expect(lwrp).to have_state_attrs(:name, :time)
+  end
+end
diff --git a/examples/step_into/providers/lwrp.rb b/examples/step_into/providers/lwrp.rb
new file mode 100644
index 0000000..99d6c81
--- /dev/null
+++ b/examples/step_into/providers/lwrp.rb
@@ -0,0 +1,3 @@
+action :something do
+  log 'message'
+end
diff --git a/examples/step_into/recipes/default.rb b/examples/step_into/recipes/default.rb
new file mode 100644
index 0000000..0209113
--- /dev/null
+++ b/examples/step_into/recipes/default.rb
@@ -0,0 +1 @@
+step_into_lwrp 'thing'
diff --git a/examples/step_into/resources/lwrp.rb b/examples/step_into/resources/lwrp.rb
new file mode 100644
index 0000000..ea8d0e9
--- /dev/null
+++ b/examples/step_into/resources/lwrp.rb
@@ -0,0 +1,4 @@
+actions :something
+default_action :something
+
+attribute :name,   kind_of: String, name_attribute: true
diff --git a/examples/step_into/spec/default_spec.rb b/examples/step_into/spec/default_spec.rb
new file mode 100644
index 0000000..1da202e
--- /dev/null
+++ b/examples/step_into/spec/default_spec.rb
@@ -0,0 +1,22 @@
+require 'chefspec'
+
+describe 'step_into::default' do
+  context 'without :step_into' do
+    let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+    it 'does not execute the LWRPs action' do
+      expect(chef_run).to_not write_log('message')
+    end
+  end
+
+  context 'with :step_into' do
+    let(:chef_run) do
+      ChefSpec::SoloRunner.new(step_into: ['step_into_lwrp'])
+        .converge(described_recipe)
+      end
+
+    it 'does execute the LWRPs action' do
+      expect(chef_run).to write_log('message')
+    end
+  end
+end
diff --git a/examples/stub_command/recipes/default.rb b/examples/stub_command/recipes/default.rb
new file mode 100644
index 0000000..e6fee66
--- /dev/null
+++ b/examples/stub_command/recipes/default.rb
@@ -0,0 +1,5 @@
+log 'message' do
+  only_if 'test -f "/tmp/file"'
+end
+
+include_recipe 'stub_command::other_recipe'
diff --git a/examples/stub_command/recipes/other_recipe.rb b/examples/stub_command/recipes/other_recipe.rb
new file mode 100644
index 0000000..2684f74
--- /dev/null
+++ b/examples/stub_command/recipes/other_recipe.rb
@@ -0,0 +1,3 @@
+log 'other message' do
+  only_if 'test -f "/tmp/other_file"'
+end
diff --git a/examples/stub_command/spec/default_spec.rb b/examples/stub_command/spec/default_spec.rb
new file mode 100644
index 0000000..52bd5e8
--- /dev/null
+++ b/examples/stub_command/spec/default_spec.rb
@@ -0,0 +1,28 @@
+require 'chefspec'
+
+describe 'stub_command::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  context 'when the command is not stubbed' do
+    it 'raises an exception' do
+      expect {
+        chef_run
+      }.to raise_error(ChefSpec::Error::CommandNotStubbed)
+    end
+  end
+
+  context 'as a String' do
+    it 'does not raise an exception' do
+      stub_command('test -f "/tmp/file"').and_return(true)
+      stub_command('test -f "/tmp/other_file"').and_return(true)
+      expect { chef_run }.to_not raise_error
+    end
+  end
+
+  context 'as a Regexp' do
+    it 'does not raise an exception' do
+      stub_command(/test -f "(.+)"/) { 1 == 2 }
+      expect { chef_run }.to_not raise_error
+    end
+  end
+end
diff --git a/examples/stub_data_bag/recipes/default.rb b/examples/stub_data_bag/recipes/default.rb
new file mode 100644
index 0000000..243cdb5
--- /dev/null
+++ b/examples/stub_data_bag/recipes/default.rb
@@ -0,0 +1,5 @@
+template '/tmp/specific_stub' do
+  variables(
+    users: data_bag(:users)
+  )
+end
diff --git a/examples/stub_data_bag/spec/default_spec.rb b/examples/stub_data_bag/spec/default_spec.rb
new file mode 100644
index 0000000..7b80274
--- /dev/null
+++ b/examples/stub_data_bag/spec/default_spec.rb
@@ -0,0 +1,27 @@
+require 'chefspec'
+
+describe 'stub_data_bag::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  context 'when the data_bag is not stubbed' do
+    it 'raises an exception' do
+      expect {
+        chef_run
+      }.to raise_error(ChefSpec::Error::DataBagNotStubbed)
+    end
+  end
+
+  context 'as a String' do
+    it 'does not raise an exception' do
+      stub_data_bag('users').and_return(['svargo'])
+      expect { chef_run }.to_not raise_error
+    end
+  end
+
+  context 'as a Symbol' do
+    it 'does not raise an exception' do
+      stub_data_bag(:users).and_return(['svargo'])
+      expect { chef_run }.to_not raise_error
+    end
+  end
+end
diff --git a/examples/stub_data_bag_item/recipes/default.rb b/examples/stub_data_bag_item/recipes/default.rb
new file mode 100644
index 0000000..36ecc37
--- /dev/null
+++ b/examples/stub_data_bag_item/recipes/default.rb
@@ -0,0 +1,5 @@
+template '/tmp/specific_stub' do
+  variables(
+    users: data_bag_item(:users, 'svargo')
+  )
+end
diff --git a/examples/stub_data_bag_item/spec/default_spec.rb b/examples/stub_data_bag_item/spec/default_spec.rb
new file mode 100644
index 0000000..1a52e57
--- /dev/null
+++ b/examples/stub_data_bag_item/spec/default_spec.rb
@@ -0,0 +1,27 @@
+require 'chefspec'
+
+describe 'stub_data_bag_item::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  context 'when the data_bag_item is not stubbed' do
+    it 'raises an exception' do
+      expect {
+        chef_run
+      }.to raise_error(ChefSpec::Error::DataBagItemNotStubbed)
+    end
+  end
+
+  context 'as a Symbol' do
+    it 'does not raise an exception' do
+      stub_data_bag_item(:users, 'svargo').and_return({ id: 'svargo', name: 'Seth' })
+      expect { chef_run }.to_not raise_error
+    end
+  end
+
+  context 'as a String' do
+    it 'does not raise an exception' do
+      stub_data_bag_item('users', 'svargo').and_return({ id: 'svargo', name: 'Seth' })
+      expect { chef_run }.to_not raise_error
+    end
+  end
+end
diff --git a/examples/stub_node/recipes/default.rb b/examples/stub_node/recipes/default.rb
new file mode 100644
index 0000000..e69de29
diff --git a/examples/stub_node/spec/default_spec.rb b/examples/stub_node/spec/default_spec.rb
new file mode 100644
index 0000000..88a6192
--- /dev/null
+++ b/examples/stub_node/spec/default_spec.rb
@@ -0,0 +1,17 @@
+require 'chefspec'
+
+describe 'stub_node::default' do
+  let(:my_node) do
+    stub_node('example.com', platform: 'ubuntu', version: '12.04') do |node|
+      node.set['foo']['bar'] = 'zip'
+    end
+  end
+
+  it 'sets the fauxhai attributes' do
+    expect(my_node['kernel']['name']).to eq('Linux')
+  end
+
+  it 'uses the overridden attributes' do
+    expect(my_node['foo']['bar']).to eq('zip')
+  end
+end
diff --git a/examples/stub_search/recipes/default.rb b/examples/stub_search/recipes/default.rb
new file mode 100644
index 0000000..d3e73e0
--- /dev/null
+++ b/examples/stub_search/recipes/default.rb
@@ -0,0 +1,5 @@
+template '/tmp/specific_stub' do
+  variables(
+    nodes: search(:node, 'name:example.com')
+  )
+end
diff --git a/examples/stub_search/spec/default_spec.rb b/examples/stub_search/spec/default_spec.rb
new file mode 100644
index 0000000..464951c
--- /dev/null
+++ b/examples/stub_search/spec/default_spec.rb
@@ -0,0 +1,27 @@
+require 'chefspec'
+
+describe 'stub_search::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  context 'when the search is not stubbed' do
+    it 'raises an exception' do
+      expect {
+        chef_run
+      }.to raise_error(ChefSpec::Error::SearchNotStubbed)
+    end
+  end
+
+  context 'as a String' do
+    it 'does not raise an exception' do
+      stub_search(:node, 'name:example.com').and_return([ { name: 'example.com' } ])
+      expect { chef_run }.to_not raise_error
+    end
+  end
+
+  context 'as a Regexp' do
+    it 'does not raise an exception' do
+      stub_search(:node, /name:(.+)/).and_return([ { name: 'example.com' } ])
+      expect { chef_run }.to_not raise_error
+    end
+  end
+end
diff --git a/examples/subscribes/recipes/chained.rb b/examples/subscribes/recipes/chained.rb
new file mode 100644
index 0000000..aa24e04
--- /dev/null
+++ b/examples/subscribes/recipes/chained.rb
@@ -0,0 +1,10 @@
+template 'template'
+
+service 'service' do
+  subscribes :create, 'template[template]'
+end
+
+log 'log' do
+  subscribes :restart, 'service[service]'
+  action :nothing
+end
diff --git a/examples/subscribes/recipes/default.rb b/examples/subscribes/recipes/default.rb
new file mode 100644
index 0000000..5fdaaca
--- /dev/null
+++ b/examples/subscribes/recipes/default.rb
@@ -0,0 +1,5 @@
+template '/tmp/notifying_resource'
+
+service 'receiving_resource' do
+  subscribes :create, 'template[/tmp/notifying_resource]'
+end
diff --git a/examples/subscribes/recipes/delayed.rb b/examples/subscribes/recipes/delayed.rb
new file mode 100644
index 0000000..bb50c81
--- /dev/null
+++ b/examples/subscribes/recipes/delayed.rb
@@ -0,0 +1,5 @@
+template '/tmp/notifying_resource'
+
+service 'receiving_resource' do
+  subscribes :create, 'template[/tmp/notifying_resource]', :delayed
+end
diff --git a/examples/subscribes/recipes/immediately.rb b/examples/subscribes/recipes/immediately.rb
new file mode 100644
index 0000000..cc2c6f6
--- /dev/null
+++ b/examples/subscribes/recipes/immediately.rb
@@ -0,0 +1,5 @@
+template '/tmp/notifying_resource'
+
+service 'receiving_resource' do
+  subscribes :create, 'template[/tmp/notifying_resource]', :immediately
+end
diff --git a/examples/subscribes/spec/chained_spec.rb b/examples/subscribes/spec/chained_spec.rb
new file mode 100644
index 0000000..77f742f
--- /dev/null
+++ b/examples/subscribes/spec/chained_spec.rb
@@ -0,0 +1,21 @@
+require 'chefspec'
+
+describe 'subscribes::chained' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:service)  { chef_run.service('service') }
+  let(:log)      { chef_run.log('log') }
+
+  it 'subscribes to the template creation' do
+    expect(service).to subscribe_to('template[template]')
+    expect(service).to_not subscribe_to('template[not_template]')
+  end
+
+  it 'subscribes to a subscription from the first' do
+    expect(log).to subscribe_to('service[service]')
+  end
+
+  it 'subscribes to the specific subscription' do
+    expect(service).to subscribe_to('template[template]').on(:create)
+    expect(service).to_not subscribe_to('template[template]').on(:delete)
+  end
+end
diff --git a/examples/subscribes/spec/default_spec.rb b/examples/subscribes/spec/default_spec.rb
new file mode 100644
index 0000000..82b179d
--- /dev/null
+++ b/examples/subscribes/spec/default_spec.rb
@@ -0,0 +1,17 @@
+require 'chefspec'
+
+describe 'subscribes::default' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:service)  { chef_run.service('receiving_resource') }
+
+  it 'subscribes to the template creation' do
+    expect(service).to subscribe_to('template[/tmp/notifying_resource]')
+    expect(service).to_not subscribe_to('template[not_receiving_resource]')
+  end
+
+  it 'subscribes to the specific action on the resource' do
+    expect(service).to subscribe_to('template[/tmp/notifying_resource]').on(:create)
+    expect(service).to_not subscribe_to('template[/tmp/notifying_resource]').on(:delete)
+  end
+end
+
diff --git a/examples/subscribes/spec/delayed_spec.rb b/examples/subscribes/spec/delayed_spec.rb
new file mode 100644
index 0000000..9d23a16
--- /dev/null
+++ b/examples/subscribes/spec/delayed_spec.rb
@@ -0,0 +1,16 @@
+require 'chefspec'
+
+describe 'subscribes::delayed' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:service) { chef_run.service('receiving_resource') }
+
+  it 'subscribes to the template creation' do
+    expect(service).to subscribe_to('template[/tmp/notifying_resource]').delayed
+    expect(service).to_not subscribe_to('template[/tmp/not_notifying_resource]').delayed
+  end
+
+  it 'subscribes to the specific action on the resource delayed' do
+    expect(service).to subscribe_to('template[/tmp/notifying_resource]').on(:create).delayed
+    expect(service).to_not subscribe_to('template[/tmp/notifying_resource]').on(:delete).immediately
+  end
+end
diff --git a/examples/subscribes/spec/immediately_spec.rb b/examples/subscribes/spec/immediately_spec.rb
new file mode 100644
index 0000000..c2c9931
--- /dev/null
+++ b/examples/subscribes/spec/immediately_spec.rb
@@ -0,0 +1,16 @@
+require 'chefspec'
+
+describe 'subscribes::immediately' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+  let(:service) { chef_run.service('receiving_resource') }
+
+  it 'sends a notification to the service' do
+    expect(service).to subscribe_to('template[/tmp/notifying_resource]').immediately
+    expect(service).to_not subscribe_to('template[/tmp/not_notifying_resource]').immediately
+  end
+
+  it 'sends the specific notification to the serivce immediately' do
+    expect(service).to subscribe_to('template[/tmp/notifying_resource]').on(:create).immediately
+    expect(service).to_not subscribe_to('template[/tmp/notifying_resource]').on(:delete).delayed
+  end
+end
diff --git a/examples/subversion/recipes/checkout.rb b/examples/subversion/recipes/checkout.rb
new file mode 100644
index 0000000..2096f9e
--- /dev/null
+++ b/examples/subversion/recipes/checkout.rb
@@ -0,0 +1,13 @@
+subversion '/tmp/explicit_action' do
+  action :checkout
+end
+
+subversion '/tmp/with_attributes' do
+  repository 'ssh://subversion.path'
+  action     :checkout
+end
+
+subversion 'specifying the identity attribute' do
+  destination '/tmp/identity_attribute'
+  action      :checkout
+end
diff --git a/examples/subversion/recipes/export.rb b/examples/subversion/recipes/export.rb
new file mode 100644
index 0000000..a5ee871
--- /dev/null
+++ b/examples/subversion/recipes/export.rb
@@ -0,0 +1,13 @@
+subversion '/tmp/explicit_action' do
+  action :export
+end
+
+subversion '/tmp/with_attributes' do
+  repository 'ssh://subversion.path'
+  action     :export
+end
+
+subversion 'specifying the identity attribute' do
+  destination '/tmp/identity_attribute'
+  action      :export
+end
diff --git a/examples/subversion/recipes/force_export.rb b/examples/subversion/recipes/force_export.rb
new file mode 100644
index 0000000..6f3bb8b
--- /dev/null
+++ b/examples/subversion/recipes/force_export.rb
@@ -0,0 +1,13 @@
+subversion '/tmp/explicit_action' do
+  action :force_export
+end
+
+subversion '/tmp/with_attributes' do
+  repository 'ssh://subversion.path'
+  action     :force_export
+end
+
+subversion 'specifying the identity attribute' do
+  destination '/tmp/identity_attribute'
+  action      :force_export
+end
diff --git a/examples/subversion/recipes/sync.rb b/examples/subversion/recipes/sync.rb
new file mode 100644
index 0000000..9d7d6e2
--- /dev/null
+++ b/examples/subversion/recipes/sync.rb
@@ -0,0 +1,13 @@
+subversion '/tmp/default_action'
+
+subversion '/tmp/explicit_action' do
+  action :sync
+end
+
+subversion '/tmp/with_attributes' do
+  repository 'ssh://subversion.path'
+end
+
+subversion 'specifying the identity attribute' do
+  destination '/tmp/identity_attribute'
+end
diff --git a/examples/subversion/spec/checkout_spec.rb b/examples/subversion/spec/checkout_spec.rb
new file mode 100644
index 0000000..72d165c
--- /dev/null
+++ b/examples/subversion/spec/checkout_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'subversion::checkout' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'checkouts a subversion with an explicit action' do
+    expect(chef_run).to checkout_subversion('/tmp/explicit_action')
+    expect(chef_run).to_not checkout_subversion('/tmp/not_explicit_action')
+  end
+
+  it 'checkouts a subversion with attributes' do
+    expect(chef_run).to checkout_subversion('/tmp/with_attributes').with(repository: 'ssh://subversion.path')
+    expect(chef_run).to_not checkout_subversion('/tmp/with_attributes').with(repository: 'ssh://subversion.other_path')
+  end
+
+  it 'checkouts a subversion when specifying the identity attribute' do
+    expect(chef_run).to checkout_subversion('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/subversion/spec/export_spec.rb b/examples/subversion/spec/export_spec.rb
new file mode 100644
index 0000000..85b599a
--- /dev/null
+++ b/examples/subversion/spec/export_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'subversion::export' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'exports a subversion with an explicit action' do
+    expect(chef_run).to export_subversion('/tmp/explicit_action')
+    expect(chef_run).to_not export_subversion('/tmp/not_explicit_action')
+  end
+
+  it 'exports a subversion with attributes' do
+    expect(chef_run).to export_subversion('/tmp/with_attributes').with(repository: 'ssh://subversion.path')
+    expect(chef_run).to_not export_subversion('/tmp/with_attributes').with(repository: 'ssh://subversion.other_path')
+  end
+
+  it 'exports a subversion when specifying the identity attribute' do
+    expect(chef_run).to export_subversion('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/subversion/spec/force_export_spec.rb b/examples/subversion/spec/force_export_spec.rb
new file mode 100644
index 0000000..92c1ecf
--- /dev/null
+++ b/examples/subversion/spec/force_export_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'subversion::force_export' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'force_exports a subversion with an explicit action' do
+    expect(chef_run).to force_export_subversion('/tmp/explicit_action')
+    expect(chef_run).to_not force_export_subversion('/tmp/not_explicit_action')
+  end
+
+  it 'force_exports a subversion with attributes' do
+    expect(chef_run).to force_export_subversion('/tmp/with_attributes').with(repository: 'ssh://subversion.path')
+    expect(chef_run).to_not force_export_subversion('/tmp/with_attributes').with(repository: 'ssh://subversion.other_path')
+  end
+
+  it 'force_exports a subversion when specifying the identity attribute' do
+    expect(chef_run).to force_export_subversion('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/subversion/spec/sync_spec.rb b/examples/subversion/spec/sync_spec.rb
new file mode 100644
index 0000000..d3e121e
--- /dev/null
+++ b/examples/subversion/spec/sync_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'subversion::sync' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'syncs a subversion with the default action' do
+    expect(chef_run).to sync_subversion('/tmp/default_action')
+    expect(chef_run).to_not sync_subversion('/tmp/not_default_action')
+  end
+
+  it 'syncs a subversion with an explicit action' do
+    expect(chef_run).to sync_subversion('/tmp/explicit_action')
+  end
+
+  it 'syncs a subversion with attributes' do
+    expect(chef_run).to sync_subversion('/tmp/with_attributes').with(repository: 'ssh://subversion.path')
+    expect(chef_run).to_not sync_subversion('/tmp/with_attributes').with(repository: 'ssh://subversion.other_path')
+  end
+
+  it 'syncs a subversion when specifying the identity attribute' do
+    expect(chef_run).to sync_subversion('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/template/recipes/create.rb b/examples/template/recipes/create.rb
new file mode 100644
index 0000000..70b040c
--- /dev/null
+++ b/examples/template/recipes/create.rb
@@ -0,0 +1,15 @@
+template '/tmp/default_action'
+
+template '/tmp/explicit_action' do
+  action :create
+end
+
+template '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+end
+
+template 'specifying the identity attribute' do
+  path '/tmp/identity_attribute'
+end
diff --git a/examples/template/recipes/create_if_missing.rb b/examples/template/recipes/create_if_missing.rb
new file mode 100644
index 0000000..3984cdf
--- /dev/null
+++ b/examples/template/recipes/create_if_missing.rb
@@ -0,0 +1,15 @@
+template '/tmp/explicit_action' do
+  action :create_if_missing
+end
+
+template '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :create_if_missing
+end
+
+template 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :create_if_missing
+end
diff --git a/examples/template/recipes/delete.rb b/examples/template/recipes/delete.rb
new file mode 100644
index 0000000..96973d5
--- /dev/null
+++ b/examples/template/recipes/delete.rb
@@ -0,0 +1,15 @@
+template '/tmp/explicit_action' do
+  action :delete
+end
+
+template '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :delete
+end
+
+template 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :delete
+end
diff --git a/examples/template/recipes/touch.rb b/examples/template/recipes/touch.rb
new file mode 100644
index 0000000..621cb02
--- /dev/null
+++ b/examples/template/recipes/touch.rb
@@ -0,0 +1,15 @@
+template '/tmp/explicit_action' do
+  action :touch
+end
+
+template '/tmp/with_attributes' do
+  user   'user'
+  group  'group'
+  backup false
+  action :touch
+end
+
+template 'specifying the identity attribute' do
+  path   '/tmp/identity_attribute'
+  action :touch
+end
diff --git a/examples/template/spec/create_if_missing_spec.rb b/examples/template/spec/create_if_missing_spec.rb
new file mode 100644
index 0000000..a4c0ade
--- /dev/null
+++ b/examples/template/spec/create_if_missing_spec.rb
@@ -0,0 +1,28 @@
+require 'chefspec'
+
+describe 'template::create_if_missing' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a template with an explicit action' do
+    expect(chef_run).to create_template_if_missing('/tmp/explicit_action')
+    expect(chef_run).to_not create_template_if_missing('/tmp/not_explicit_action')
+  end
+
+  it 'creates a template with attributes' do
+    expect(chef_run).to create_template_if_missing('/tmp/with_attributes').with(
+      user:   'user',
+      group:  'group',
+      backup: false,
+    )
+
+    expect(chef_run).to_not create_template_if_missing('/tmp/with_attributes').with(
+      user:   'bacon',
+      group:  'fat',
+      backup: true,
+    )
+  end
+
+  it 'creates a template when specifying the identity attribute' do
+    expect(chef_run).to create_template_if_missing('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/template/spec/create_spec.rb b/examples/template/spec/create_spec.rb
new file mode 100644
index 0000000..39835bd
--- /dev/null
+++ b/examples/template/spec/create_spec.rb
@@ -0,0 +1,32 @@
+require 'chefspec'
+
+describe 'template::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a template with the default action' do
+    expect(chef_run).to create_template('/tmp/default_action')
+    expect(chef_run).to_not create_template('/tmp/not_default_action')
+  end
+
+  it 'creates a template with an explicit action' do
+    expect(chef_run).to create_template('/tmp/explicit_action')
+  end
+
+  it 'creates a template with attributes' do
+    expect(chef_run).to create_template('/tmp/with_attributes').with(
+      user:   'user',
+      group:  'group',
+      backup: false,
+    )
+
+    expect(chef_run).to_not create_template('/tmp/with_attributes').with(
+      user:   'bacon',
+      group:  'fat',
+      backup: true,
+    )
+  end
+
+  it 'creates a template when specifying the identity attribute' do
+    expect(chef_run).to create_template('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/template/spec/delete_spec.rb b/examples/template/spec/delete_spec.rb
new file mode 100644
index 0000000..cab296d
--- /dev/null
+++ b/examples/template/spec/delete_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'template::delete' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'deletes a template with an explicit action' do
+    expect(chef_run).to delete_template('/tmp/explicit_action')
+    expect(chef_run).to_not delete_template('/tmp/not_explicit_action')
+  end
+
+  it 'deletes a template with attributes' do
+    expect(chef_run).to delete_template('/tmp/with_attributes').with(backup: false)
+    expect(chef_run).to_not delete_template('/tmp/with_attributes').with(backup: true)
+  end
+
+  it 'deletes a template when specifying the identity attribute' do
+    expect(chef_run).to delete_template('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/template/spec/touch_spec.rb b/examples/template/spec/touch_spec.rb
new file mode 100644
index 0000000..e2d1f43
--- /dev/null
+++ b/examples/template/spec/touch_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'template::touch' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'touches a template with an explicit action' do
+    expect(chef_run).to touch_template('/tmp/explicit_action')
+    expect(chef_run).to_not touch_template('/tmp/not_explicit_action')
+  end
+
+  it 'touches a template with attributes' do
+    expect(chef_run).to touch_template('/tmp/with_attributes').with(backup: false)
+    expect(chef_run).to_not touch_template('/tmp/with_attributes').with(backup: true)
+  end
+
+  it 'touches a template when specifying the identity attribute' do
+    expect(chef_run).to touch_template('/tmp/identity_attribute')
+  end
+end
diff --git a/examples/use_inline_resources/libraries/matchers.rb b/examples/use_inline_resources/libraries/matchers.rb
new file mode 100644
index 0000000..a1cde07
--- /dev/null
+++ b/examples/use_inline_resources/libraries/matchers.rb
@@ -0,0 +1,5 @@
+if defined?(ChefSpec)
+  def run_use_inline_resources_lwrp(resource_name)
+    ChefSpec::Matchers::ResourceMatcher.new(:use_inline_resources_lwrp, :run, resource_name)
+  end
+end
diff --git a/examples/use_inline_resources/providers/lwrp.rb b/examples/use_inline_resources/providers/lwrp.rb
new file mode 100644
index 0000000..bfcf12d
--- /dev/null
+++ b/examples/use_inline_resources/providers/lwrp.rb
@@ -0,0 +1,7 @@
+use_inline_resources
+
+action :run do
+  package('package') { action :install }
+  service('service') { action :start }
+  template('template') { action :create }
+end
diff --git a/examples/use_inline_resources/recipes/default.rb b/examples/use_inline_resources/recipes/default.rb
new file mode 100644
index 0000000..14dff80
--- /dev/null
+++ b/examples/use_inline_resources/recipes/default.rb
@@ -0,0 +1 @@
+use_inline_resources_lwrp 'resource'
diff --git a/examples/use_inline_resources/resources/lwrp.rb b/examples/use_inline_resources/resources/lwrp.rb
new file mode 100644
index 0000000..9024f26
--- /dev/null
+++ b/examples/use_inline_resources/resources/lwrp.rb
@@ -0,0 +1,4 @@
+actions :run
+default_action :run
+
+attribute :name, name_attribute: true
diff --git a/examples/use_inline_resources/spec/default_spec.rb b/examples/use_inline_resources/spec/default_spec.rb
new file mode 100644
index 0000000..ccef526
--- /dev/null
+++ b/examples/use_inline_resources/spec/default_spec.rb
@@ -0,0 +1,18 @@
+require 'chefspec'
+
+describe 'use_inline_resources::default' do
+  let(:chef_run) do
+    ChefSpec::SoloRunner.new(step_into: ['use_inline_resources_lwrp'])
+      .converge(described_recipe)
+    end
+
+  it 'uses the LWRP' do
+    expect(chef_run).to run_use_inline_resources_lwrp('resource')
+  end
+
+  it 'steps into the LWRP' do
+    expect(chef_run).to install_package('package')
+    expect(chef_run).to start_service('service')
+    expect(chef_run).to create_template('template')
+  end
+end
diff --git a/examples/user/recipes/create.rb b/examples/user/recipes/create.rb
new file mode 100644
index 0000000..e3356f0
--- /dev/null
+++ b/examples/user/recipes/create.rb
@@ -0,0 +1,13 @@
+user 'default_action'
+
+user 'explicit_action' do
+  action :create
+end
+
+user 'with_attributes' do
+  uid '1234'
+end
+
+user 'specifying the identity attribute' do
+  username 'identity_attribute'
+end
diff --git a/examples/user/recipes/lock.rb b/examples/user/recipes/lock.rb
new file mode 100644
index 0000000..16751e9
--- /dev/null
+++ b/examples/user/recipes/lock.rb
@@ -0,0 +1,13 @@
+user 'explicit_action' do
+  action :lock
+end
+
+user 'with_attributes' do
+  uid    '1234'
+  action :lock
+end
+
+user 'specifying the identity attribute' do
+  username 'identity_attribute'
+  action   :lock
+end
diff --git a/examples/user/recipes/manage.rb b/examples/user/recipes/manage.rb
new file mode 100644
index 0000000..a969f95
--- /dev/null
+++ b/examples/user/recipes/manage.rb
@@ -0,0 +1,13 @@
+user 'explicit_action' do
+  action :manage
+end
+
+user 'with_attributes' do
+  uid    '1234'
+  action :manage
+end
+
+user 'specifying the identity attribute' do
+  username 'identity_attribute'
+  action   :manage
+end
diff --git a/examples/user/recipes/modify.rb b/examples/user/recipes/modify.rb
new file mode 100644
index 0000000..48ccd49
--- /dev/null
+++ b/examples/user/recipes/modify.rb
@@ -0,0 +1,13 @@
+user 'explicit_action' do
+  action :modify
+end
+
+user 'with_attributes' do
+  uid    '1234'
+  action :modify
+end
+
+user 'specifying the identity attribute' do
+  username 'identity_attribute'
+  action   :modify
+end
diff --git a/examples/user/recipes/remove.rb b/examples/user/recipes/remove.rb
new file mode 100644
index 0000000..aea8a7f
--- /dev/null
+++ b/examples/user/recipes/remove.rb
@@ -0,0 +1,13 @@
+user 'explicit_action' do
+  action :remove
+end
+
+user 'with_attributes' do
+  uid    '1234'
+  action :remove
+end
+
+user 'specifying the identity attribute' do
+  username 'identity_attribute'
+  action   :remove
+end
diff --git a/examples/user/recipes/unlock.rb b/examples/user/recipes/unlock.rb
new file mode 100644
index 0000000..f917764
--- /dev/null
+++ b/examples/user/recipes/unlock.rb
@@ -0,0 +1,13 @@
+user 'explicit_action' do
+  action :unlock
+end
+
+user 'with_attributes' do
+  uid    '1234'
+  action :unlock
+end
+
+user 'specifying the identity attribute' do
+  username 'identity_attribute'
+  action   :unlock
+end
diff --git a/examples/user/spec/create_spec.rb b/examples/user/spec/create_spec.rb
new file mode 100644
index 0000000..329b264
--- /dev/null
+++ b/examples/user/spec/create_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'user::create' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'creates a user with the default action' do
+    expect(chef_run).to create_user('default_action')
+    expect(chef_run).to_not create_user('not_default_action')
+  end
+
+  it 'creates a user with an explicit action' do
+    expect(chef_run).to create_user('explicit_action')
+  end
+
+  it 'creates a user with attributes' do
+    expect(chef_run).to create_user('with_attributes').with(uid: '1234')
+    expect(chef_run).to_not create_user('with_attributes').with(uid: '5678')
+  end
+
+  it 'creates a user when specifying the identity attribute' do
+    expect(chef_run).to create_user('identity_attribute')
+  end
+end
diff --git a/examples/user/spec/lock_spec.rb b/examples/user/spec/lock_spec.rb
new file mode 100644
index 0000000..975d082
--- /dev/null
+++ b/examples/user/spec/lock_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'user::lock' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'locks a user with an explicit action' do
+    expect(chef_run).to lock_user('explicit_action')
+    expect(chef_run).to_not lock_user('not_explicit_action')
+  end
+
+  it 'locks a user with attributes' do
+    expect(chef_run).to lock_user('with_attributes').with(uid: '1234')
+    expect(chef_run).to_not lock_user('with_attributes').with(uid: '5678')
+  end
+
+  it 'locks a user when specifying the identity attribute' do
+    expect(chef_run).to lock_user('identity_attribute')
+  end
+end
diff --git a/examples/user/spec/manage_spec.rb b/examples/user/spec/manage_spec.rb
new file mode 100644
index 0000000..1f79c67
--- /dev/null
+++ b/examples/user/spec/manage_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'user::manage' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'manages a user with an explicit action' do
+    expect(chef_run).to manage_user('explicit_action')
+    expect(chef_run).to_not manage_user('not_explicit_action')
+  end
+
+  it 'manages a user with attributes' do
+    expect(chef_run).to manage_user('with_attributes').with(uid: '1234')
+    expect(chef_run).to_not manage_user('with_attributes').with(uid: '5678')
+  end
+
+  it 'manages a user when specifying the identity attribute' do
+    expect(chef_run).to manage_user('identity_attribute')
+  end
+end
diff --git a/examples/user/spec/modify_spec.rb b/examples/user/spec/modify_spec.rb
new file mode 100644
index 0000000..2758aad
--- /dev/null
+++ b/examples/user/spec/modify_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'user::modify' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'modifys a user with an explicit action' do
+    expect(chef_run).to modify_user('explicit_action')
+    expect(chef_run).to_not modify_user('not_explicit_action')
+  end
+
+  it 'modifys a user with attributes' do
+    expect(chef_run).to modify_user('with_attributes').with(uid: '1234')
+    expect(chef_run).to_not modify_user('with_attributes').with(uid: '5678')
+  end
+
+  it 'modifys a user when specifying the identity attribute' do
+    expect(chef_run).to modify_user('identity_attribute')
+  end
+end
diff --git a/examples/user/spec/remove_spec.rb b/examples/user/spec/remove_spec.rb
new file mode 100644
index 0000000..f0a1ff2
--- /dev/null
+++ b/examples/user/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'user::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a user with an explicit action' do
+    expect(chef_run).to remove_user('explicit_action')
+    expect(chef_run).to_not remove_user('not_explicit_action')
+  end
+
+  it 'removes a user with attributes' do
+    expect(chef_run).to remove_user('with_attributes').with(uid: '1234')
+    expect(chef_run).to_not remove_user('with_attributes').with(uid: '5678')
+  end
+
+  it 'removes a user when specifying the identity attribute' do
+    expect(chef_run).to remove_user('identity_attribute')
+  end
+end
diff --git a/examples/user/spec/unlock_spec.rb b/examples/user/spec/unlock_spec.rb
new file mode 100644
index 0000000..b6bfd90
--- /dev/null
+++ b/examples/user/spec/unlock_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'user::unlock' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'unlocks a user with an explicit action' do
+    expect(chef_run).to unlock_user('explicit_action')
+    expect(chef_run).to_not unlock_user('not_explicit_action')
+  end
+
+  it 'unlocks a user with attributes' do
+    expect(chef_run).to unlock_user('with_attributes').with(uid: '1234')
+    expect(chef_run).to_not unlock_user('with_attributes').with(uid: '5678')
+  end
+
+  it 'unlocks a user when specifying the identity attribute' do
+    expect(chef_run).to unlock_user('identity_attribute')
+  end
+end
diff --git a/examples/windows_service/recipes/configure_startup.rb b/examples/windows_service/recipes/configure_startup.rb
new file mode 100644
index 0000000..4fed654
--- /dev/null
+++ b/examples/windows_service/recipes/configure_startup.rb
@@ -0,0 +1,13 @@
+windows_service 'explicit_action' do
+  action :configure_startup
+end
+
+windows_service 'with_attributes' do
+  pattern 'pattern'
+  action :configure_startup
+end
+
+windows_service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action :configure_startup
+end
diff --git a/examples/windows_service/recipes/disable.rb b/examples/windows_service/recipes/disable.rb
new file mode 100644
index 0000000..8ebfb5c
--- /dev/null
+++ b/examples/windows_service/recipes/disable.rb
@@ -0,0 +1,13 @@
+windows_service 'explicit_action' do
+  action :disable
+end
+
+windows_service 'with_attributes' do
+  pattern 'pattern'
+  action :disable
+end
+
+windows_service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action :disable
+end
diff --git a/examples/windows_service/recipes/enable.rb b/examples/windows_service/recipes/enable.rb
new file mode 100644
index 0000000..2a6b1b0
--- /dev/null
+++ b/examples/windows_service/recipes/enable.rb
@@ -0,0 +1,13 @@
+windows_service 'explicit_action' do
+  action :enable
+end
+
+windows_service 'with_attributes' do
+  pattern 'pattern'
+  action :enable
+end
+
+windows_service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action :enable
+end
diff --git a/examples/windows_service/recipes/reload.rb b/examples/windows_service/recipes/reload.rb
new file mode 100644
index 0000000..ace7fd9
--- /dev/null
+++ b/examples/windows_service/recipes/reload.rb
@@ -0,0 +1,13 @@
+windows_service 'explicit_action' do
+  action :reload
+end
+
+windows_service 'with_attributes' do
+  pattern 'pattern'
+  action :reload
+end
+
+windows_service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action :reload
+end
diff --git a/examples/windows_service/recipes/restart.rb b/examples/windows_service/recipes/restart.rb
new file mode 100644
index 0000000..d51b0a3
--- /dev/null
+++ b/examples/windows_service/recipes/restart.rb
@@ -0,0 +1,13 @@
+windows_service 'explicit_action' do
+  action :restart
+end
+
+windows_service 'with_attributes' do
+  pattern 'pattern'
+  action :restart
+end
+
+windows_service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action :restart
+end
diff --git a/examples/windows_service/recipes/start.rb b/examples/windows_service/recipes/start.rb
new file mode 100644
index 0000000..6183a32
--- /dev/null
+++ b/examples/windows_service/recipes/start.rb
@@ -0,0 +1,13 @@
+windows_service 'explicit_action' do
+  action :start
+end
+
+windows_service 'with_attributes' do
+  pattern 'pattern'
+  action :start
+end
+
+windows_service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action :start
+end
diff --git a/examples/windows_service/recipes/stop.rb b/examples/windows_service/recipes/stop.rb
new file mode 100644
index 0000000..be8b7eb
--- /dev/null
+++ b/examples/windows_service/recipes/stop.rb
@@ -0,0 +1,13 @@
+windows_service 'explicit_action' do
+  action :stop
+end
+
+windows_service 'with_attributes' do
+  pattern 'pattern'
+  action :stop
+end
+
+windows_service 'specifying the identity attribute' do
+  service_name 'identity_attribute'
+  action :stop
+end
diff --git a/examples/windows_service/spec/configure_startup_spec.rb b/examples/windows_service/spec/configure_startup_spec.rb
new file mode 100644
index 0000000..24f53a8
--- /dev/null
+++ b/examples/windows_service/spec/configure_startup_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'windows_service::configure_startup' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'configures startup for a windows_service with an explicit action' do
+    expect(chef_run).to configure_startup_windows_service('explicit_action')
+    expect(chef_run).to_not configure_startup_windows_service('not_explicit_action')
+  end
+
+  it 'configures startup for a windows_service with attributes' do
+    expect(chef_run).to configure_startup_windows_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not configure_startup_windows_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'configures startup for a windows_service when specifying the identity attribute' do
+    expect(chef_run).to configure_startup_windows_service('identity_attribute')
+  end
+end
diff --git a/examples/windows_service/spec/disable_spec.rb b/examples/windows_service/spec/disable_spec.rb
new file mode 100644
index 0000000..b0dfbd3
--- /dev/null
+++ b/examples/windows_service/spec/disable_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'windows_service::disable' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'disables a windows_service with an explicit action' do
+    expect(chef_run).to disable_windows_service('explicit_action')
+    expect(chef_run).to_not disable_windows_service('not_explicit_action')
+  end
+
+  it 'disables a windows_service with attributes' do
+    expect(chef_run).to disable_windows_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not disable_windows_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'disables a windows_service when specifying the identity attribute' do
+    expect(chef_run).to disable_windows_service('identity_attribute')
+  end
+end
diff --git a/examples/windows_service/spec/enable_spec.rb b/examples/windows_service/spec/enable_spec.rb
new file mode 100644
index 0000000..3a28898
--- /dev/null
+++ b/examples/windows_service/spec/enable_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'windows_service::enable' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'enables a windows_service with an explicit action' do
+    expect(chef_run).to enable_windows_service('explicit_action')
+    expect(chef_run).to_not enable_windows_service('not_explicit_action')
+  end
+
+  it 'enables a windows_service with attributes' do
+    expect(chef_run).to enable_windows_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not enable_windows_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'enables a windows_service when specifying the identity attribute' do
+    expect(chef_run).to enable_windows_service('identity_attribute')
+  end
+end
diff --git a/examples/windows_service/spec/reload_spec.rb b/examples/windows_service/spec/reload_spec.rb
new file mode 100644
index 0000000..624c989
--- /dev/null
+++ b/examples/windows_service/spec/reload_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'windows_service::reload' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'reloads a windows_service with an explicit action' do
+    expect(chef_run).to reload_windows_service('explicit_action')
+    expect(chef_run).to_not reload_windows_service('not_explicit_action')
+  end
+
+  it 'reloads a windows_service with attributes' do
+    expect(chef_run).to reload_windows_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not reload_windows_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'reloads a windows_service when specifying the identity attribute' do
+    expect(chef_run).to reload_windows_service('identity_attribute')
+  end
+end
diff --git a/examples/windows_service/spec/restart_spec.rb b/examples/windows_service/spec/restart_spec.rb
new file mode 100644
index 0000000..4f6bcc4
--- /dev/null
+++ b/examples/windows_service/spec/restart_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'windows_service::restart' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'restarts a windows_service with an explicit action' do
+    expect(chef_run).to restart_windows_service('explicit_action')
+    expect(chef_run).to_not restart_windows_service('not_explicit_action')
+  end
+
+  it 'restarts a windows_service with attributes' do
+    expect(chef_run).to restart_windows_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not restart_windows_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'restarts a windows_service when specifying the identity attribute' do
+    expect(chef_run).to restart_windows_service('identity_attribute')
+  end
+end
diff --git a/examples/windows_service/spec/start_spec.rb b/examples/windows_service/spec/start_spec.rb
new file mode 100644
index 0000000..8bf1822
--- /dev/null
+++ b/examples/windows_service/spec/start_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'windows_service::start' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'starts a windows_service with an explicit action' do
+    expect(chef_run).to start_windows_service('explicit_action')
+    expect(chef_run).to_not start_windows_service('not_explicit_action')
+  end
+
+  it 'starts a windows_service with attributes' do
+    expect(chef_run).to start_windows_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not start_windows_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'starts a windows_service when specifying the identity attribute' do
+    expect(chef_run).to start_windows_service('identity_attribute')
+  end
+end
diff --git a/examples/windows_service/spec/stop_spec.rb b/examples/windows_service/spec/stop_spec.rb
new file mode 100644
index 0000000..27f601d
--- /dev/null
+++ b/examples/windows_service/spec/stop_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'windows_service::stop' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'stops a windows_service with an explicit action' do
+    expect(chef_run).to stop_windows_service('explicit_action')
+    expect(chef_run).to_not stop_windows_service('not_explicit_action')
+  end
+
+  it 'stops a windows_service with attributes' do
+    expect(chef_run).to stop_windows_service('with_attributes').with(pattern: 'pattern')
+    expect(chef_run).to_not stop_windows_service('with_attributes').with(pattern: 'bacon')
+  end
+
+  it 'stops a windows_service when specifying the identity attribute' do
+    expect(chef_run).to stop_windows_service('identity_attribute')
+  end
+end
diff --git a/examples/yum_package/recipes/install.rb b/examples/yum_package/recipes/install.rb
new file mode 100644
index 0000000..4609b7e
--- /dev/null
+++ b/examples/yum_package/recipes/install.rb
@@ -0,0 +1,13 @@
+yum_package 'default_action'
+
+yum_package 'explicit_action' do
+  action :install
+end
+
+yum_package 'with_attributes' do
+  version '1.0.0'
+end
+
+yum_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+end
diff --git a/examples/yum_package/recipes/purge.rb b/examples/yum_package/recipes/purge.rb
new file mode 100644
index 0000000..8319e8b
--- /dev/null
+++ b/examples/yum_package/recipes/purge.rb
@@ -0,0 +1,13 @@
+yum_package 'explicit_action' do
+  action :purge
+end
+
+yum_package 'with_attributes' do
+  version '1.0.0'
+  action  :purge
+end
+
+yum_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :purge
+end
diff --git a/examples/yum_package/recipes/remove.rb b/examples/yum_package/recipes/remove.rb
new file mode 100644
index 0000000..7484c94
--- /dev/null
+++ b/examples/yum_package/recipes/remove.rb
@@ -0,0 +1,13 @@
+yum_package 'explicit_action' do
+  action :remove
+end
+
+yum_package 'with_attributes' do
+  version '1.0.0'
+  action  :remove
+end
+
+yum_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :remove
+end
diff --git a/examples/yum_package/recipes/upgrade.rb b/examples/yum_package/recipes/upgrade.rb
new file mode 100644
index 0000000..b7f274c
--- /dev/null
+++ b/examples/yum_package/recipes/upgrade.rb
@@ -0,0 +1,13 @@
+yum_package 'explicit_action' do
+  action :upgrade
+end
+
+yum_package 'with_attributes' do
+  version '1.0.0'
+  action  :upgrade
+end
+
+yum_package 'specifying the identity attribute' do
+  package_name 'identity_attribute'
+  action       :upgrade
+end
diff --git a/examples/yum_package/spec/install_spec.rb b/examples/yum_package/spec/install_spec.rb
new file mode 100644
index 0000000..efe361a
--- /dev/null
+++ b/examples/yum_package/spec/install_spec.rb
@@ -0,0 +1,23 @@
+require 'chefspec'
+
+describe 'yum_package::install' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'installs a yum_package with the default action' do
+    expect(chef_run).to install_yum_package('default_action')
+    expect(chef_run).to_not install_yum_package('not_default_action')
+  end
+
+  it 'installs a yum_package with an explicit action' do
+    expect(chef_run).to install_yum_package('explicit_action')
+  end
+
+  it 'installs a yum_package with attributes' do
+    expect(chef_run).to install_yum_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not install_yum_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'installs a yum_package when specifying the identity attribute' do
+    expect(chef_run).to install_yum_package('identity_attribute')
+  end
+end
diff --git a/examples/yum_package/spec/purge_spec.rb b/examples/yum_package/spec/purge_spec.rb
new file mode 100644
index 0000000..f1b3c28
--- /dev/null
+++ b/examples/yum_package/spec/purge_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'yum_package::purge' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'purges a yum_package with an explicit action' do
+    expect(chef_run).to purge_yum_package('explicit_action')
+    expect(chef_run).to_not purge_yum_package('not_explicit_action')
+  end
+
+  it 'purges a yum_package with attributes' do
+    expect(chef_run).to purge_yum_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not purge_yum_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'purges a yum_package when specifying the identity attribute' do
+    expect(chef_run).to purge_yum_package('identity_attribute')
+  end
+end
diff --git a/examples/yum_package/spec/remove_spec.rb b/examples/yum_package/spec/remove_spec.rb
new file mode 100644
index 0000000..39aa435
--- /dev/null
+++ b/examples/yum_package/spec/remove_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'yum_package::remove' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'removes a yum_package with an explicit action' do
+    expect(chef_run).to remove_yum_package('explicit_action')
+    expect(chef_run).to_not remove_yum_package('not_explicit_action')
+  end
+
+  it 'removes a yum_package with attributes' do
+    expect(chef_run).to remove_yum_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not remove_yum_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'removes a yum_package when specifying the identity attribute' do
+    expect(chef_run).to remove_yum_package('identity_attribute')
+  end
+end
diff --git a/examples/yum_package/spec/upgrade_spec.rb b/examples/yum_package/spec/upgrade_spec.rb
new file mode 100644
index 0000000..4ca9cb2
--- /dev/null
+++ b/examples/yum_package/spec/upgrade_spec.rb
@@ -0,0 +1,19 @@
+require 'chefspec'
+
+describe 'yum_package::upgrade' do
+  let(:chef_run) { ChefSpec::SoloRunner.converge(described_recipe) }
+
+  it 'upgrades a yum_package with an explicit action' do
+    expect(chef_run).to upgrade_yum_package('explicit_action')
+    expect(chef_run).to_not upgrade_yum_package('not_explicit_action')
+  end
+
+  it 'upgrades a yum_package with attributes' do
+    expect(chef_run).to upgrade_yum_package('with_attributes').with(version: '1.0.0')
+    expect(chef_run).to_not upgrade_yum_package('with_attributes').with(version: '1.2.3')
+  end
+
+  it 'upgrades a yum_package when specifying the identity attribute' do
+    expect(chef_run).to upgrade_yum_package('identity_attribute')
+  end
+end
diff --git a/features/apt_package.feature b/features/apt_package.feature
new file mode 100644
index 0000000..bec8a50
--- /dev/null
+++ b/features/apt_package.feature
@@ -0,0 +1,14 @@
+Feature: The apt_package matcher
+  Background:
+    * I am using the "apt_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | reconfig |
+    | remove   |
+    | upgrade  |
diff --git a/features/attributes.feature b/features/attributes.feature
new file mode 100644
index 0000000..0951da9
--- /dev/null
+++ b/features/attributes.feature
@@ -0,0 +1,7 @@
+Feature: Overridding attributes in the Runner
+  Background:
+    * I am using the "attributes" cookbook
+
+  Scenario: Running the specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/batch.feature b/features/batch.feature
new file mode 100644
index 0000000..3880688
--- /dev/null
+++ b/features/batch.feature
@@ -0,0 +1,10 @@
+Feature: The batch matcher
+  Background:
+    * I am using the "batch" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | run     |
diff --git a/features/cached.feature b/features/cached.feature
new file mode 100644
index 0000000..4b9a621
--- /dev/null
+++ b/features/cached.feature
@@ -0,0 +1,7 @@
+Feature: The cached runner
+  Background:
+    * I am using the "cached" cookbook
+
+  Scenario: Running specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/chef_gem.feature b/features/chef_gem.feature
new file mode 100644
index 0000000..4deb9c7
--- /dev/null
+++ b/features/chef_gem.feature
@@ -0,0 +1,14 @@
+Feature: The chef_gem matcher
+  Background:
+    * I am using the "chef_gem" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | reconfig |
+    | remove   |
+    | upgrade  |
diff --git a/features/compile_time.feature b/features/compile_time.feature
new file mode 100644
index 0000000..6182aae
--- /dev/null
+++ b/features/compile_time.feature
@@ -0,0 +1,7 @@
+Feature: Overridding compile_time in the Runner
+  Background:
+    * I am using the "compile_time" cookbook
+
+  Scenario: Running the specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/cookbook_file.feature b/features/cookbook_file.feature
new file mode 100644
index 0000000..5e83153
--- /dev/null
+++ b/features/cookbook_file.feature
@@ -0,0 +1,13 @@
+Feature: The cookbook_file matcher
+  Background:
+    * I am using the "cookbook_file" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher           |
+    | create            |
+    | create_if_missing |
+    | delete            |
+    | touch             |
diff --git a/features/cron.feature b/features/cron.feature
new file mode 100644
index 0000000..ec8d791
--- /dev/null
+++ b/features/cron.feature
@@ -0,0 +1,11 @@
+Feature: The cron matcher
+  Background:
+    * I am using the "cron" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | create  |
+    | delete  |
diff --git a/features/custom_matcher.feature b/features/custom_matcher.feature
new file mode 100644
index 0000000..828d21d
--- /dev/null
+++ b/features/custom_matcher.feature
@@ -0,0 +1,11 @@
+Feature: The custom_matcher matcher
+  Background:
+    * I am using the "custom_matcher" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | install |
+    | remove  |
diff --git a/features/deploy.feature b/features/deploy.feature
new file mode 100644
index 0000000..d8fcb3e
--- /dev/null
+++ b/features/deploy.feature
@@ -0,0 +1,12 @@
+Feature: The deploy matcher
+  Background:
+    * I am using the "deploy" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher      |
+    | deploy       |
+    | force_deploy |
+    | rollback     |
diff --git a/features/directory.feature b/features/directory.feature
new file mode 100644
index 0000000..1eaf862
--- /dev/null
+++ b/features/directory.feature
@@ -0,0 +1,11 @@
+Feature: The directory matcher
+  Background:
+    * I am using the "directory" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | create  |
+    | delete  |
diff --git a/features/do_nothing.feature b/features/do_nothing.feature
new file mode 100644
index 0000000..7a47639
--- /dev/null
+++ b/features/do_nothing.feature
@@ -0,0 +1,10 @@
+Feature: The do_nothing matcher
+  Background:
+    * I am using the "do_nothing" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | default |
diff --git a/features/dpkg_package.feature b/features/dpkg_package.feature
new file mode 100644
index 0000000..dbcf290
--- /dev/null
+++ b/features/dpkg_package.feature
@@ -0,0 +1,12 @@
+Feature: The dpkg_package matcher
+  Background:
+    * I am using the "dpkg_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | remove   |
diff --git a/features/easy_install_package.feature b/features/easy_install_package.feature
new file mode 100644
index 0000000..e701271
--- /dev/null
+++ b/features/easy_install_package.feature
@@ -0,0 +1,13 @@
+Feature: The easy_install_package matcher
+  Background:
+    * I am using the "easy_install_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | remove   |
+    | upgrade  |
diff --git a/features/env.feature b/features/env.feature
new file mode 100644
index 0000000..3fb8a6d
--- /dev/null
+++ b/features/env.feature
@@ -0,0 +1,12 @@
+Feature: The env matcher
+  Background:
+    * I am using the "env" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | create  |
+    | delete  |
+    | modify  |
diff --git a/features/erl_call.feature b/features/erl_call.feature
new file mode 100644
index 0000000..2c030c6
--- /dev/null
+++ b/features/erl_call.feature
@@ -0,0 +1,10 @@
+Feature: The erl_call matcher
+  Background:
+    * I am using the "erl_call" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | run     |
diff --git a/features/execute.feature b/features/execute.feature
new file mode 100644
index 0000000..6249947
--- /dev/null
+++ b/features/execute.feature
@@ -0,0 +1,10 @@
+Feature: The execute matcher
+  Background:
+    * I am using the "execute" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | run     |
diff --git a/features/expect_exception.feature b/features/expect_exception.feature
new file mode 100644
index 0000000..374c41f
--- /dev/null
+++ b/features/expect_exception.feature
@@ -0,0 +1,12 @@
+Feature: The expect_exception matcher
+  Background:
+    * I am using the "expect_exception" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher        |
+    | compile_error  |
+    | converge_error |
+    | no_error       |
diff --git a/features/file.feature b/features/file.feature
new file mode 100644
index 0000000..e64ecea
--- /dev/null
+++ b/features/file.feature
@@ -0,0 +1,13 @@
+Feature: The file matcher
+  Background:
+    * I am using the "file" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher           |
+    | create            |
+    | create_if_missing |
+    | delete            |
+    | touch             |
diff --git a/features/freebsd_package.feature b/features/freebsd_package.feature
new file mode 100644
index 0000000..aa4f302
--- /dev/null
+++ b/features/freebsd_package.feature
@@ -0,0 +1,11 @@
+Feature: The freebsd_package matcher
+  Background:
+    * I am using the "freebsd_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | remove   |
diff --git a/features/gem_package.feature b/features/gem_package.feature
new file mode 100644
index 0000000..5069814
--- /dev/null
+++ b/features/gem_package.feature
@@ -0,0 +1,14 @@
+Feature: The gem_package matcher
+  Background:
+    * I am using the "gem_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | reconfig |
+    | remove   |
+    | upgrade  |
diff --git a/features/git.feature b/features/git.feature
new file mode 100644
index 0000000..dce4655
--- /dev/null
+++ b/features/git.feature
@@ -0,0 +1,12 @@
+Feature: The git matcher
+  Background:
+    * I am using the "git" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | checkout |
+    | export   |
+    | sync     |
diff --git a/features/group.feature b/features/group.feature
new file mode 100644
index 0000000..659a40c
--- /dev/null
+++ b/features/group.feature
@@ -0,0 +1,13 @@
+Feature: The group matcher
+  Background:
+    * I am using the "group" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | create  |
+    | manage  |
+    | modify  |
+    | remove  |
diff --git a/features/guards.feature b/features/guards.feature
new file mode 100644
index 0000000..2338a0c
--- /dev/null
+++ b/features/guards.feature
@@ -0,0 +1,10 @@
+Feature: The guards matcher
+  Background:
+    * I am using the "guards" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher     |
+    | default     |
diff --git a/features/heavy_provider_light_resource.feature b/features/heavy_provider_light_resource.feature
new file mode 100644
index 0000000..9908331
--- /dev/null
+++ b/features/heavy_provider_light_resource.feature
@@ -0,0 +1,10 @@
+Feature: A cookbook with a heavy provider and light weight resource
+  Background:
+    * I am using the "heavy_provider_light_resource" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher          |
+    | provider_service |
diff --git a/features/http_request.feature b/features/http_request.feature
new file mode 100644
index 0000000..b106b76
--- /dev/null
+++ b/features/http_request.feature
@@ -0,0 +1,15 @@
+Feature: The http_request matcher
+  Background:
+    * I am using the "http_request" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | delete  |
+    | head    |
+    | get     |
+    | options |
+    | post    |
+    | put     |
diff --git a/features/ifconfig.feature b/features/ifconfig.feature
new file mode 100644
index 0000000..5b44e45
--- /dev/null
+++ b/features/ifconfig.feature
@@ -0,0 +1,13 @@
+Feature: The ifconfig matcher
+  Background:
+    * I am using the "ifconfig" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | add     |
+    | delete  |
+    | disable |
+    | enable  |
diff --git a/features/include_recipe.feature b/features/include_recipe.feature
new file mode 100644
index 0000000..abbb5df
--- /dev/null
+++ b/features/include_recipe.feature
@@ -0,0 +1,10 @@
+Feature: The include_recipe matcher
+  Background:
+    * I am using the "include_recipe" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | default |
diff --git a/features/ips_package.feature b/features/ips_package.feature
new file mode 100644
index 0000000..55fe7fb
--- /dev/null
+++ b/features/ips_package.feature
@@ -0,0 +1,12 @@
+Feature: The ips_package matcher
+  Background:
+    * I am using the "ips_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | remove   |
+    | upgrade  |
diff --git a/features/link.feature b/features/link.feature
new file mode 100644
index 0000000..2b8da05
--- /dev/null
+++ b/features/link.feature
@@ -0,0 +1,12 @@
+Feature: The link matcher
+  Background:
+    * I am using the "link" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | create  |
+    | delete  |
+    | link_to |
diff --git a/features/log.feature b/features/log.feature
new file mode 100644
index 0000000..6fed0f6
--- /dev/null
+++ b/features/log.feature
@@ -0,0 +1,10 @@
+Feature: The log matcher
+  Background:
+    * I am using the "log" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | write   |
diff --git a/features/macports_package.feature b/features/macports_package.feature
new file mode 100644
index 0000000..d861fd3
--- /dev/null
+++ b/features/macports_package.feature
@@ -0,0 +1,13 @@
+Feature: The macports_package matcher
+  Background:
+    * I am using the "macports_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | remove   |
+    | upgrade  |
diff --git a/features/mdadm.feature b/features/mdadm.feature
new file mode 100644
index 0000000..63d967d
--- /dev/null
+++ b/features/mdadm.feature
@@ -0,0 +1,12 @@
+Feature: The mdadm matcher
+  Background:
+    * I am using the "mdadm" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | create   |
+    | assemble |
+    | stop     |
diff --git a/features/mount.feature b/features/mount.feature
new file mode 100644
index 0000000..4f63f2f
--- /dev/null
+++ b/features/mount.feature
@@ -0,0 +1,14 @@
+Feature: The mount matcher
+  Background:
+    * I am using the "mount" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | disable |
+    | enable  |
+    | mount   |
+    | remount |
+    | umount  |
diff --git a/features/multiple_actions.feature b/features/multiple_actions.feature
new file mode 100644
index 0000000..eaf6a93
--- /dev/null
+++ b/features/multiple_actions.feature
@@ -0,0 +1,11 @@
+Feature: The multiple_actions matcher
+  Background:
+    * I am using the "multiple_actions" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher    |
+    | default    |
+    | sequential |
diff --git a/features/multiple_run_action.feature b/features/multiple_run_action.feature
new file mode 100644
index 0000000..13a2a21
--- /dev/null
+++ b/features/multiple_run_action.feature
@@ -0,0 +1,7 @@
+Feature: The multiple_run_action matcher
+  Background:
+    * I am using the "multiple_run_action" cookbook
+
+  Scenario: Running specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/notifications.feature b/features/notifications.feature
new file mode 100644
index 0000000..4c8ecb2
--- /dev/null
+++ b/features/notifications.feature
@@ -0,0 +1,13 @@
+Feature: The notifications matcher
+  Background:
+    * I am using the "notifications" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher     |
+    | chained     |
+    | default     |
+    | delayed     |
+    | immediately |
diff --git a/features/ohai.feature b/features/ohai.feature
new file mode 100644
index 0000000..92c5864
--- /dev/null
+++ b/features/ohai.feature
@@ -0,0 +1,10 @@
+Feature: The ohai matcher
+  Background:
+    * I am using the "ohai" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | reload  |
diff --git a/features/package.feature b/features/package.feature
new file mode 100644
index 0000000..13166c5
--- /dev/null
+++ b/features/package.feature
@@ -0,0 +1,14 @@
+Feature: The package matcher
+  Background:
+    * I am using the "package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | reconfig |
+    | remove   |
+    | upgrade  |
diff --git a/features/pacman_package.feature b/features/pacman_package.feature
new file mode 100644
index 0000000..bc606d8
--- /dev/null
+++ b/features/pacman_package.feature
@@ -0,0 +1,13 @@
+Feature: The pacman_package matcher
+  Background:
+    * I am using the "pacman_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | remove   |
+    | upgrade  |
diff --git a/features/portage_package.feature b/features/portage_package.feature
new file mode 100644
index 0000000..c494212
--- /dev/null
+++ b/features/portage_package.feature
@@ -0,0 +1,13 @@
+Feature: The portage_package matcher
+  Background:
+    * I am using the "portage_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | remove   |
+    | upgrade  |
diff --git a/features/powershell_script.feature b/features/powershell_script.feature
new file mode 100644
index 0000000..d3995be
--- /dev/null
+++ b/features/powershell_script.feature
@@ -0,0 +1,10 @@
+Feature: The powershell_script matcher
+  Background:
+    * I am using the "powershell_script" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | run     |
diff --git a/features/reboot.feature b/features/reboot.feature
new file mode 100644
index 0000000..90bd857
--- /dev/null
+++ b/features/reboot.feature
@@ -0,0 +1,19 @@
+ at not_chef_11_14_2
+ at not_chef_11_14_6
+ at not_chef_11_16_0
+ at not_chef_11_16_2
+ at not_chef_11_16_4
+ at not_chef_11_18_0
+ at not_chef_11_18_6
+Feature: The reboot matcher
+  Background:
+    * I am using the "reboot" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | request |
+    | now     |
+    | cancel  |
diff --git a/features/registry_key.feature b/features/registry_key.feature
new file mode 100644
index 0000000..58e41bf
--- /dev/null
+++ b/features/registry_key.feature
@@ -0,0 +1,13 @@
+Feature: The registry_key matcher
+  Background:
+    * I am using the "registry_key" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher            |
+    | create             |
+    | create_if_missing  |
+    | delete             |
+    | delete_key         |
diff --git a/features/remote_directory.feature b/features/remote_directory.feature
new file mode 100644
index 0000000..b53e6d5
--- /dev/null
+++ b/features/remote_directory.feature
@@ -0,0 +1,12 @@
+Feature: The remote_directory matcher
+  Background:
+    * I am using the "remote_directory" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher           |
+    | create            |
+    | create_if_missing |
+    | delete            |
diff --git a/features/remote_file.feature b/features/remote_file.feature
new file mode 100644
index 0000000..e2db899
--- /dev/null
+++ b/features/remote_file.feature
@@ -0,0 +1,13 @@
+Feature: The remote_file matcher
+  Background:
+    * I am using the "remote_file" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher           |
+    | create            |
+    | create_if_missing |
+    | delete            |
+    | touch             |
diff --git a/features/render_file.feature b/features/render_file.feature
new file mode 100644
index 0000000..24b0744
--- /dev/null
+++ b/features/render_file.feature
@@ -0,0 +1,11 @@
+Feature: The render_file matcher
+  Background:
+    * I am using the "render_file" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher          |
+    | default          |
+    | template_helpers |
diff --git a/features/roles.feature b/features/roles.feature
new file mode 100644
index 0000000..7ed64ff
--- /dev/null
+++ b/features/roles.feature
@@ -0,0 +1,7 @@
+Feature: Overridding roles in the Runner
+  Background:
+    * I am using the "roles" cookbook
+
+  Scenario: Running the specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/route.feature b/features/route.feature
new file mode 100644
index 0000000..f05dbba
--- /dev/null
+++ b/features/route.feature
@@ -0,0 +1,11 @@
+Feature: The route matcher
+  Background:
+    * I am using the "route" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | add     |
+    | delete  |
diff --git a/features/rpm_package.feature b/features/rpm_package.feature
new file mode 100644
index 0000000..5ff1976
--- /dev/null
+++ b/features/rpm_package.feature
@@ -0,0 +1,12 @@
+Feature: The rpm_package matcher
+  Background:
+    * I am using the "rpm_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | remove   |
+    | upgrade  |
diff --git a/features/ruby_block.feature b/features/ruby_block.feature
new file mode 100644
index 0000000..84b10e6
--- /dev/null
+++ b/features/ruby_block.feature
@@ -0,0 +1,10 @@
+Feature: The ruby_block matcher
+  Background:
+    * I am using the "ruby_block" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | run     |
diff --git a/features/script.feature b/features/script.feature
new file mode 100644
index 0000000..8ff7456
--- /dev/null
+++ b/features/script.feature
@@ -0,0 +1,15 @@
+Feature: The script matcher
+  Background:
+    * I am using the "script" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher    |
+    | run_bash   |
+    | run_csh    |
+    | run_perl   |
+    | run_python |
+    | run_ruby   |
+    | run_script |
diff --git a/features/server.feature b/features/server.feature
new file mode 100644
index 0000000..cf0f440
--- /dev/null
+++ b/features/server.feature
@@ -0,0 +1,16 @@
+Feature: The ChefSpec server
+  Background:
+    * I am using the "server" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Component>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Component          |
+    | client             |
+    | data_bag           |
+    | environment        |
+    | node               |
+    | render_with_cached |
+    | role               |
+    | search             |
diff --git a/features/service.feature b/features/service.feature
new file mode 100644
index 0000000..d9b090c
--- /dev/null
+++ b/features/service.feature
@@ -0,0 +1,15 @@
+Feature: The service matcher
+  Background:
+    * I am using the "service" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | disable |
+    | enable  |
+    | reload  |
+    | restart |
+    | start   |
+    | stop    |
diff --git a/features/smartos_package.feature b/features/smartos_package.feature
new file mode 100644
index 0000000..21f818e
--- /dev/null
+++ b/features/smartos_package.feature
@@ -0,0 +1,12 @@
+Feature: The smartos_package matcher
+  Background:
+    * I am using the "smartos_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | remove   |
+    | upgrade  |
diff --git a/features/solaris_package.feature b/features/solaris_package.feature
new file mode 100644
index 0000000..7fd266c
--- /dev/null
+++ b/features/solaris_package.feature
@@ -0,0 +1,11 @@
+Feature: The solaris_package matcher
+  Background:
+    * I am using the "solaris_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | remove   |
diff --git a/features/state_attrs.feature b/features/state_attrs.feature
new file mode 100644
index 0000000..c07b36b
--- /dev/null
+++ b/features/state_attrs.feature
@@ -0,0 +1,7 @@
+Feature: Testing state attributes
+  Background:
+    * I am using the "state_attrs" cookbook
+
+  Scenario: Running the specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/step_definitions/background_steps.rb b/features/step_definitions/background_steps.rb
new file mode 100644
index 0000000..9cee138
--- /dev/null
+++ b/features/step_definitions/background_steps.rb
@@ -0,0 +1,4 @@
+Given /^I am using the "(.+)" cookbook$/ do |cookbook|
+  FileUtils.cp_r(File.join('examples', cookbook), current_dir)
+  cd(cookbook)
+end
diff --git a/features/step_into.feature b/features/step_into.feature
new file mode 100644
index 0000000..812d6f6
--- /dev/null
+++ b/features/step_into.feature
@@ -0,0 +1,7 @@
+Feature: The step_into matcher
+  Background:
+    * I am using the "step_into" cookbook
+
+  Scenario:
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/stub_command.feature b/features/stub_command.feature
new file mode 100644
index 0000000..d7bb765
--- /dev/null
+++ b/features/stub_command.feature
@@ -0,0 +1,7 @@
+Feature: The stub_command matcher
+  Background:
+    * I am using the "stub_command" cookbook
+
+  Scenario: Running specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/stub_data_bag.feature b/features/stub_data_bag.feature
new file mode 100644
index 0000000..c451100
--- /dev/null
+++ b/features/stub_data_bag.feature
@@ -0,0 +1,7 @@
+Feature: The stub_data_bag matcher
+  Background:
+    * I am using the "stub_data_bag" cookbook
+
+  Scenario: Running specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/stub_data_bag_item.feature b/features/stub_data_bag_item.feature
new file mode 100644
index 0000000..a3e7429
--- /dev/null
+++ b/features/stub_data_bag_item.feature
@@ -0,0 +1,7 @@
+Feature: The stub_data_bag_item matcher
+  Background:
+    * I am using the "stub_data_bag_item" cookbook
+
+  Scenario: Running specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/stub_node.feature b/features/stub_node.feature
new file mode 100644
index 0000000..19987b6
--- /dev/null
+++ b/features/stub_node.feature
@@ -0,0 +1,7 @@
+Feature: The stub_node matcher
+  Background:
+    * I am using the "stub_node" cookbook
+
+  Scenario: Running specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/stub_search.feature b/features/stub_search.feature
new file mode 100644
index 0000000..7a021bb
--- /dev/null
+++ b/features/stub_search.feature
@@ -0,0 +1,7 @@
+Feature: The stub_search matcher
+  Background:
+    * I am using the "stub_search" cookbook
+
+  Scenario: Running specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/subscribes.feature b/features/subscribes.feature
new file mode 100644
index 0000000..7208068
--- /dev/null
+++ b/features/subscribes.feature
@@ -0,0 +1,13 @@
+Feature: The subscribes matcher
+  Background:
+    * I am using the "subscribes" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher     |
+    | chained     |
+    | default     |
+    | delayed     |
+    | immediately |
diff --git a/features/subversion.feature b/features/subversion.feature
new file mode 100644
index 0000000..539caef
--- /dev/null
+++ b/features/subversion.feature
@@ -0,0 +1,13 @@
+Feature: The subversion matcher
+  Background:
+    * I am using the "subversion" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher      |
+    | checkout     |
+    | export       |
+    | force_export |
+    | sync         |
diff --git a/features/support/env.rb b/features/support/env.rb
new file mode 100644
index 0000000..115c714
--- /dev/null
+++ b/features/support/env.rb
@@ -0,0 +1,29 @@
+require 'aruba'
+require 'aruba/cucumber'
+require 'aruba/in_process'
+
+require 'rspec'
+require 'chefspec'
+
+require_relative 'executor'
+
+Before do
+  FileUtils.mkdir_p(current_dir)
+  FileUtils.cp_r('examples', current_dir)
+
+  # Use InProcess testing by default
+  Aruba::InProcess.main_class = ChefSpec::Executor
+  Aruba.process = Aruba::InProcess
+
+  # Need to reload this on each run because RSpec.reset (called by the
+  # RSpec::Runner) removes our configurations :(
+  load 'lib/chefspec/rspec.rb'
+
+  # Use a temporary directory to suppress Travis warnings
+  @dirs = [Dir.mktmpdir]
+end
+
+After do
+  # Cleanup the test files
+  FileUtils.rm_rf(@dirs.first)
+end
diff --git a/features/support/executor.rb b/features/support/executor.rb
new file mode 100644
index 0000000..3f3770a
--- /dev/null
+++ b/features/support/executor.rb
@@ -0,0 +1,20 @@
+require 'rspec/core'
+
+module ChefSpec
+  #
+  # @private
+  # A wrapper class around RSpec for running Aruba::InProcess
+  #
+  class Executor
+    def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
+      @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
+    end
+
+    def execute!
+      exitstatus = RSpec::Core::Runner.run(@argv, @stderr, @stdout)
+      @kernel.exit(exitstatus)
+    ensure
+      RSpec.reset
+    end
+  end
+end
diff --git a/features/template.feature b/features/template.feature
new file mode 100644
index 0000000..6c28455
--- /dev/null
+++ b/features/template.feature
@@ -0,0 +1,13 @@
+Feature: The template matcher
+  Background:
+    * I am using the "template" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher            |
+    | create             |
+    | create_if_missing  |
+    | delete             |
+    | touch              |
diff --git a/features/use_inline_resources.feature b/features/use_inline_resources.feature
new file mode 100644
index 0000000..c3db974
--- /dev/null
+++ b/features/use_inline_resources.feature
@@ -0,0 +1,7 @@
+Feature: Testing an LWRP with inline resources
+  Background:
+    * I am using the "use_inline_resources" cookbook
+
+  Scenario: Running the specs
+    * I successfully run `rspec spec/default_spec.rb`
+    * the output should contain "0 failures"
diff --git a/features/user.feature b/features/user.feature
new file mode 100644
index 0000000..9867bfc
--- /dev/null
+++ b/features/user.feature
@@ -0,0 +1,15 @@
+Feature: The user matcher
+  Background:
+    * I am using the "user" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | create  |
+    | remove  |
+    | modify  |
+    | manage  |
+    | lock    |
+    | unlock  |
diff --git a/features/windows_service.feature b/features/windows_service.feature
new file mode 100644
index 0000000..a65e745
--- /dev/null
+++ b/features/windows_service.feature
@@ -0,0 +1,23 @@
+ at not_chef_11_14_2
+ at not_chef_11_14_6
+ at not_chef_11_16_0
+ at not_chef_11_16_2
+ at not_chef_11_16_4
+ at not_chef_11_18_0
+ at not_chef_11_18_6
+Feature: The windows_service matcher
+  Background:
+    * I am using the "windows_service" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher |
+    | configure_startup |
+    | disable |
+    | enable  |
+    | reload  |
+    | restart |
+    | start   |
+    | stop    |
diff --git a/features/yum_package.feature b/features/yum_package.feature
new file mode 100644
index 0000000..dbf01fa
--- /dev/null
+++ b/features/yum_package.feature
@@ -0,0 +1,13 @@
+Feature: The yum_package matcher
+  Background:
+    * I am using the "yum_package" cookbook
+
+  Scenario Outline: Running specs
+    * I successfully run `rspec spec/<Matcher>_spec.rb`
+    * the output should contain "0 failures"
+  Examples:
+    | Matcher  |
+    | install  |
+    | purge    |
+    | remove   |
+    | upgrade  |
diff --git a/gemfiles/chefspec.gemfile b/gemfiles/chefspec.gemfile
new file mode 100644
index 0000000..cb1cb51
--- /dev/null
+++ b/gemfiles/chefspec.gemfile
@@ -0,0 +1,9 @@
+source 'https://rubygems.org'
+
+if ENV['CHEF_VERSION'] == 'master'
+  gem 'chef', github: 'chef/chef'
+else
+  gem 'chef', ENV['CHEF_VERSION']
+end
+
+gemspec path: '..'
diff --git a/lib/chefspec.rb b/lib/chefspec.rb
new file mode 100644
index 0000000..46d8a25
--- /dev/null
+++ b/lib/chefspec.rb
@@ -0,0 +1,83 @@
+require 'rspec'
+
+module ChefSpec
+  #
+  # Defines a new runner method on the Chef runner.
+  #
+  # @param [Symbol] resource_name
+  #   the name of the resource to define a method
+  #
+  # @return [self]
+  #
+  def define_matcher(resource_name)
+    matchers[resource_name.to_sym] = Proc.new do |identity|
+      find_resource(resource_name, identity)
+    end
+
+    self
+  end
+  module_function :define_matcher
+
+  #
+  # The source root of the ChefSpec gem. This is useful when requiring files
+  # that are relative to the root of the project.
+  #
+  # @return [Pathname]
+  #
+  def root
+    @root ||= Pathname.new(File.expand_path('../../', __FILE__))
+  end
+  module_function :root
+
+  protected
+
+  #
+  # The list of custom defined matchers.
+  #
+  # @return [Hash<String, Proc>]
+  #
+  def matchers
+    @matchers ||= {}
+  end
+  module_function :matchers
+end
+
+require_relative 'chefspec/extensions/chef/securable'
+require_relative 'chefspec/extensions/chef/client'
+require_relative 'chefspec/extensions/chef/conditional'
+require_relative 'chefspec/extensions/chef/cookbook_uploader'
+require_relative 'chefspec/extensions/chef/data_query'
+require_relative 'chefspec/extensions/chef/lwrp_base'
+require_relative 'chefspec/extensions/chef/resource'
+require_relative 'chefspec/extensions/chef/resource/freebsd_package'
+
+require_relative 'chefspec/mixins/normalize'
+
+require_relative 'chefspec/stubs/command_registry'
+require_relative 'chefspec/stubs/command_stub'
+require_relative 'chefspec/stubs/data_bag_item_registry'
+require_relative 'chefspec/stubs/data_bag_item_stub'
+require_relative 'chefspec/stubs/data_bag_registry'
+require_relative 'chefspec/stubs/data_bag_stub'
+require_relative 'chefspec/stubs/registry'
+require_relative 'chefspec/stubs/stub'
+require_relative 'chefspec/stubs/search_registry'
+require_relative 'chefspec/stubs/search_stub'
+
+require_relative 'chefspec/api'
+require_relative 'chefspec/cacher'
+require_relative 'chefspec/coverage'
+require_relative 'chefspec/errors'
+require_relative 'chefspec/expect_exception'
+require_relative 'chefspec/formatter'
+require_relative 'chefspec/macros'
+require_relative 'chefspec/matchers'
+require_relative 'chefspec/renderer'
+require_relative 'chefspec/rspec'
+require_relative 'chefspec/server_runner'
+require_relative 'chefspec/solo_runner'
+require_relative 'chefspec/util'
+require_relative 'chefspec/version'
+
+require_relative 'chefspec/deprecations'
+require_relative 'chefspec/chef_backwards_compat' if Chef::VERSION.to_f < 12.0
diff --git a/lib/chefspec/api.rb b/lib/chefspec/api.rb
new file mode 100644
index 0000000..98b4d3b
--- /dev/null
+++ b/lib/chefspec/api.rb
@@ -0,0 +1,78 @@
+module ChefSpec
+  module API
+    extend self
+
+    def self.included(base)
+      submodules.each do |child|
+        base.send(:include, child)
+      end
+    end
+
+    private
+
+    #
+    # WARNING: This is metaprogramming madness. Find all modules who are
+    # nested beneath this module.
+    #
+    # @return [Array<Module>]
+    #
+    def submodules
+      self.constants
+        .map { |name| const_get(name) }
+        .select { |const| const.class == Module }
+    end
+  end
+end
+
+require_relative 'api/apt_package'
+require_relative 'api/batch'
+require_relative 'api/chef_gem'
+require_relative 'api/cookbook_file'
+require_relative 'api/cron'
+require_relative 'api/deploy'
+require_relative 'api/directory'
+require_relative 'api/dpkg_package'
+require_relative 'api/do_nothing'
+require_relative 'api/easy_install_package'
+require_relative 'api/env'
+require_relative 'api/erl_call'
+require_relative 'api/execute'
+require_relative 'api/file'
+require_relative 'api/freebsd_package'
+require_relative 'api/gem_package'
+require_relative 'api/git'
+require_relative 'api/group'
+require_relative 'api/http_request'
+require_relative 'api/ifconfig'
+require_relative 'api/include_recipe'
+require_relative 'api/ips_package'
+require_relative 'api/link'
+require_relative 'api/log'
+require_relative 'api/macports_package'
+require_relative 'api/mdadm'
+require_relative 'api/mount'
+require_relative 'api/notifications'
+require_relative 'api/ohai'
+require_relative 'api/package'
+require_relative 'api/pacman_package'
+require_relative 'api/portage_package'
+require_relative 'api/powershell_script'
+require_relative 'api/reboot'
+require_relative 'api/registry_key'
+require_relative 'api/remote_directory'
+require_relative 'api/remote_file'
+require_relative 'api/render_file'
+require_relative 'api/route'
+require_relative 'api/rpm_package'
+require_relative 'api/ruby_block'
+require_relative 'api/script'
+require_relative 'api/service'
+require_relative 'api/smartos_package'
+require_relative 'api/solaris_package'
+require_relative 'api/state_attrs'
+require_relative 'api/subscriptions'
+require_relative 'api/subversion'
+require_relative 'api/template'
+require_relative 'api/user'
+require_relative 'api/windows_service'
+require_relative 'api/yum_package'
diff --git a/lib/chefspec/api/apt_package.rb b/lib/chefspec/api/apt_package.rb
new file mode 100644
index 0000000..4a92a74
--- /dev/null
+++ b/lib/chefspec/api/apt_package.rb
@@ -0,0 +1,192 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module AptPackageMatchers
+    ChefSpec.define_matcher :apt_package
+
+    #
+    # Assert that an +apt_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as an
+    # +apt_package+:
+    #
+    #     apt_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +apt_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +apt_package+ was installed
+    #   expect(chef_run).to install_apt_package('apache2')
+    #
+    # @example Assert that an +apt_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_apt_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +apt_package+ was installed with attributes
+    #   expect(chef_run).to install_apt_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +apt_package+ was installed using a regex
+    #   expect(chef_run).to install_apt_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +apt_package+ was _not_ installed
+    #   expect(chef_run).to_not install_apt_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_apt_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:apt_package, :install, resource_name)
+    end
+
+    #
+    # Assert that an +apt_package+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "apache2" as an
+    # +apt_package+:
+    #
+    #     apt_package 'apache2' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +apt_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +apt_package+ was purged
+    #   expect(chef_run).to purge_apt_package('apache2')
+    #
+    # @example Assert that an +apt_package+ was purged with predicate matchers
+    #   expect(chef_run).to purge_apt_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +apt_package+ was purged with attributes
+    #   expect(chef_run).to purge_apt_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +apt_package+ was purged using a regex
+    #   expect(chef_run).to purge_apt_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +apt_package+ was _not_ purged
+    #   expect(chef_run).to_not purge_apt_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_apt_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:apt_package, :purge, resource_name)
+    end
+
+    #
+    # Assert that an +apt_package+ resource exists in the Chef run with the
+    # action +:reconfig+. Given a Chef Recipe that reconfigures "apache2" as an
+    # +apt_package+:
+    #
+    #     apt_package 'apache2' do
+    #       action :reconfig
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +apt_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +apt_package+ was reconfigured
+    #   expect(chef_run).to reconfig_apt_package('apache2')
+    #
+    # @example Assert that an +apt_package+ was reconfigured with predicate matchers
+    #   expect(chef_run).to reconfig_apt_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +apt_package+ was reconfigured with attributes
+    #   expect(chef_run).to reconfig_apt_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +apt_package+ was reconfigured using a regex
+    #   expect(chef_run).to reconfig_apt_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +apt_package+ was _not_ reconfigured
+    #   expect(chef_run).to_not reconfig_apt_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def reconfig_apt_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:apt_package, :reconfig, resource_name)
+    end
+
+    #
+    # Assert that an +apt_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as an
+    # +apt_package+:
+    #
+    #     apt_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +apt_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +apt_package+ was removed
+    #   expect(chef_run).to remove_apt_package('apache2')
+    #
+    # @example Assert that an +apt_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_apt_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +apt_package+ was removed with attributes
+    #   expect(chef_run).to remove_apt_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +apt_package+ was removed using a regex
+    #   expect(chef_run).to remove_apt_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +apt_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_apt_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_apt_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:apt_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that an +apt_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "apache2" as an
+    # +apt_package+:
+    #
+    #     apt_package 'apache2' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +apt_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +apt_package+ was upgraded
+    #   expect(chef_run).to upgrade_apt_package('apache2')
+    #
+    # @example Assert that an +apt_package+ was upgraded with predicate matchers
+    #   expect(chef_run).to upgrade_apt_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +apt_package+ was upgraded with attributes
+    #   expect(chef_run).to upgrade_apt_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +apt_package+ was upgraded using a regex
+    #   expect(chef_run).to upgrade_apt_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +apt_package+ was _not_ upgraded
+    #   expect(chef_run).to_not upgrade_apt_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_apt_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:apt_package, :upgrade, resource_name)
+    end
+
+  end
+end
diff --git a/lib/chefspec/api/batch.rb b/lib/chefspec/api/batch.rb
new file mode 100644
index 0000000..6016dc2
--- /dev/null
+++ b/lib/chefspec/api/batch.rb
@@ -0,0 +1,43 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module BatchMatchers
+    ChefSpec.define_matcher :batch
+
+    #
+    # Assert that a +batch+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "unzip" using
+    # +batch+:
+    #
+    #     batch 'unzip' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +batch+ resource with ChefSpec.
+    #
+    # @example Assert that a +batch+ was run
+    #   expect(chef_run).to run_batch('unzip')
+    #
+    # @example Assert that a +batch+ was run with predicate matchers
+    #   expect(chef_run).to run_batch('unzip').with_cwd('/home')
+    #
+    # @example Assert that a +batch+ was run with attributes
+    #   expect(chef_run).to run_batch('unzip').with(cwd: '/home')
+    #
+    # @example Assert that a +batch+ was run using a regex
+    #   expect(chef_run).to run_batch('unzip').with(cwd: /\/(.+)/)
+    #
+    # @example Assert that a +batch+ was _not_ run
+    #   expect(chef_run).to_not run_batch('unzip')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_batch(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:batch, :run, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/chef_gem.rb b/lib/chefspec/api/chef_gem.rb
new file mode 100644
index 0000000..8bd3251
--- /dev/null
+++ b/lib/chefspec/api/chef_gem.rb
@@ -0,0 +1,191 @@
+module ChefSpec::API
+  # @since 0.8.0
+  module ChefGemMatchers
+    ChefSpec.define_matcher :chef_gem
+
+    #
+    # Assert that a +chef_gem+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "community-zero" as a
+    # +chef_gem+:
+    #
+    #     chef_gem 'community-zero' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +chef_gem+ resource with ChefSpec.
+    #
+    # @example Assert that a +chef_gem+ was installed
+    #   expect(chef_run).to install_chef_gem('community-zero')
+    #
+    # @example Assert that a +chef_gem+ was installed with predicate matchers
+    #   expect(chef_run).to install_chef_gem('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was installed with attributes
+    #   expect(chef_run).to install_chef_gem('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was installed using a regex
+    #   expect(chef_run).to install_chef_gem('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +chef_gem+ was _not_ installed
+    #   expect(chef_run).to_not install_chef_gem('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_chef_gem(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:chef_gem, :install, resource_name)
+    end
+
+    #
+    # Assert that a +chef_gem+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "community-zero" as a
+    # +chef_gem+:
+    #
+    #     chef_gem 'community-zero' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +chef_gem+ resource with ChefSpec.
+    #
+    # @example Assert that a +chef_gem+ was purgeed
+    #   expect(chef_run).to purge_chef_gem('community-zero')
+    #
+    # @example Assert that a +chef_gem+ was purgeed with predicate matchers
+    #   expect(chef_run).to purge_chef_gem('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was purgeed with attributes
+    #   expect(chef_run).to purge_chef_gem('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was purgeed using a regex
+    #   expect(chef_run).to purge_chef_gem('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +chef_gem+ was _not_ purgeed
+    #   expect(chef_run).to_not purge_chef_gem('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_chef_gem(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:chef_gem, :purge, resource_name)
+    end
+
+    #
+    # Assert that a +chef_gem+ resource exists in the Chef run with the
+    # action +:reconfig+. Given a Chef Recipe that reconfigures "community-zero"
+    # as a +chef_gem+:
+    #
+    #     chef_gem 'community-zero' do
+    #       action :reconfig
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +chef_gem+ resource with ChefSpec.
+    #
+    # @example Assert that a +chef_gem+ was reconfigured
+    #   expect(chef_run).to reconfig_chef_gem('community-zero')
+    #
+    # @example Assert that a +chef_gem+ was reconfigured with predicate matchers
+    #   expect(chef_run).to reconfig_chef_gem('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was reconfigured with attributes
+    #   expect(chef_run).to reconfig_chef_gem('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was reconfigured using a regex
+    #   expect(chef_run).to reconfig_chef_gem('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +chef_gem+ was _not_ reconfigured
+    #   expect(chef_run).to_not reconfig_chef_gem('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def reconfig_chef_gem(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:chef_gem, :reconfig, resource_name)
+    end
+
+    #
+    # Assert that a +chef_gem+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "community-zero" as a
+    # +chef_gem+:
+    #
+    #     chef_gem 'community-zero' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +chef_gem+ resource with ChefSpec.
+    #
+    # @example Assert that a +chef_gem+ was removeed
+    #   expect(chef_run).to remove_chef_gem('community-zero')
+    #
+    # @example Assert that a +chef_gem+ was removeed with predicate matchers
+    #   expect(chef_run).to remove_chef_gem('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was removeed with attributes
+    #   expect(chef_run).to remove_chef_gem('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was removeed using a regex
+    #   expect(chef_run).to remove_chef_gem('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +chef_gem+ was _not_ removeed
+    #   expect(chef_run).to_not remove_chef_gem('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_chef_gem(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:chef_gem, :remove, resource_name)
+    end
+
+    #
+    # Assert that a +chef_gem+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "community-zero" as a
+    # +chef_gem+:
+    #
+    #     chef_gem 'community-zero' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +chef_gem+ resource with ChefSpec.
+    #
+    # @example Assert that a +chef_gem+ was upgradeed
+    #   expect(chef_run).to upgrade_chef_gem('community-zero')
+    #
+    # @example Assert that a +chef_gem+ was upgradeed with predicate matchers
+    #   expect(chef_run).to upgrade_chef_gem('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was upgradeed with attributes
+    #   expect(chef_run).to upgrade_chef_gem('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +chef_gem+ was upgradeed using a regex
+    #   expect(chef_run).to upgrade_chef_gem('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +chef_gem+ was _not_ upgradeed
+    #   expect(chef_run).to_not upgrade_chef_gem('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_chef_gem(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:chef_gem, :upgrade, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/cookbook_file.rb b/lib/chefspec/api/cookbook_file.rb
new file mode 100644
index 0000000..906bef3
--- /dev/null
+++ b/lib/chefspec/api/cookbook_file.rb
@@ -0,0 +1,166 @@
+module ChefSpec::API
+  # @since 0.0.1
+  module CookbookFileMatchers
+    ChefSpec.define_matcher :cookbook_file
+
+    #
+    # Assert that a +cookbook_file+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "/tmp/config" as a
+    # +cookbook_file+:
+    #
+    #     cookbook_file '/tmp/config' do
+    #       action :create
+    #     end
+    #
+    # To test the content rendered by a +cookbook_file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +cookbook_file+ resource with ChefSpec.
+    #
+    # @example Assert that a +cookbook_file+ was created
+    #   expect(chef_run).to create_cookbook_file('/tmp/config')
+    #
+    # @example Assert that a +cookbook_file+ was created with predicate matchers
+    #   expect(chef_run).to create_cookbook_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +cookbook_file+ was created with attributes
+    #   expect(chef_run).to create_cookbook_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +cookbook_file+ was created using a regex
+    #   expect(chef_run).to create_cookbook_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +cookbook_file+ was _not_ created
+    #   expect(chef_run).to_not create_cookbook_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_cookbook_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:cookbook_file, :create, resource_name)
+    end
+
+    #
+    # Assert that a +cookbook_file+ resource exists in the Chef run with the
+    # action +:create_if_missing+. Given a Chef Recipe that creates "/tmp/config"
+    # if missing as a +cookbook_file+:
+    #
+    #     cookbook_file '/tmp/config' do
+    #       action :create_if_missing
+    #     end
+    #
+    # To test the content rendered by a +cookbook_file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +cookbook_file+ resource with ChefSpec.
+    #
+    # @example Assert that a +cookbook_file+ was created if missing
+    #   expect(chef_run).to create_cookbook_file_if_missing('/tmp/config')
+    #
+    # @example Assert that a +cookbook_file+ was created if missing with predicate matchers
+    #   expect(chef_run).to create_cookbook_file_if_missing('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +cookbook_file+ was created if missing with attributes
+    #   expect(chef_run).to create_cookbook_file_if_missing('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +cookbook_file+ was created if missing using a regex
+    #   expect(chef_run).to create_cookbook_file_if_missing('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +cookbook_file+ was _not_ created if missing
+    #   expect(chef_run).to_not create_cookbook_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_cookbook_file_if_missing(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:cookbook_file, :create_if_missing, resource_name)
+    end
+
+    #
+    # Assert that a +cookbook_file+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "/tmp/config" as a
+    # +cookbook_file+:
+    #
+    #     cookbook_file '/tmp/config' do
+    #       action :delete
+    #     end
+    #
+    # To test the content rendered by a +cookbook_file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +cookbook_file+ resource with ChefSpec.
+    #
+    # @example Assert that a +cookbook_file+ was deleted
+    #   expect(chef_run).to delete_cookbook_file('/tmp/config')
+    #
+    # @example Assert that a +cookbook_file+ was deleted with predicate matchers
+    #   expect(chef_run).to delete_cookbook_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +cookbook_file+ was deleted with attributes
+    #   expect(chef_run).to delete_cookbook_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +cookbook_file+ was deleted using a regex
+    #   expect(chef_run).to delete_cookbook_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +cookbook_file+ was _not_ deleted
+    #   expect(chef_run).to_not delete_cookbook_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_cookbook_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:cookbook_file, :delete, resource_name)
+    end
+
+    #
+    # Assert that a +cookbook_file+ resource exists in the Chef run with the
+    # action +:touch+. Given a Chef Recipe that touches "/tmp/config" as a
+    # +cookbook_file+:
+    #
+    #     cookbook_file '/tmp/config' do
+    #       action :touch
+    #     end
+    #
+    # To test the content rendered by a +cookbook_file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +cookbook_file+ resource with ChefSpec.
+    #
+    # @example Assert that a +cookbook_file+ was touched
+    #   expect(chef_run).to touch_cookbook_file('/tmp/config')
+    #
+    # @example Assert that a +cookbook_file+ was touched with predicate matchers
+    #   expect(chef_run).to touch_cookbook_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +cookbook_file+ was touched with attributes
+    #   expect(chef_run).to touch_cookbook_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +cookbook_file+ was touched using a regex
+    #   expect(chef_run).to touch_cookbook_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +cookbook_file+ was _not_ touched
+    #   expect(chef_run).to_not touch_cookbook_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def touch_cookbook_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:cookbook_file, :touch, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/cron.rb b/lib/chefspec/api/cron.rb
new file mode 100644
index 0000000..7ac1f1d
--- /dev/null
+++ b/lib/chefspec/api/cron.rb
@@ -0,0 +1,80 @@
+module ChefSpec::API
+  # @since 0.7.0
+  module CronMatchers
+    ChefSpec.define_matcher :cron
+
+    #
+    # Assert that a +cron+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "ping nagios" as a
+    # +cron+:
+    #
+    #     cron 'ping nagios' do
+    #       action :create
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +cron+ resource with ChefSpec.
+    #
+    # @example Assert that a +cron+ was created
+    #   expect(chef_run).to create_cron('ping nagios')
+    #
+    # @example Assert that a +cron+ was created with predicate matchers
+    #   expect(chef_run).to create_cron('ping nagios').with_home('/home')
+    #
+    # @example Assert that a +cron+ was created with attributes
+    #   expect(chef_run).to create_cron('ping nagios').with(home: '/home')
+    #
+    # @example Assert that a +cron+ was created using a regex
+    #   expect(chef_run).to create_cron('ping nagios').with(home: /\/home/)
+    #
+    # @example Assert that a +cron+ was _not_ created
+    #   expect(chef_run).to_not create_cron('ping nagios')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_cron(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:cron, :create, resource_name)
+    end
+
+    #
+    # Assert that a +cron+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "ping nagios" as a
+    # +cron+:
+    #
+    #     cron 'ping nagios' do
+    #       action :delete
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +cron+ resource with ChefSpec.
+    #
+    # @example Assert that a +cron+ was deleted
+    #   expect(chef_run).to delete_cron('ping nagios')
+    #
+    # @example Assert that a +cron+ was deleted with predicate matchers
+    #   expect(chef_run).to delete_cron('ping nagios').with_home('/home')
+    #
+    # @example Assert that a +cron+ was deleted with attributes
+    #   expect(chef_run).to delete_cron('ping nagios').with(home: '/home')
+    #
+    # @example Assert that a +cron+ was deleted using a regex
+    #   expect(chef_run).to delete_cron('ping nagios').with(home: /\/home/)
+    #
+    # @example Assert that a +cron+ was _not_ deleted
+    #   expect(chef_run).to_not delete_cron('ping nagios')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_cron(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:cron, :delete, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/deploy.rb b/lib/chefspec/api/deploy.rb
new file mode 100644
index 0000000..7b18bd1
--- /dev/null
+++ b/lib/chefspec/api/deploy.rb
@@ -0,0 +1,117 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module DeployMatchers
+    ChefSpec.define_matcher :deploy
+
+    #
+    # Assert that a +deploy+ resource exists in the Chef run with the
+    # action +:deploy+. Given a Chef Recipe that deploys "/tmp/path" as a
+    # +deploy+:
+    #
+    #     deploy '/tmp/path' do
+    #       action :deploy
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +deploy+ resource with ChefSpec.
+    #
+    # @example Assert that a +deploy+ was deployed
+    #   expect(chef_run).to deploy_deploy('/tmp/path')
+    #
+    # @example Assert that a +deploy+ was deployed with predicate matchers
+    #   expect(chef_run).to deploy_deploy('/tmp/path').with_repo('ssh://...')
+    #
+    # @example Assert that a +deploy+ was deployed with attributes
+    #   expect(chef_run).to deploy_deploy('/tmp/path').with(repo: 'ssh://...')
+    #
+    # @example Assert that a +deploy+ was deployed using a regex
+    #   expect(chef_run).to deploy_deploy('/tmp/path').with(repo: /ssh:(.+)/)
+    #
+    # @example Assert that a +deploy+ was _not_ deployed
+    #   expect(chef_run).to_not deploy_deploy('/tmp/path')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def deploy_deploy(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:deploy, :deploy, resource_name)
+    end
+
+    #
+    # Assert that a +deploy+ resource exists in the Chef run with the
+    # action +:force_deploy+. Given a Chef Recipe that force_deploys "/tmp/path" as a
+    # +deploy+:
+    #
+    #     deploy '/tmp/path' do
+    #       action :force_deploy
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +deploy+ resource with ChefSpec.
+    #
+    # @example Assert that a +deploy+ was force_deployed
+    #   expect(chef_run).to force_deploy_deploy('/tmp/path')
+    #
+    # @example Assert that a +deploy+ was force_deployed with predicate matchers
+    #   expect(chef_run).to force_deploy_deploy('/tmp/path').with_repo('ssh://...')
+    #
+    # @example Assert that a +deploy+ was force_deployed with attributes
+    #   expect(chef_run).to force_deploy_deploy('/tmp/path').with(repo: 'ssh://...')
+    #
+    # @example Assert that a +deploy+ was force_deployed using a regex
+    #   expect(chef_run).to force_deploy_deploy('/tmp/path').with(repo: /ssh:(.+)/)
+    #
+    # @example Assert that a +deploy+ was _not_ force_deployed
+    #   expect(chef_run).to_not force_deploy_deploy('/tmp/path')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def force_deploy_deploy(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:deploy, :force_deploy, resource_name)
+    end
+
+    #
+    # Assert that a +deploy+ resource exists in the Chef run with the
+    # action +:rollback+. Given a Chef Recipe that rolls back "/tmp/path" as a
+    # +deploy+:
+    #
+    #     deploy '/tmp/path' do
+    #       action :rollback
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +deploy+ resource with ChefSpec.
+    #
+    # @example Assert that a +deploy+ was rolled back
+    #   expect(chef_run).to rollback_deploy('/tmp/path')
+    #
+    # @example Assert that a +deploy+ was rolled back with predicate matchers
+    #   expect(chef_run).to rollback_deploy('/tmp/path').with_repo('ssh://...')
+    #
+    # @example Assert that a +deploy+ was rolled back with attributes
+    #   expect(chef_run).to rollback_deploy('/tmp/path').with(repo: 'ssh://...')
+    #
+    # @example Assert that a +deploy+ was rolled back using a regex
+    #   expect(chef_run).to rollback_deploy('/tmp/path').with(repo: /ssh:(.+)/)
+    #
+    # @example Assert that a +deploy+ was _not_ rolled back
+    #   expect(chef_run).to_not rollback_deploy('/tmp/path')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def rollback_deploy(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:deploy, :rollback, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/directory.rb b/lib/chefspec/api/directory.rb
new file mode 100644
index 0000000..2928ff8
--- /dev/null
+++ b/lib/chefspec/api/directory.rb
@@ -0,0 +1,80 @@
+module ChefSpec::API
+  # @since 0.0.1
+  module DirectoryMatchers
+    ChefSpec.define_matcher :directory
+
+    #
+    # Assert that a +directory+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "/tmp" as a
+    # +directory+:
+    #
+    #     directory '/tmp' do
+    #       action :create
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +directory+ resource with ChefSpec.
+    #
+    # @example Assert that a +directory+ was createed
+    #   expect(chef_run).to create_directory('/tmp')
+    #
+    # @example Assert that a +directory+ was createed with predicate matchers
+    #   expect(chef_run).to create_directory('/tmp').with_user('svargo')
+    #
+    # @example Assert that a +directory+ was createed with attributes
+    #   expect(chef_run).to create_directory('/tmp').with(user: 'svargo')
+    #
+    # @example Assert that a +directory+ was createed using a regex
+    #   expect(chef_run).to create_directory('/tmp').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +directory+ was _not_ createed
+    #   expect(chef_run).to_not create_directory('/tmp')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_directory(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:directory, :create, resource_name)
+    end
+
+    #
+    # Assert that a +directory+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "/tmp" as a
+    # +directory+:
+    #
+    #     directory '/tmp' do
+    #       action :delete
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +directory+ resource with ChefSpec.
+    #
+    # @example Assert that a +directory+ was deleteed
+    #   expect(chef_run).to delete_directory('/tmp')
+    #
+    # @example Assert that a +directory+ was deleteed with predicate matchers
+    #   expect(chef_run).to delete_directory('/tmp').with_user('svargo')
+    #
+    # @example Assert that a +directory+ was deleteed with attributes
+    #   expect(chef_run).to delete_directory('/tmp').with(user: 'svargo')
+    #
+    # @example Assert that a +directory+ was deleteed using a regex
+    #   expect(chef_run).to delete_directory('/tmp').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +directory+ was _not_ deleteed
+    #   expect(chef_run).to_not delete_directory('/tmp')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_directory(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:directory, :delete, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/do_nothing.rb b/lib/chefspec/api/do_nothing.rb
new file mode 100644
index 0000000..c48b43f
--- /dev/null
+++ b/lib/chefspec/api/do_nothing.rb
@@ -0,0 +1,24 @@
+module ChefSpec::API
+  # @since 3.4.0
+  module DoNothingMatchers
+    #
+    # Assert that a resource in the Chef run does not perform any actions. Given
+    # a resource with +action :nothing+:
+    #
+    #     package 'apache2' do
+    #       action :nothing
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test that no
+    # actions were performed on a resource in a Chef run.
+    #
+    # @example Assert the +package+ does not perform any actions
+    #
+    #
+    # @return [ChefSpec::Matchers::DoNothingMatcher]
+    #
+    def do_nothing
+      ChefSpec::Matchers::DoNothingMatcher.new
+    end
+  end
+end
diff --git a/lib/chefspec/api/dpkg_package.rb b/lib/chefspec/api/dpkg_package.rb
new file mode 100644
index 0000000..fa2b853
--- /dev/null
+++ b/lib/chefspec/api/dpkg_package.rb
@@ -0,0 +1,117 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module DpkgPackageMatchers
+    ChefSpec.define_matcher :dpkg_package
+
+    #
+    # Assert that a +dpkg_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as a
+    # +dpkg_package+:
+    #
+    #     dpkg_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +dpkg_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +dpkg_package+ was installed
+    #   expect(chef_run).to install_dpkg_package('apache2')
+    #
+    # @example Assert that a +dpkg_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_dpkg_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +dpkg_package+ was installed with attributes
+    #   expect(chef_run).to install_dpkg_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +dpkg_package+ was installed using a regex
+    #   expect(chef_run).to install_dpkg_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +dpkg_package+ was _not_ installed
+    #   expect(chef_run).to_not install_dpkg_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_dpkg_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:dpkg_package, :install, resource_name)
+    end
+
+    #
+    # Assert that a +dpkg_package+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "apache2" as a
+    # +dpkg_package+:
+    #
+    #     dpkg_package 'apache2' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +dpkg_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +dpkg_package+ was purged
+    #   expect(chef_run).to purge_dpkg_package('apache2')
+    #
+    # @example Assert that a +dpkg_package+ was purged with predicate matchers
+    #   expect(chef_run).to purge_dpkg_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +dpkg_package+ was purged with attributes
+    #   expect(chef_run).to purge_dpkg_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +dpkg_package+ was purged using a regex
+    #   expect(chef_run).to purge_dpkg_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +dpkg_package+ was _not_ purged
+    #   expect(chef_run).to_not purge_dpkg_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_dpkg_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:dpkg_package, :purge, resource_name)
+    end
+
+    #
+    # Assert that a +dpkg_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as a
+    # +dpkg_package+:
+    #
+    #     dpkg_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +dpkg_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +dpkg_package+ was removed
+    #   expect(chef_run).to remove_dpkg_package('apache2')
+    #
+    # @example Assert that a +dpkg_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_dpkg_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +dpkg_package+ was removed with attributes
+    #   expect(chef_run).to remove_dpkg_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +dpkg_package+ was removed using a regex
+    #   expect(chef_run).to remove_dpkg_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +dpkg_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_dpkg_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_dpkg_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:dpkg_package, :remove, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/easy_install_package.rb b/lib/chefspec/api/easy_install_package.rb
new file mode 100644
index 0000000..93a7645
--- /dev/null
+++ b/lib/chefspec/api/easy_install_package.rb
@@ -0,0 +1,154 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module EasyInstallPackageMatchers
+    ChefSpec.define_matcher :easy_install_package
+
+    #
+    # Assert that an +easy_install_package+ resource exists in the Chef run
+    # with the action +:install+. Given a Chef Recipe that installs "pygments" as a
+    # +easy_install_package+:
+    #
+    #     easy_install_package 'pygments' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +easy_install_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +easy_install_package+ was installed
+    #   expect(chef_run).to install_easy_install_package('pygments')
+    #
+    # @example Assert that an +easy_install_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_easy_install_package('pygments').with_version('1.2.3')
+    #
+    # @example Assert that an +easy_install_package+ was installed with attributes
+    #   expect(chef_run).to install_easy_install_package('pygments').with(version: '1.2.3')
+    #
+    # @example Assert that an +easy_install_package+ was installed using a regex
+    #   expect(chef_run).to install_easy_install_package('pygments').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +easy_install_package+ was _not_ installed
+    #   expect(chef_run).to_not install_easy_install_package('pygments')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_easy_install_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:easy_install_package, :install, resource_name)
+    end
+
+    #
+    # Assert that an +easy_install_package+ resource exists in the Chef run
+    # with the action +:purge+. Given a Chef Recipe that purges "pygments" as a
+    # +easy_install_package+:
+    #
+    #     easy_install_package 'pygments' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +easy_install_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +easy_install_package+ was purged
+    #   expect(chef_run).to purge_easy_install_package('pygments')
+    #
+    # @example Assert that an +easy_install_package+ was purged with predicate matchers
+    #   expect(chef_run).to purge_easy_install_package('pygments').with_version('1.2.3')
+    #
+    # @example Assert that an +easy_install_package+ was purged with attributes
+    #   expect(chef_run).to purge_easy_install_package('pygments').with(version: '1.2.3')
+    #
+    # @example Assert that an +easy_install_package+ was purged using a regex
+    #   expect(chef_run).to purge_easy_install_package('pygments').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +easy_install_package+ was _not_ purged
+    #   expect(chef_run).to_not purge_easy_install_package('pygments')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_easy_install_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:easy_install_package, :purge, resource_name)
+    end
+
+    #
+    # Assert that an +easy_install_package+ resource exists in the Chef run
+    # with the action +:remove+. Given a Chef Recipe that removes "pygments" as a
+    # +easy_install_package+:
+    #
+    #     easy_install_package 'pygments' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +easy_install_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +easy_install_package+ was removed
+    #   expect(chef_run).to remove_easy_install_package('pygments')
+    #
+    # @example Assert that an +easy_install_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_easy_install_package('pygments').with_version('1.2.3')
+    #
+    # @example Assert that an +easy_install_package+ was removed with attributes
+    #   expect(chef_run).to remove_easy_install_package('pygments').with(version: '1.2.3')
+    #
+    # @example Assert that an +easy_install_package+ was removed using a regex
+    #   expect(chef_run).to remove_easy_install_package('pygments').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +easy_install_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_easy_install_package('pygments')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_easy_install_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:easy_install_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that an +easy_install_package+ resource exists in the Chef run
+    # with the action +:upgrade+. Given a Chef Recipe that upgrades "pygments" as a
+    # +easy_install_package+:
+    #
+    #     easy_install_package 'pygments' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +easy_install_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +easy_install_package+ was upgraded
+    #   expect(chef_run).to upgrade_easy_install_package('pygments')
+    #
+    # @example Assert that an +easy_install_package+ was upgraded with predicate matchers
+    #   expect(chef_run).to upgrade_easy_install_package('pygments').with_version('1.2.3')
+    #
+    # @example Assert that an +easy_install_package+ was upgraded with attributes
+    #   expect(chef_run).to upgrade_easy_install_package('pygments').with(version: '1.2.3')
+    #
+    # @example Assert that an +easy_install_package+ was upgraded using a regex
+    #   expect(chef_run).to upgrade_easy_install_package('pygments').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +easy_install_package+ was _not_ upgraded
+    #   expect(chef_run).to_not upgrade_easy_install_package('pygments')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_easy_install_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:easy_install_package, :upgrade, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/env.rb b/lib/chefspec/api/env.rb
new file mode 100644
index 0000000..fc9e3bb
--- /dev/null
+++ b/lib/chefspec/api/env.rb
@@ -0,0 +1,117 @@
+module ChefSpec::API
+  # @since 0.9.0
+  module EnvMatchers
+    ChefSpec.define_matcher :env
+
+    #
+    # Assert that an +env+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "HOME" as an
+    # +env+:
+    #
+    #     env 'HOME' do
+    #       action :create
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +env+ resource with ChefSpec.
+    #
+    # @example Assert that an +env+ was created
+    #   expect(chef_run).to create_env('HOME')
+    #
+    # @example Assert that an +env+ was created with predicate matchers
+    #   expect(chef_run).to create_env('HOME').with_value('/home')
+    #
+    # @example Assert that an +env+ was created with attributes
+    #   expect(chef_run).to create_env('HOME').with(value: 'home')
+    #
+    # @example Assert that an +env+ was created using a regex
+    #   expect(chef_run).to create_env('HOME').with(value: /\/ho(.+)/)
+    #
+    # @example Assert that an +env+ was _not_ created
+    #   expect(chef_run).to_not create_env('HOME')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_env(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:env, :create, resource_name)
+    end
+
+    #
+    # Assert that an +env+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "HOME" as an
+    # +env+:
+    #
+    #     env 'HOME' do
+    #       action :delete
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +env+ resource with ChefSpec.
+    #
+    # @example Assert that an +env+ was deleted
+    #   expect(chef_run).to delete_env('HOME')
+    #
+    # @example Assert that an +env+ was deleted with predicate matchers
+    #   expect(chef_run).to delete_env('HOME').with_value('/home')
+    #
+    # @example Assert that an +env+ was deleted with attributes
+    #   expect(chef_run).to delete_env('HOME').with(value: 'home')
+    #
+    # @example Assert that an +env+ was deleted using a regex
+    #   expect(chef_run).to delete_env('HOME').with(value: /\/ho(.+)/)
+    #
+    # @example Assert that an +env+ was _not_ deleted
+    #   expect(chef_run).to_not delete_env('HOME')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_env(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:env, :delete, resource_name)
+    end
+
+    #
+    # Assert that an +env+ resource exists in the Chef run with the
+    # action +:modify+. Given a Chef Recipe that modifies "HOME" as an
+    # +env+:
+    #
+    #     env 'HOME' do
+    #       action :modify
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +env+ resource with ChefSpec.
+    #
+    # @example Assert that an +env+ was modified
+    #   expect(chef_run).to modify_env('HOME')
+    #
+    # @example Assert that an +env+ was modified with predicate matchers
+    #   expect(chef_run).to modify_env('HOME').with_value('/home')
+    #
+    # @example Assert that an +env+ was modified with attributes
+    #   expect(chef_run).to modify_env('HOME').with(value: 'home')
+    #
+    # @example Assert that an +env+ was modified using a regex
+    #   expect(chef_run).to modify_env('HOME').with(value: /\/ho(.+)/)
+    #
+    # @example Assert that an +env+ was _not_ modified
+    #   expect(chef_run).to_not modify_env('HOME')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def modify_env(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:env, :modify, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/erl_call.rb b/lib/chefspec/api/erl_call.rb
new file mode 100644
index 0000000..a945f08
--- /dev/null
+++ b/lib/chefspec/api/erl_call.rb
@@ -0,0 +1,43 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module ErlCallMatchers
+    ChefSpec.define_matcher :erl_call
+
+    #
+    # Assert that an +erl_call+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "net_adm:names()" as an
+    # +erl_call+:
+    #
+    #     erl_call 'net_adm:names()' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +erl_call+ resource with ChefSpec.
+    #
+    # @example Assert that an +erl_call+ was run
+    #   expect(chef_run).to run_erl_call('net_adm:names()')
+    #
+    # @example Assert that an +erl_call+ was run with predicate matchers
+    #   expect(chef_run).to run_erl_call('net_adm:names()').with_node_name('example')
+    #
+    # @example Assert that an +erl_call+ was run with attributes
+    #   expect(chef_run).to run_erl_call('net_adm:names()').with(node_name: 'example')
+    #
+    # @example Assert that an +erl_call+ was run using a regex
+    #   expect(chef_run).to run_erl_call('net_adm:names()').with(node_name: /[a-z]+/)
+    #
+    # @example Assert that an +erl_call+ was _not_ run
+    #   expect(chef_run).to_not run_erl_call('net_adm:names()')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_erl_call(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:erl_call, :run, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/execute.rb b/lib/chefspec/api/execute.rb
new file mode 100644
index 0000000..c69de6d
--- /dev/null
+++ b/lib/chefspec/api/execute.rb
@@ -0,0 +1,43 @@
+module ChefSpec::API
+  # @since 0.0.1
+  module ExecuteMatchers
+    ChefSpec.define_matcher :execute
+
+    #
+    # Assert that an +execute+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "echo "hello"" as an
+    # +execute+:
+    #
+    #     execute 'echo "hello"' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +execute+ resource with ChefSpec.
+    #
+    # @example Assert that an +execute+ was run
+    #   expect(chef_run).to run_execute('echo "hello"')
+    #
+    # @example Assert that an +execute+ was run with predicate matchers
+    #   expect(chef_run).to run_execute('echo "hello"').with_user('svargo')
+    #
+    # @example Assert that an +execute+ was run with attributes
+    #   expect(chef_run).to run_execute('echo "hello"').with(user: 'svargo')
+    #
+    # @example Assert that an +execute+ was run using a regex
+    #   expect(chef_run).to run_execute('echo "hello"').with(user: /sva(.+)/)
+    #
+    # @example Assert that an +execute+ was _not_ run
+    #   expect(chef_run).to_not run_execute('echo "hello"')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_execute(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:execute, :run, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/file.rb b/lib/chefspec/api/file.rb
new file mode 100644
index 0000000..89b1271
--- /dev/null
+++ b/lib/chefspec/api/file.rb
@@ -0,0 +1,166 @@
+module ChefSpec::API
+  # @since 0.0.1
+  module FileMatchers
+    ChefSpec.define_matcher :file
+
+    #
+    # Assert that a +file+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "/tmp/config" as a
+    # +file+:
+    #
+    #     file '/tmp/config' do
+    #       action :create
+    #     end
+    #
+    # To test the content rendered by a +file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +file+ resource with ChefSpec.
+    #
+    # @example Assert that a +file+ was created
+    #   expect(chef_run).to create_file('/tmp/config')
+    #
+    # @example Assert that a +file+ was created with predicate matchers
+    #   expect(chef_run).to create_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +file+ was created with attributes
+    #   expect(chef_run).to create_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +file+ was created using a regex
+    #   expect(chef_run).to create_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +file+ was _not_ created
+    #   expect(chef_run).to_not create_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:file, :create, resource_name)
+    end
+
+    #
+    # Assert that a +file+ resource exists in the Chef run with the
+    # action +:create_if_missing+. Given a Chef Recipe that creates "/tmp/config"
+    # if missing as a +file+:
+    #
+    #     file '/tmp/config' do
+    #       action :create_if_missing
+    #     end
+    #
+    # To test the content rendered by a +file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +file+ resource with ChefSpec.
+    #
+    # @example Assert that a +file+ was created if missing
+    #   expect(chef_run).to create_file_if_missing('/tmp/config')
+    #
+    # @example Assert that a +file+ was created if missing with predicate matchers
+    #   expect(chef_run).to create_file_if_missing('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +file+ was created if missing with attributes
+    #   expect(chef_run).to create_file_if_missing('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +file+ was created if missing using a regex
+    #   expect(chef_run).to create_file_if_missing('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +file+ was _not_ created if missing
+    #   expect(chef_run).to_not create_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_file_if_missing(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:file, :create_if_missing, resource_name)
+    end
+
+    #
+    # Assert that a +file+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "/tmp/config" as a
+    # +file+:
+    #
+    #     file '/tmp/config' do
+    #       action :delete
+    #     end
+    #
+    # To test the content rendered by a +file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +file+ resource with ChefSpec.
+    #
+    # @example Assert that a +file+ was deleted
+    #   expect(chef_run).to delete_file('/tmp/config')
+    #
+    # @example Assert that a +file+ was deleted with predicate matchers
+    #   expect(chef_run).to delete_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +file+ was deleted with attributes
+    #   expect(chef_run).to delete_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +file+ was deleted using a regex
+    #   expect(chef_run).to delete_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +file+ was _not_ deleted
+    #   expect(chef_run).to_not delete_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:file, :delete, resource_name)
+    end
+
+    #
+    # Assert that a +file+ resource exists in the Chef run with the
+    # action +:touch+. Given a Chef Recipe that touches "/tmp/config" as a
+    # +file+:
+    #
+    #     file '/tmp/config' do
+    #       action :touch
+    #     end
+    #
+    # To test the content rendered by a +file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +file+ resource with ChefSpec.
+    #
+    # @example Assert that a +file+ was touched
+    #   expect(chef_run).to touch_file('/tmp/config')
+    #
+    # @example Assert that a +file+ was touched with predicate matchers
+    #   expect(chef_run).to touch_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +file+ was touched with attributes
+    #   expect(chef_run).to touch_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +file+ was touched using a regex
+    #   expect(chef_run).to touch_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +file+ was _not_ touched
+    #   expect(chef_run).to_not touch_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def touch_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:file, :touch, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/freebsd_package.rb b/lib/chefspec/api/freebsd_package.rb
new file mode 100644
index 0000000..18a1cc9
--- /dev/null
+++ b/lib/chefspec/api/freebsd_package.rb
@@ -0,0 +1,80 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module FreebsdPackageMatchers
+    ChefSpec.define_matcher :freebsd_package
+
+    #
+    # Assert that a +freebsd_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as a
+    # +freebsd_package+:
+    #
+    #     freebsd_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +freebsd_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +freebsd_package+ was installed
+    #   expect(chef_run).to install_freebsd_package('apache2')
+    #
+    # @example Assert that a +freebsd_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_freebsd_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +freebsd_package+ was installed with attributes
+    #   expect(chef_run).to install_freebsd_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +freebsd_package+ was installed using a regex
+    #   expect(chef_run).to install_freebsd_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +freebsd_package+ was _not_ installed
+    #   expect(chef_run).to_not install_freebsd_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_freebsd_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:freebsd_package, :install, resource_name)
+    end
+
+    #
+    # Assert that a +freebsd_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as a
+    # +freebsd_package+:
+    #
+    #     freebsd_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +freebsd_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +freebsd_package+ was removed
+    #   expect(chef_run).to remove_freebsd_package('apache2')
+    #
+    # @example Assert that a +freebsd_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_freebsd_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +freebsd_package+ was removed with attributes
+    #   expect(chef_run).to remove_freebsd_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +freebsd_package+ was removed using a regex
+    #   expect(chef_run).to remove_freebsd_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +freebsd_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_freebsd_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_freebsd_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:freebsd_package, :remove, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/gem_package.rb b/lib/chefspec/api/gem_package.rb
new file mode 100644
index 0000000..2ca2d5b
--- /dev/null
+++ b/lib/chefspec/api/gem_package.rb
@@ -0,0 +1,191 @@
+module ChefSpec::API
+  # @since 0.5.0
+  module GemPackageMatchers
+    ChefSpec.define_matcher :gem_package
+
+    #
+    # Assert that a +gem_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "community-zero" as a
+    # +gem_package+:
+    #
+    #     gem_package 'community-zero' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +gem_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +gem_package+ was installed
+    #   expect(chef_run).to install_gem_package('community-zero')
+    #
+    # @example Assert that a +gem_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_gem_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +gem_package+ was installed with attributes
+    #   expect(chef_run).to install_gem_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +gem_package+ was installed using a regex
+    #   expect(chef_run).to install_gem_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +gem_package+ was _not_ installed
+    #   expect(chef_run).to_not install_gem_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_gem_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:gem_package, :install, resource_name)
+    end
+
+    #
+    # Assert that a +gem_package+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "community-zero" as a
+    # +gem_package+:
+    #
+    #     gem_package 'community-zero' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +gem_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +gem_package+ was purgeed
+    #   expect(chef_run).to purge_gem_package('community-zero')
+    #
+    # @example Assert that a +gem_package+ was purgeed with predicate matchers
+    #   expect(chef_run).to purge_gem_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +gem_package+ was purgeed with attributes
+    #   expect(chef_run).to purge_gem_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +gem_package+ was purgeed using a regex
+    #   expect(chef_run).to purge_gem_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +gem_package+ was _not_ purgeed
+    #   expect(chef_run).to_not purge_gem_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_gem_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:gem_package, :purge, resource_name)
+    end
+
+    #
+    # Assert that a +gem_package+ resource exists in the Chef run with the
+    # action +:reconfig+. Given a Chef Recipe that reconfigures "community-zero"
+    # as a +gem_package+:
+    #
+    #     gem_package 'community-zero' do
+    #       action :reconfig
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +gem_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +gem_package+ was reconfigured
+    #   expect(chef_run).to reconfig_gem_package('community-zero')
+    #
+    # @example Assert that a +gem_package+ was reconfigured with predicate matchers
+    #   expect(chef_run).to reconfig_gem_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +gem_package+ was reconfigured with attributes
+    #   expect(chef_run).to reconfig_gem_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +gem_package+ was reconfigured using a regex
+    #   expect(chef_run).to reconfig_gem_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +gem_package+ was _not_ reconfigured
+    #   expect(chef_run).to_not reconfig_gem_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def reconfig_gem_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:gem_package, :reconfig, resource_name)
+    end
+
+    #
+    # Assert that a +gem_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "community-zero" as a
+    # +gem_package+:
+    #
+    #     gem_package 'community-zero' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +gem_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +gem_package+ was removeed
+    #   expect(chef_run).to remove_gem_package('community-zero')
+    #
+    # @example Assert that a +gem_package+ was removeed with predicate matchers
+    #   expect(chef_run).to remove_gem_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +gem_package+ was removeed with attributes
+    #   expect(chef_run).to remove_gem_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +gem_package+ was removeed using a regex
+    #   expect(chef_run).to remove_gem_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +gem_package+ was _not_ removeed
+    #   expect(chef_run).to_not remove_gem_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_gem_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:gem_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that a +gem_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "community-zero" as a
+    # +gem_package+:
+    #
+    #     gem_package 'community-zero' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +gem_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +gem_package+ was upgradeed
+    #   expect(chef_run).to upgrade_gem_package('community-zero')
+    #
+    # @example Assert that a +gem_package+ was upgradeed with predicate matchers
+    #   expect(chef_run).to upgrade_gem_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +gem_package+ was upgradeed with attributes
+    #   expect(chef_run).to upgrade_gem_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +gem_package+ was upgradeed using a regex
+    #   expect(chef_run).to upgrade_gem_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +gem_package+ was _not_ upgradeed
+    #   expect(chef_run).to_not upgrade_gem_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_gem_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:gem_package, :upgrade, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/git.rb b/lib/chefspec/api/git.rb
new file mode 100644
index 0000000..47602aa
--- /dev/null
+++ b/lib/chefspec/api/git.rb
@@ -0,0 +1,117 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module GitMatchers
+    ChefSpec.define_matcher :git
+
+    #
+    # Assert that a +git+ resource exists in the Chef run with the
+    # action +:checkout+. Given a Chef Recipe that checks out "git://..." as a
+    # +git+:
+    #
+    #     git 'git://...' do
+    #       action :checkout
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +git+ resource with ChefSpec.
+    #
+    # @example Assert that a +git+ was checked out
+    #   expect(chef_run).to checkout_git('git://...')
+    #
+    # @example Assert that a +git+ was checked out with predicate matchers
+    #   expect(chef_run).to checkout_git('git://...').with_user('svargo')
+    #
+    # @example Assert that a +git+ was checked out with attributes
+    #   expect(chef_run).to checkout_git('git://...').with(user: 'svargo')
+    #
+    # @example Assert that a +git+ was checked out using a regex
+    #   expect(chef_run).to checkout_git('git://...').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +git+ was _not_ checked out
+    #   expect(chef_run).to_not checkout_git('git://...')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def checkout_git(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:git, :checkout, resource_name)
+    end
+
+    #
+    # Assert that a +git+ resource exists in the Chef run with the
+    # action +:export+. Given a Chef Recipe that exports "git://" as a
+    # +git+:
+    #
+    #     git 'git://' do
+    #       action :export
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +git+ resource with ChefSpec.
+    #
+    # @example Assert that a +git+ was exported
+    #   expect(chef_run).to export_git('git://')
+    #
+    # @example Assert that a +git+ was exported with predicate matchers
+    #   expect(chef_run).to export_git('git://').with_user('svargo')
+    #
+    # @example Assert that a +git+ was exported with attributes
+    #   expect(chef_run).to export_git('git://').with(user: 'svargo')
+    #
+    # @example Assert that a +git+ was exported using a regex
+    #   expect(chef_run).to export_git('git://').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +git+ was _not_ exported
+    #   expect(chef_run).to_not export_git('git://')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def export_git(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:git, :export, resource_name)
+    end
+
+    #
+    # Assert that a +git+ resource exists in the Chef run with the
+    # action +:sync+. Given a Chef Recipe that syncs "git://" as a
+    # +git+:
+    #
+    #     git 'git://' do
+    #       action :sync
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +git+ resource with ChefSpec.
+    #
+    # @example Assert that a +git+ was synced
+    #   expect(chef_run).to sync_git('git://')
+    #
+    # @example Assert that a +git+ was synced with predicate matchers
+    #   expect(chef_run).to sync_git('git://').with_user('svargo')
+    #
+    # @example Assert that a +git+ was synced with attributes
+    #   expect(chef_run).to sync_git('git://').with(user: 'svargo')
+    #
+    # @example Assert that a +git+ was synced using a regex
+    #   expect(chef_run).to sync_git('git://').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +git+ was _not_ synced
+    #   expect(chef_run).to_not sync_git('git://')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def sync_git(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:git, :sync, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/group.rb b/lib/chefspec/api/group.rb
new file mode 100644
index 0000000..e895775
--- /dev/null
+++ b/lib/chefspec/api/group.rb
@@ -0,0 +1,154 @@
+module ChefSpec::API
+  # @since 1.0.0
+  module GroupMatchers
+    ChefSpec.define_matcher :group
+
+    #
+    # Assert that a +group+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "apache2" as a
+    # +group+:
+    #
+    #     group 'apache2' do
+    #       action :create
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +group+ resource with ChefSpec.
+    #
+    # @example Assert that a +group+ was createed
+    #   expect(chef_run).to create_group('apache2')
+    #
+    # @example Assert that a +group+ was createed with predicate matchers
+    #   expect(chef_run).to create_group('apache2').with_gid(1234)
+    #
+    # @example Assert that a +group+ was createed with attributes
+    #   expect(chef_run).to create_group('apache2').with(gid: 1234)
+    #
+    # @example Assert that a +group+ was createed using a regex
+    #   expect(chef_run).to create_group('apache2').with(gid: /\d+/)
+    #
+    # @example Assert that a +group+ was _not_ createed
+    #   expect(chef_run).to_not create_group('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_group(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:group, :create, resource_name)
+    end
+
+    #
+    # Assert that a +group+ resource exists in the Chef run with the
+    # action +:manage+. Given a Chef Recipe that manages "apache2" as a
+    # +group+:
+    #
+    #     group 'apache2' do
+    #       action :manage
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +group+ resource with ChefSpec.
+    #
+    # @example Assert that a +group+ was managed
+    #   expect(chef_run).to manage_group('apache2')
+    #
+    # @example Assert that a +group+ was managed with predicate matchers
+    #   expect(chef_run).to manage_group('apache2').with_gid(1234)
+    #
+    # @example Assert that a +group+ was managed with attributes
+    #   expect(chef_run).to manage_group('apache2').with(gid: 1234)
+    #
+    # @example Assert that a +group+ was managed using a regex
+    #   expect(chef_run).to manage_group('apache2').with(gid: /\d+/)
+    #
+    # @example Assert that a +group+ was _not_ managed
+    #   expect(chef_run).to_not manage_group('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def manage_group(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:group, :manage, resource_name)
+    end
+
+    #
+    # Assert that a +group+ resource exists in the Chef run with the
+    # action +:modify+. Given a Chef Recipe that modifies "apache2" as a
+    # +group+:
+    #
+    #     group 'apache2' do
+    #       action :modify
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +group+ resource with ChefSpec.
+    #
+    # @example Assert that a +group+ was modified
+    #   expect(chef_run).to modify_group('apache2')
+    #
+    # @example Assert that a +group+ was modified with predicate matchers
+    #   expect(chef_run).to modify_group('apache2').with_gid(1234)
+    #
+    # @example Assert that a +group+ was modified with attributes
+    #   expect(chef_run).to modify_group('apache2').with(gid: 1234)
+    #
+    # @example Assert that a +group+ was modified using a regex
+    #   expect(chef_run).to modify_group('apache2').with(gid: /\d+/)
+    #
+    # @example Assert that a +group+ was _not_ modified
+    #   expect(chef_run).to_not modify_group('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def modify_group(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:group, :modify, resource_name)
+    end
+
+    #
+    # Assert that a +group+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as a
+    # +group+:
+    #
+    #     group 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +group+ resource with ChefSpec.
+    #
+    # @example Assert that a +group+ was removed
+    #   expect(chef_run).to remove_group('apache2')
+    #
+    # @example Assert that a +group+ was removed with predicate matchers
+    #   expect(chef_run).to remove_group('apache2').with_gid(1234)
+    #
+    # @example Assert that a +group+ was removed with attributes
+    #   expect(chef_run).to remove_group('apache2').with(gid: 1234)
+    #
+    # @example Assert that a +group+ was removed using a regex
+    #   expect(chef_run).to remove_group('apache2').with(gid: /\d+/)
+    #
+    # @example Assert that a +group+ was _not_ removed
+    #   expect(chef_run).to_not remove_group('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_group(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:group, :remove, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/http_request.rb b/lib/chefspec/api/http_request.rb
new file mode 100644
index 0000000..3b9dfba
--- /dev/null
+++ b/lib/chefspec/api/http_request.rb
@@ -0,0 +1,228 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module HttpRequestMatchers
+    ChefSpec.define_matcher :http_request
+
+    #
+    # Assert that an +http_request+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "apache2" as an
+    # +http_request+:
+    #
+    #     http_request 'apache2' do
+    #       action :delete
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +http_request+ resource with ChefSpec.
+    #
+    # @example Assert that an +http_request+ was DELETE
+    #   expect(chef_run).to delete_http_request('apache2')
+    #
+    # @example Assert that an +http_request+ was DELETE with predicate matchers
+    #   expect(chef_run).to delete_http_request('apache2').with_message('hello')
+    #
+    # @example Assert that an +http_request+ was DELETE with attributes
+    #   expect(chef_run).to delete_http_request('apache2').with(message: 'hello')
+    #
+    # @example Assert that an +http_request+ was DELETE using a regex
+    #   expect(chef_run).to delete_http_request('apache2').with(message: /he(.+)/)
+    #
+    # @example Assert that an +http_request+ was _not_ DELETE
+    #   expect(chef_run).to_not delete_http_request('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_http_request(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:http_request, :delete, resource_name)
+    end
+
+    #
+    # Assert that an +http_request+ resource exists in the Chef run with the
+    # action +:get+. Given a Chef Recipe that gets "apache2" as an
+    # +http_request+:
+    #
+    #     http_request 'apache2' do
+    #       action :get
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +http_request+ resource with ChefSpec.
+    #
+    # @example Assert that an +http_request+ was GET
+    #   expect(chef_run).to get_http_request('apache2')
+    #
+    # @example Assert that an +http_request+ was GET with predicate matchers
+    #   expect(chef_run).to get_http_request('apache2').with_message('hello')
+    #
+    # @example Assert that an +http_request+ was GET with attributes
+    #   expect(chef_run).to get_http_request('apache2').with(message: 'hello')
+    #
+    # @example Assert that an +http_request+ was GET using a regex
+    #   expect(chef_run).to get_http_request('apache2').with(message: /he(.+)/)
+    #
+    # @example Assert that an +http_request+ was _not_ GET
+    #   expect(chef_run).to_not get_http_request('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def get_http_request(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:http_request, :get, resource_name)
+    end
+
+    #
+    # Assert that an +http_request+ resource exists in the Chef run with the
+    # action +:head+. Given a Chef Recipe that heads "apache2" as an
+    # +http_request+:
+    #
+    #     http_request 'apache2' do
+    #       action :head
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +http_request+ resource with ChefSpec.
+    #
+    # @example Assert that an +http_request+ was HEAD
+    #   expect(chef_run).to head_http_request('apache2')
+    #
+    # @example Assert that an +http_request+ was HEAD with predicate matchers
+    #   expect(chef_run).to head_http_request('apache2').with_message('hello')
+    #
+    # @example Assert that an +http_request+ was HEAD with attributes
+    #   expect(chef_run).to head_http_request('apache2').with(message: 'hello')
+    #
+    # @example Assert that an +http_request+ was HEAD using a regex
+    #   expect(chef_run).to head_http_request('apache2').with(message: /he(.+)/)
+    #
+    # @example Assert that an +http_request+ was _not_ HEAD
+    #   expect(chef_run).to_not head_http_request('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def head_http_request(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:http_request, :head, resource_name)
+    end
+
+    #
+    # Assert that an +http_request+ resource exists in the Chef run with the
+    # action +:options+. Given a Chef Recipe that optionss "apache2" as an
+    # +http_request+:
+    #
+    #     http_request 'apache2' do
+    #       action :options
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +http_request+ resource with ChefSpec.
+    #
+    # @example Assert that an +http_request+ was OPTIONS
+    #   expect(chef_run).to options_http_request('apache2')
+    #
+    # @example Assert that an +http_request+ was OPTIONS with predicate matchers
+    #   expect(chef_run).to options_http_request('apache2').with_message('hello')
+    #
+    # @example Assert that an +http_request+ was OPTIONS with attributes
+    #   expect(chef_run).to options_http_request('apache2').with(message: 'hello')
+    #
+    # @example Assert that an +http_request+ was OPTIONS using a regex
+    #   expect(chef_run).to options_http_request('apache2').with(message: /he(.+)/)
+    #
+    # @example Assert that an +http_request+ was _not_ OPTIONS
+    #   expect(chef_run).to_not options_http_request('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def options_http_request(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:http_request, :options, resource_name)
+    end
+
+    #
+    # Assert that an +http_request+ resource exists in the Chef run with the
+    # action +:post+. Given a Chef Recipe that posts "apache2" as an
+    # +http_request+:
+    #
+    #     http_request 'apache2' do
+    #       action :post
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +http_request+ resource with ChefSpec.
+    #
+    # @example Assert that an +http_request+ was POST
+    #   expect(chef_run).to post_http_request('apache2')
+    #
+    # @example Assert that an +http_request+ was POST with predicate matchers
+    #   expect(chef_run).to post_http_request('apache2').with_message('hello')
+    #
+    # @example Assert that an +http_request+ was POST with attributes
+    #   expect(chef_run).to post_http_request('apache2').with(message: 'hello')
+    #
+    # @example Assert that an +http_request+ was POST using a regex
+    #   expect(chef_run).to post_http_request('apache2').with(message: /he(.+)/)
+    #
+    # @example Assert that an +http_request+ was _not_ POST
+    #   expect(chef_run).to_not post_http_request('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def post_http_request(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:http_request, :post, resource_name)
+    end
+
+    #
+    # Assert that an +http_request+ resource exists in the Chef run with the
+    # action +:put+. Given a Chef Recipe that puts "apache2" as an
+    # +http_request+:
+    #
+    #     http_request 'apache2' do
+    #       action :put
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +http_request+ resource with ChefSpec.
+    #
+    # @example Assert that an +http_request+ was PUT
+    #   expect(chef_run).to put_http_request('apache2')
+    #
+    # @example Assert that an +http_request+ was PUT with predicate matchers
+    #   expect(chef_run).to put_http_request('apache2').with_message('hello')
+    #
+    # @example Assert that an +http_request+ was PUT with attributes
+    #   expect(chef_run).to put_http_request('apache2').with(message: 'hello')
+    #
+    # @example Assert that an +http_request+ was PUT using a regex
+    #   expect(chef_run).to put_http_request('apache2').with(message: /he(.+)/)
+    #
+    # @example Assert that an +http_request+ was _not_ PUT
+    #   expect(chef_run).to_not put_http_request('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def put_http_request(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:http_request, :put, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/ifconfig.rb b/lib/chefspec/api/ifconfig.rb
new file mode 100644
index 0000000..092cc1b
--- /dev/null
+++ b/lib/chefspec/api/ifconfig.rb
@@ -0,0 +1,154 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module IfconfigMatchers
+    ChefSpec.define_matcher :ifconfig
+
+    #
+    # Assert that an +ifconfig+ resource exists in the Chef run with the
+    # action +:add+. Given a Chef Recipe that adds "10.0.0.1" as an
+    # +ifconfig+:
+    #
+    #     ifconfig '10.0.0.1' do
+    #       action :add
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +ifconfig+ resource with ChefSpec.
+    #
+    # @example Assert that an +ifconfig+ was added
+    #   expect(chef_run).to add_ifconfig('10.0.0.1')
+    #
+    # @example Assert that an +ifconfig+ was added with predicate matchers
+    #   expect(chef_run).to add_ifconfig('10.0.0.1').with_device('eth0')
+    #
+    # @example Assert that an +ifconfig+ was added with attributes
+    #   expect(chef_run).to add_ifconfig('10.0.0.1').with(device: 'eth0')
+    #
+    # @example Assert that an +ifconfig+ was added using a regex
+    #   expect(chef_run).to add_ifconfig('10.0.0.1').with(device: /eth(\d+)/)
+    #
+    # @example Assert that an +ifconfig+ was _not_ added
+    #   expect(chef_run).to_not add_ifconfig('10.0.0.1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def add_ifconfig(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ifconfig, :add, resource_name)
+    end
+
+    #
+    # Assert that an +ifconfig+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "10.0.0.1" as an
+    # +ifconfig+:
+    #
+    #     ifconfig '10.0.0.1' do
+    #       action :delete
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +ifconfig+ resource with ChefSpec.
+    #
+    # @example Assert that an +ifconfig+ was deleted
+    #   expect(chef_run).to delete_ifconfig('10.0.0.1')
+    #
+    # @example Assert that an +ifconfig+ was deleted with predicate matchers
+    #   expect(chef_run).to delete_ifconfig('10.0.0.1').with_device('eth0')
+    #
+    # @example Assert that an +ifconfig+ was deleted with attributes
+    #   expect(chef_run).to delete_ifconfig('10.0.0.1').with(device: 'eth0')
+    #
+    # @example Assert that an +ifconfig+ was deleted using a regex
+    #   expect(chef_run).to delete_ifconfig('10.0.0.1').with(device: /eth(\d+)/)
+    #
+    # @example Assert that an +ifconfig+ was _not_ deleted
+    #   expect(chef_run).to_not delete_ifconfig('10.0.0.1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_ifconfig(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ifconfig, :delete, resource_name)
+    end
+
+    #
+    # Assert that an +ifconfig+ resource exists in the Chef run with the
+    # action +:disable+. Given a Chef Recipe that disables "10.0.0.1" as an
+    # +ifconfig+:
+    #
+    #     ifconfig '10.0.0.1' do
+    #       action :disable
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +ifconfig+ resource with ChefSpec.
+    #
+    # @example Assert that an +ifconfig+ was disabled
+    #   expect(chef_run).to disable_ifconfig('10.0.0.1')
+    #
+    # @example Assert that an +ifconfig+ was disabled with predicate matchers
+    #   expect(chef_run).to disable_ifconfig('10.0.0.1').with_device('eth0')
+    #
+    # @example Assert that an +ifconfig+ was disabled with attributes
+    #   expect(chef_run).to disable_ifconfig('10.0.0.1').with(device: 'eth0')
+    #
+    # @example Assert that an +ifconfig+ was disabled using a regex
+    #   expect(chef_run).to disable_ifconfig('10.0.0.1').with(device: /eth(\d+)/)
+    #
+    # @example Assert that an +ifconfig+ was _not_ disabled
+    #   expect(chef_run).to_not disable_ifconfig('10.0.0.1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def disable_ifconfig(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ifconfig, :disable, resource_name)
+    end
+
+    #
+    # Assert that an +ifconfig+ resource exists in the Chef run with the
+    # action +:enable+. Given a Chef Recipe that enables "10.0.0.1" as an
+    # +ifconfig+:
+    #
+    #     ifconfig '10.0.0.1' do
+    #       action :enable
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +ifconfig+ resource with ChefSpec.
+    #
+    # @example Assert that an +ifconfig+ was enabled
+    #   expect(chef_run).to enable_ifconfig('10.0.0.1')
+    #
+    # @example Assert that an +ifconfig+ was enabled with predicate matchers
+    #   expect(chef_run).to enable_ifconfig('10.0.0.1').with_device('eth0')
+    #
+    # @example Assert that an +ifconfig+ was enabled with attributes
+    #   expect(chef_run).to enable_ifconfig('10.0.0.1').with(device: 'eth0')
+    #
+    # @example Assert that an +ifconfig+ was enabled using a regex
+    #   expect(chef_run).to enable_ifconfig('10.0.0.1').with(device: /eth(\d+)/)
+    #
+    # @example Assert that an +ifconfig+ was _not_ enabled
+    #   expect(chef_run).to_not enable_ifconfig('10.0.0.1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def enable_ifconfig(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ifconfig, :enable, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/include_recipe.rb b/lib/chefspec/api/include_recipe.rb
new file mode 100644
index 0000000..797fcb2
--- /dev/null
+++ b/lib/chefspec/api/include_recipe.rb
@@ -0,0 +1,26 @@
+module ChefSpec::API
+  # @since 0.2.1
+  module IncludeRecipeMatchers
+    #
+    # Assert that a Chef run includes a certain recipe. Given a Chef Recipe
+    # that calls +include_recipe+:
+    #
+    #     include_recipe 'apache2::default'
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +include_recipe+ directive with ChefSpec.
+    #
+    # @example Assert the +apache2::default+ recipe is included in the Chef run
+    #   expect(chef_run).to include_recipe('apache2::default')
+    #
+    #
+    # @param [String] recipe_name
+    #   the name of the recipe to be included
+    #
+    # @return [ChefSpec::Matchers::IncludeRecipeMatcher]
+    #
+    def include_recipe(recipe_name)
+      ChefSpec::Matchers::IncludeRecipeMatcher.new(recipe_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/ips_package.rb b/lib/chefspec/api/ips_package.rb
new file mode 100644
index 0000000..54cca87
--- /dev/null
+++ b/lib/chefspec/api/ips_package.rb
@@ -0,0 +1,117 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module IpsPackageMatchers
+    ChefSpec.define_matcher :ips_package
+
+    #
+    # Assert that an +ips_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as an
+    # +ips_package+:
+    #
+    #     ips_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +ips_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +ips_package+ was installed
+    #   expect(chef_run).to install_ips_package('apache2')
+    #
+    # @example Assert that an +ips_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_ips_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +ips_package+ was installed with attributes
+    #   expect(chef_run).to install_ips_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +ips_package+ was installed using a regex
+    #   expect(chef_run).to install_ips_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +ips_package+ was _not_ installed
+    #   expect(chef_run).to_not install_ips_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_ips_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ips_package, :install, resource_name)
+    end
+
+    #
+    # Assert that an +ips_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as an
+    # +ips_package+:
+    #
+    #     ips_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +ips_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +ips_package+ was removed
+    #   expect(chef_run).to remove_ips_package('apache2')
+    #
+    # @example Assert that an +ips_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_ips_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +ips_package+ was removed with attributes
+    #   expect(chef_run).to remove_ips_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +ips_package+ was removed using a regex
+    #   expect(chef_run).to remove_ips_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +ips_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_ips_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_ips_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ips_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that an +ips_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "apache2" as an
+    # +ips_package+:
+    #
+    #     ips_package 'apache2' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +ips_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +ips_package+ was upgradeed
+    #   expect(chef_run).to upgrade_ips_package('apache2')
+    #
+    # @example Assert that an +ips_package+ was upgradeed with predicate matchers
+    #   expect(chef_run).to upgrade_ips_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +ips_package+ was upgradeed with attributes
+    #   expect(chef_run).to upgrade_ips_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +ips_package+ was upgradeed using a regex
+    #   expect(chef_run).to upgrade_ips_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +ips_package+ was _not_ upgradeed
+    #   expect(chef_run).to_not upgrade_ips_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_ips_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ips_package, :upgrade, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/link.rb b/lib/chefspec/api/link.rb
new file mode 100644
index 0000000..760515a
--- /dev/null
+++ b/lib/chefspec/api/link.rb
@@ -0,0 +1,102 @@
+module ChefSpec::API
+  # @since 1.1.0
+  module LinkMatchers
+    ChefSpec.define_matcher :link
+
+    #
+    # Assert that a +link+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "/tmp" as a
+    # +link+:
+    #
+    #     link '/tmp' do
+    #       action :create
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +link+ resource with ChefSpec.
+    #
+    # @example Assert that a +link+ was createed
+    #   expect(chef_run).to create_link('/tmp')
+    #
+    # @example Assert that a +link+ was createed with predicate matchers
+    #   expect(chef_run).to create_link('/tmp').with_link_type(:hard)
+    #
+    # @example Assert that a +link+ was createed with attributes
+    #   expect(chef_run).to create_link('/tmp').with(link_type: :hard)
+    #
+    # @example Assert that a +link+ was createed using a regex
+    #   expect(chef_run).to create_link('/tmp').with(link_type: Symbol)
+    #
+    # @example Assert that a +link+ was _not_ createed
+    #   expect(chef_run).to_not create_link('/tmp')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_link(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:link, :create, resource_name)
+    end
+
+    #
+    # Assert that a +link+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "/tmp" as a
+    # +link+:
+    #
+    #     link '/tmp' do
+    #       action :delete
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +link+ resource with ChefSpec.
+    #
+    # @example Assert that a +link+ was deleteed
+    #   expect(chef_run).to delete_link('/tmp')
+    #
+    # @example Assert that a +link+ was deleteed with predicate matchers
+    #   expect(chef_run).to delete_link('/tmp').with_link_type(:hard)
+    #
+    # @example Assert that a +link+ was deleteed with attributes
+    #   expect(chef_run).to delete_link('/tmp').with(link_type: :hard)
+    #
+    # @example Assert that a +link+ was deleteed using a regex
+    #   expect(chef_run).to delete_link('/tmp').with(link_type: Symbol)
+    #
+    # @example Assert that a +link+ was _not_ deleteed
+    #   expect(chef_run).to_not delete_link('/tmp')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_link(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:link, :delete, resource_name)
+    end
+
+    #
+    # Assert that a symlink links to a specific path. This is really
+    # syntactic sugar for the following:
+    #
+    #       expect(chef_run).to create_link('/tmp/thing').with(to: '/tmp/other_thing')
+    #
+    # @example Using +link_to+ with a String path
+    #   link = chef_run.link('/tmp/thing')
+    #   expect(link).to link_to('/tmp/other_thing')
+    #
+    # @example Using +link_to+ with a regular expression
+    #   expect(link).to link_to(/\/tmp/(.+)/)
+    #
+    # @param [String, Regex] path
+    #   the path to link to
+    #
+    # @return [ChefSpec::Matchers::LinkToMatcher]
+    #
+    def link_to(path)
+      ChefSpec::Matchers::LinkToMatcher.new(path)
+    end
+  end
+end
diff --git a/lib/chefspec/api/log.rb b/lib/chefspec/api/log.rb
new file mode 100644
index 0000000..9baf187
--- /dev/null
+++ b/lib/chefspec/api/log.rb
@@ -0,0 +1,43 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module LogMatchers
+    ChefSpec.define_matcher :log
+
+    #
+    # Assert that a +log+ resource exists in the Chef run with the
+    # action +:write+. Given a Chef Recipe that writes "message" as a
+    # +log+:
+    #
+    #     log 'message' do
+    #       action :write
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +log+ resource with ChefSpec.
+    #
+    # @example Assert that a +log+ was writeed
+    #   expect(chef_run).to write_log('message')
+    #
+    # @example Assert that a +log+ was writeed with predicate matchers
+    #   expect(chef_run).to write_log('message').with_level(:info)
+    #
+    # @example Assert that a +log+ was writeed with attributes
+    #   expect(chef_run).to write_log('message').with(level: :info)
+    #
+    # @example Assert that a +log+ was writeed using a regex
+    #   expect(chef_run).to write_log('message').with(level: Symbol)
+    #
+    # @example Assert that a +log+ was _not_ writeed
+    #   expect(chef_run).to_not write_log('message')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def write_log(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:log, :write, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/macports_package.rb b/lib/chefspec/api/macports_package.rb
new file mode 100644
index 0000000..c66df18
--- /dev/null
+++ b/lib/chefspec/api/macports_package.rb
@@ -0,0 +1,154 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module MacportsPackageMatchers
+    ChefSpec.define_matcher :macports_package
+
+    #
+    # Assert that a +macports_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "community-zero" as a
+    # +macports_package+:
+    #
+    #     macports_package 'community-zero' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +macports_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +macports_package+ was installed
+    #   expect(chef_run).to install_macports_package('community-zero')
+    #
+    # @example Assert that a +macports_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_macports_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +macports_package+ was installed with attributes
+    #   expect(chef_run).to install_macports_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +macports_package+ was installed using a regex
+    #   expect(chef_run).to install_macports_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +macports_package+ was _not_ installed
+    #   expect(chef_run).to_not install_macports_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_macports_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:macports_package, :install, resource_name)
+    end
+
+    #
+    # Assert that a +macports_package+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "community-zero" as a
+    # +macports_package+:
+    #
+    #     macports_package 'community-zero' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +macports_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +macports_package+ was purgeed
+    #   expect(chef_run).to purge_macports_package('community-zero')
+    #
+    # @example Assert that a +macports_package+ was purgeed with predicate matchers
+    #   expect(chef_run).to purge_macports_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +macports_package+ was purgeed with attributes
+    #   expect(chef_run).to purge_macports_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +macports_package+ was purgeed using a regex
+    #   expect(chef_run).to purge_macports_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +macports_package+ was _not_ purgeed
+    #   expect(chef_run).to_not purge_macports_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_macports_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:macports_package, :purge, resource_name)
+    end
+
+    #
+    # Assert that a +macports_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "community-zero" as a
+    # +macports_package+:
+    #
+    #     macports_package 'community-zero' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +macports_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +macports_package+ was removeed
+    #   expect(chef_run).to remove_macports_package('community-zero')
+    #
+    # @example Assert that a +macports_package+ was removeed with predicate matchers
+    #   expect(chef_run).to remove_macports_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +macports_package+ was removeed with attributes
+    #   expect(chef_run).to remove_macports_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +macports_package+ was removeed using a regex
+    #   expect(chef_run).to remove_macports_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +macports_package+ was _not_ removeed
+    #   expect(chef_run).to_not remove_macports_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_macports_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:macports_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that a +macports_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "community-zero" as a
+    # +macports_package+:
+    #
+    #     macports_package 'community-zero' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +macports_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +macports_package+ was upgradeed
+    #   expect(chef_run).to upgrade_macports_package('community-zero')
+    #
+    # @example Assert that a +macports_package+ was upgradeed with predicate matchers
+    #   expect(chef_run).to upgrade_macports_package('community-zero').with_version('1.2.3')
+    #
+    # @example Assert that a +macports_package+ was upgradeed with attributes
+    #   expect(chef_run).to upgrade_macports_package('community-zero').with(version: '1.2.3')
+    #
+    # @example Assert that a +macports_package+ was upgradeed using a regex
+    #   expect(chef_run).to upgrade_macports_package('community-zero').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +macports_package+ was _not_ upgradeed
+    #   expect(chef_run).to_not upgrade_macports_package('community-zero')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_macports_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:macports_package, :upgrade, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/mdadm.rb b/lib/chefspec/api/mdadm.rb
new file mode 100644
index 0000000..83f0dcb
--- /dev/null
+++ b/lib/chefspec/api/mdadm.rb
@@ -0,0 +1,117 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module MdadmMatchers
+    ChefSpec.define_matcher :mdadm
+
+    #
+    # Assert that a +mdadm+ resource exists in the Chef run with the
+    # action +:assemble+. Given a Chef Recipe that assembles "/dev/md0" as a
+    # +mdadm+:
+    #
+    #     mdadm '/dev/md0' do
+    #       action :assemble
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +mdadm+ resource with ChefSpec.
+    #
+    # @example Assert that a +mdadm+ was assembled
+    #   expect(chef_run).to assemble_mdadm('/dev/md0')
+    #
+    # @example Assert that a +mdadm+ was assembled with predicate matchers
+    #   expect(chef_run).to assemble_mdadm('/dev/md0').with_devices(['/dev/sda'])
+    #
+    # @example Assert that a +mdadm+ was assembled with attributes
+    #   expect(chef_run).to assemble_mdadm('/dev/md0').with(devices: ['/dev/sda'])
+    #
+    # @example Assert that a +mdadm+ was assembled using a regex
+    #   expect(chef_run).to assemble_mdadm('/dev/md0').with(devices: '/dev/sda')
+    #
+    # @example Assert that a +mdadm+ was _not_ assembled
+    #   expect(chef_run).to_not assemble_mdadm('/dev/md0')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def assemble_mdadm(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:mdadm, :assemble, resource_name)
+    end
+
+    #
+    # Assert that a +mdadm+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "/dev/md0" as a
+    # +mdadm+:
+    #
+    #     mdadm '/dev/md0' do
+    #       action :create
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +mdadm+ resource with ChefSpec.
+    #
+    # @example Assert that a +mdadm+ was createed
+    #   expect(chef_run).to create_mdadm('/dev/md0')
+    #
+    # @example Assert that a +mdadm+ was createed with predicate matchers
+    #   expect(chef_run).to create_mdadm('/dev/md0').with_devices(['/dev/sda'])
+    #
+    # @example Assert that a +mdadm+ was createed with attributes
+    #   expect(chef_run).to create_mdadm('/dev/md0').with(devices: ['/dev/sda'])
+    #
+    # @example Assert that a +mdadm+ was createed using a regex
+    #   expect(chef_run).to create_mdadm('/dev/md0').with(devices: '/dev/sda')
+    #
+    # @example Assert that a +mdadm+ was _not_ createed
+    #   expect(chef_run).to_not create_mdadm('/dev/md0')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_mdadm(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:mdadm, :create, resource_name)
+    end
+
+    #
+    # Assert that a +mdadm+ resource exists in the Chef run with the
+    # action +:stop+. Given a Chef Recipe that stops "/dev/md0" as a
+    # +mdadm+:
+    #
+    #     mdadm '/dev/md0' do
+    #       action :stop
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +mdadm+ resource with ChefSpec.
+    #
+    # @example Assert that a +mdadm+ was stopped
+    #   expect(chef_run).to stop_mdadm('/dev/md0')
+    #
+    # @example Assert that a +mdadm+ was stopped with predicate matchers
+    #   expect(chef_run).to stop_mdadm('/dev/md0').with_devices(['/dev/sda'])
+    #
+    # @example Assert that a +mdadm+ was stopped with attributes
+    #   expect(chef_run).to stop_mdadm('/dev/md0').with(devices: ['/dev/sda'])
+    #
+    # @example Assert that a +mdadm+ was stopped using a regex
+    #   expect(chef_run).to stop_mdadm('/dev/md0').with(devices: '/dev/sda')
+    #
+    # @example Assert that a +mdadm+ was _not_ stopped
+    #   expect(chef_run).to_not stop_mdadm('/dev/md0')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def stop_mdadm(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:mdadm, :stop, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/mount.rb b/lib/chefspec/api/mount.rb
new file mode 100644
index 0000000..de27a04
--- /dev/null
+++ b/lib/chefspec/api/mount.rb
@@ -0,0 +1,192 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module MountMatchers
+    ChefSpec.define_matcher :mount
+
+    #
+    # Assert that a +mount+ resource exists in the Chef run with the
+    # action +:disable+. Given a Chef Recipe that disables "/mnt/volume1" as a
+    # +mount+:
+    #
+    #     mount '/mnt/volume1' do
+    #       action :disable
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +mount+ resource with ChefSpec.
+    #
+    # @example Assert that a +mount+ was disableed
+    #   expect(chef_run).to disable_mount('/mnt/volume1')
+    #
+    # @example Assert that a +mount+ was disableed with predicate matchers
+    #   expect(chef_run).to disable_mount('/mnt/volume1').with_pass(4)
+    #
+    # @example Assert that a +mount+ was disableed with attributes
+    #   expect(chef_run).to disable_mount('/mnt/volume1').with(pass: 4)
+    #
+    # @example Assert that a +mount+ was disableed using a regex
+    #   expect(chef_run).to disable_mount('/mnt/volume1').with(pass: /\d+/)
+    #
+    # @example Assert that a +mount+ was _not_ disableed
+    #   expect(chef_run).to_not disable_mount('/mnt/volume1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def disable_mount(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:mount, :disable, resource_name)
+    end
+
+    #
+    # Assert that a +mount+ resource exists in the Chef run with the
+    # action +:enable+. Given a Chef Recipe that enables "/mnt/volume1" as a
+    # +mount+:
+    #
+    #     mount '/mnt/volume1' do
+    #       action :enable
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +mount+ resource with ChefSpec.
+    #
+    # @example Assert that a +mount+ was enabled
+    #   expect(chef_run).to enable_mount('/mnt/volume1')
+    #
+    # @example Assert that a +mount+ was enabled with predicate matchers
+    #   expect(chef_run).to enable_mount('/mnt/volume1').with_pass(4)
+    #
+    # @example Assert that a +mount+ was enabled with attributes
+    #   expect(chef_run).to enable_mount('/mnt/volume1').with(pass: 4)
+    #
+    # @example Assert that a +mount+ was enabled using a regex
+    #   expect(chef_run).to enable_mount('/mnt/volume1').with(pass: /\d+/)
+    #
+    # @example Assert that a +mount+ was _not_ enabled
+    #   expect(chef_run).to_not enable_mount('/mnt/volume1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def enable_mount(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:mount, :enable, resource_name)
+    end
+
+    #
+    # Assert that a +mount+ resource exists in the Chef run with the
+    # action +:mount+. Given a Chef Recipe that mounts "/mnt/volume1" as a
+    # +mount+:
+    #
+    #     mount '/mnt/volume1' do
+    #       action :mount
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +mount+ resource with ChefSpec.
+    #
+    # @example Assert that a +mount+ was mounted
+    #   expect(chef_run).to mount_mount('/mnt/volume1')
+    #
+    # @example Assert that a +mount+ was mounted with predicate matchers
+    #   expect(chef_run).to mount_mount('/mnt/volume1').with_pass(4)
+    #
+    # @example Assert that a +mount+ was mounted with attributes
+    #   expect(chef_run).to mount_mount('/mnt/volume1').with(pass: 4)
+    #
+    # @example Assert that a +mount+ was mounted using a regex
+    #   expect(chef_run).to mount_mount('/mnt/volume1').with(pass: /\d+/)
+    #
+    # @example Assert that a +mount+ was _not_ mounted
+    #   expect(chef_run).to_not mount_mount('/mnt/volume1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def mount_mount(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:mount, :mount, resource_name)
+    end
+
+    #
+    # Assert that a +mount+ resource exists in the Chef run with the
+    # action +:remount+. Given a Chef Recipe that remounts "/mnt/volume1" as a
+    # +mount+:
+    #
+    #     mount '/mnt/volume1' do
+    #       action :remount
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +mount+ resource with ChefSpec.
+    #
+    # @example Assert that a +mount+ was remounted
+    #   expect(chef_run).to remount_mount('/mnt/volume1')
+    #
+    # @example Assert that a +mount+ was remounted with predicate matchers
+    #   expect(chef_run).to remount_mount('/mnt/volume1').with_pass(4)
+    #
+    # @example Assert that a +mount+ was remounted with attributes
+    #   expect(chef_run).to remount_mount('/mnt/volume1').with(pass: 4)
+    #
+    # @example Assert that a +mount+ was remounted using a regex
+    #   expect(chef_run).to remount_mount('/mnt/volume1').with(pass: /\d+/)
+    #
+    # @example Assert that a +mount+ was _not_ remounted
+    #   expect(chef_run).to_not remount_mount('/mnt/volume1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remount_mount(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:mount, :remount, resource_name)
+    end
+
+    #
+    # Assert that a +mount+ resource exists in the Chef run with the
+    # action +:umount+. Given a Chef Recipe that umounts "/mnt/volume1" as a
+    # +mount+:
+    #
+    #     mount '/mnt/volume1' do
+    #       action :umount
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +mount+ resource with ChefSpec.
+    #
+    # @example Assert that a +mount+ was umounted
+    #   expect(chef_run).to umount_mount('/mnt/volume1')
+    #
+    # @example Assert that a +mount+ was umounted with predicate matchers
+    #   expect(chef_run).to umount_mount('/mnt/volume1').with_pass(4)
+    #
+    # @example Assert that a +mount+ was umounted with attributes
+    #   expect(chef_run).to umount_mount('/mnt/volume1').with(pass: 4)
+    #
+    # @example Assert that a +mount+ was umounted using a regex
+    #   expect(chef_run).to umount_mount('/mnt/volume1').with(pass: /\d+/)
+    #
+    # @example Assert that a +mount+ was _not_ umounted
+    #   expect(chef_run).to_not umount_mount('/mnt/volume1')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def umount_mount(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:mount, :umount, resource_name)
+    end
+
+  end
+end
diff --git a/lib/chefspec/api/notifications.rb b/lib/chefspec/api/notifications.rb
new file mode 100644
index 0000000..4ea89ad
--- /dev/null
+++ b/lib/chefspec/api/notifications.rb
@@ -0,0 +1,38 @@
+module ChefSpec::API
+  # @since 0.9.0
+  module NotificationsMatchers
+    #
+    # Assert that a resource notifies another. Given a Chef Recipe that
+    # notifies a template resource to restart apache:
+    #
+    #     template '/etc/apache2/config' do
+    #       notifies :restart, 'service[apache2]'
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # notifications on a resource with ChefSpec.
+    #
+    # @example Assert the template notifies apache of something
+    #   template = chef_run.template('/etc/apache2.conf')
+    #   expect(template).to notify('service[apache2]')
+    #
+    # @example Assert the template notifies apache to restart
+    #   expect(template).to notify('service[apache2]').to(:restart)
+    #
+    # @example Assert the template notifies apache to restart immediately
+    #   expect(template).to notify('service[apache2]').to(:restart).immediately
+    #
+    # @example Assert the template notifies apache to restart delayed
+    #   expect(template).to notify('service[apache2]').to(:restart).delayed
+    #
+    #
+    # @param [String] signature
+    #   the signature of the notification to match
+    #
+    # @return [ChefSpec::Matchers::NotificationsMatcher]
+    #
+    def notify(signature)
+      ChefSpec::Matchers::NotificationsMatcher.new(signature)
+    end
+  end
+end
diff --git a/lib/chefspec/api/ohai.rb b/lib/chefspec/api/ohai.rb
new file mode 100644
index 0000000..4b6350e
--- /dev/null
+++ b/lib/chefspec/api/ohai.rb
@@ -0,0 +1,43 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module OhaiMatchers
+    ChefSpec.define_matcher :ohai
+
+    #
+    # Assert that an +ohai+ resource exists in the Chef run with the
+    # action +:reload+. Given a Chef Recipe that reloads "reload" as an
+    # +ohai+:
+    #
+    #     ohai 'reload' do
+    #       action :reload
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +ohai+ resource with ChefSpec.
+    #
+    # @example Assert that an +ohai+ was reloaded
+    #   expect(chef_run).to reload_ohai('reload')
+    #
+    # @example Assert that an +ohai+ was reloaded with predicate matchers
+    #   expect(chef_run).to reload_ohai('reload').with_system(true)
+    #
+    # @example Assert that an +ohai+ was reloaded with attributes
+    #   expect(chef_run).to reload_ohai('reload').with(system: true)
+    #
+    # @example Assert that an +ohai+ was reloaded using a regex
+    #   expect(chef_run).to reload_ohai('reload').with(system: /true/)
+    #
+    # @example Assert that an +ohai+ was _not_ reloaded
+    #   expect(chef_run).to_not reload_ohai('reload')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def reload_ohai(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ohai, :reload, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/package.rb b/lib/chefspec/api/package.rb
new file mode 100644
index 0000000..ddeacef
--- /dev/null
+++ b/lib/chefspec/api/package.rb
@@ -0,0 +1,192 @@
+module ChefSpec::API
+  # @since 0.0.1
+  module PackageMatchers
+    ChefSpec.define_matcher :package
+
+    #
+    # Assert that an +package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as an
+    # +package+:
+    #
+    #     package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +package+ resource with ChefSpec.
+    #
+    # @example Assert that an +package+ was installed
+    #   expect(chef_run).to install_package('apache2')
+    #
+    # @example Assert that an +package+ was installed with predicate matchers
+    #   expect(chef_run).to install_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +package+ was installed with attributes
+    #   expect(chef_run).to install_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +package+ was installed using a regex
+    #   expect(chef_run).to install_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +package+ was _not_ installed
+    #   expect(chef_run).to_not install_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:package, :install, resource_name)
+    end
+
+    #
+    # Assert that an +package+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "apache2" as an
+    # +package+:
+    #
+    #     package 'apache2' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +package+ resource with ChefSpec.
+    #
+    # @example Assert that an +package+ was purged
+    #   expect(chef_run).to purge_package('apache2')
+    #
+    # @example Assert that an +package+ was purged with predicate matchers
+    #   expect(chef_run).to purge_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +package+ was purged with attributes
+    #   expect(chef_run).to purge_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +package+ was purged using a regex
+    #   expect(chef_run).to purge_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +package+ was _not_ purged
+    #   expect(chef_run).to_not purge_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:package, :purge, resource_name)
+    end
+
+    #
+    # Assert that an +package+ resource exists in the Chef run with the
+    # action +:reconfig+. Given a Chef Recipe that reconfigures "apache2" as an
+    # +package+:
+    #
+    #     package 'apache2' do
+    #       action :reconfig
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +package+ resource with ChefSpec.
+    #
+    # @example Assert that an +package+ was reconfigured
+    #   expect(chef_run).to reconfig_package('apache2')
+    #
+    # @example Assert that an +package+ was reconfigured with predicate matchers
+    #   expect(chef_run).to reconfig_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +package+ was reconfigured with attributes
+    #   expect(chef_run).to reconfig_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +package+ was reconfigured using a regex
+    #   expect(chef_run).to reconfig_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +package+ was _not_ reconfigured
+    #   expect(chef_run).to_not reconfig_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def reconfig_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:package, :reconfig, resource_name)
+    end
+
+    #
+    # Assert that an +package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as an
+    # +package+:
+    #
+    #     package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +package+ resource with ChefSpec.
+    #
+    # @example Assert that an +package+ was removed
+    #   expect(chef_run).to remove_package('apache2')
+    #
+    # @example Assert that an +package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +package+ was removed with attributes
+    #   expect(chef_run).to remove_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +package+ was removed using a regex
+    #   expect(chef_run).to remove_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +package+ was _not_ removed
+    #   expect(chef_run).to_not remove_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:package, :remove, resource_name)
+    end
+
+    #
+    # Assert that an +package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "apache2" as an
+    # +package+:
+    #
+    #     package 'apache2' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +package+ resource with ChefSpec.
+    #
+    # @example Assert that an +package+ was upgraded
+    #   expect(chef_run).to upgrade_package('apache2')
+    #
+    # @example Assert that an +package+ was upgraded with predicate matchers
+    #   expect(chef_run).to upgrade_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +package+ was upgraded with attributes
+    #   expect(chef_run).to upgrade_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +package+ was upgraded using a regex
+    #   expect(chef_run).to upgrade_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +package+ was _not_ upgraded
+    #   expect(chef_run).to_not upgrade_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:package, :upgrade, resource_name)
+    end
+
+  end
+end
diff --git a/lib/chefspec/api/pacman_package.rb b/lib/chefspec/api/pacman_package.rb
new file mode 100644
index 0000000..723bb5b
--- /dev/null
+++ b/lib/chefspec/api/pacman_package.rb
@@ -0,0 +1,155 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module PacmanPackageMatchers
+    ChefSpec.define_matcher :pacman_package
+
+    #
+    # Assert that an +pacman_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as an
+    # +pacman_package+:
+    #
+    #     pacman_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +pacman_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +pacman_package+ was installed
+    #   expect(chef_run).to install_pacman_package('apache2')
+    #
+    # @example Assert that an +pacman_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_pacman_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +pacman_package+ was installed with attributes
+    #   expect(chef_run).to install_pacman_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +pacman_package+ was installed using a regex
+    #   expect(chef_run).to install_pacman_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +pacman_package+ was _not_ installed
+    #   expect(chef_run).to_not install_pacman_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_pacman_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:pacman_package, :install, resource_name)
+    end
+
+    #
+    # Assert that an +pacman_package+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "apache2" as an
+    # +pacman_package+:
+    #
+    #     pacman_package 'apache2' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +pacman_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +pacman_package+ was purged
+    #   expect(chef_run).to purge_pacman_package('apache2')
+    #
+    # @example Assert that an +pacman_package+ was purged with predicate matchers
+    #   expect(chef_run).to purge_pacman_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +pacman_package+ was purged with attributes
+    #   expect(chef_run).to purge_pacman_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +pacman_package+ was purged using a regex
+    #   expect(chef_run).to purge_pacman_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +pacman_package+ was _not_ purged
+    #   expect(chef_run).to_not purge_pacman_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_pacman_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:pacman_package, :purge, resource_name)
+    end
+
+    #
+    # Assert that an +pacman_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as an
+    # +pacman_package+:
+    #
+    #     pacman_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +pacman_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +pacman_package+ was removed
+    #   expect(chef_run).to remove_pacman_package('apache2')
+    #
+    # @example Assert that an +pacman_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_pacman_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +pacman_package+ was removed with attributes
+    #   expect(chef_run).to remove_pacman_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +pacman_package+ was removed using a regex
+    #   expect(chef_run).to remove_pacman_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +pacman_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_pacman_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_pacman_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:pacman_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that an +pacman_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "apache2" as an
+    # +pacman_package+:
+    #
+    #     pacman_package 'apache2' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +pacman_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +pacman_package+ was upgraded
+    #   expect(chef_run).to upgrade_pacman_package('apache2')
+    #
+    # @example Assert that an +pacman_package+ was upgraded with predicate matchers
+    #   expect(chef_run).to upgrade_pacman_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +pacman_package+ was upgraded with attributes
+    #   expect(chef_run).to upgrade_pacman_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +pacman_package+ was upgraded using a regex
+    #   expect(chef_run).to upgrade_pacman_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +pacman_package+ was _not_ upgraded
+    #   expect(chef_run).to_not upgrade_pacman_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_pacman_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:pacman_package, :upgrade, resource_name)
+    end
+
+  end
+end
diff --git a/lib/chefspec/api/portage_package.rb b/lib/chefspec/api/portage_package.rb
new file mode 100644
index 0000000..0270fd7
--- /dev/null
+++ b/lib/chefspec/api/portage_package.rb
@@ -0,0 +1,155 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module PortagePackageMatchers
+    ChefSpec.define_matcher :portage_package
+
+    #
+    # Assert that an +portage_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as an
+    # +portage_package+:
+    #
+    #     portage_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +portage_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +portage_package+ was installed
+    #   expect(chef_run).to install_portage_package('apache2')
+    #
+    # @example Assert that an +portage_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_portage_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +portage_package+ was installed with attributes
+    #   expect(chef_run).to install_portage_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +portage_package+ was installed using a regex
+    #   expect(chef_run).to install_portage_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +portage_package+ was _not_ installed
+    #   expect(chef_run).to_not install_portage_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_portage_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:portage_package, :install, resource_name)
+    end
+
+    #
+    # Assert that an +portage_package+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "apache2" as an
+    # +portage_package+:
+    #
+    #     portage_package 'apache2' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +portage_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +portage_package+ was purged
+    #   expect(chef_run).to purge_portage_package('apache2')
+    #
+    # @example Assert that an +portage_package+ was purged with predicate matchers
+    #   expect(chef_run).to purge_portage_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +portage_package+ was purged with attributes
+    #   expect(chef_run).to purge_portage_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +portage_package+ was purged using a regex
+    #   expect(chef_run).to purge_portage_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +portage_package+ was _not_ purged
+    #   expect(chef_run).to_not purge_portage_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_portage_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:portage_package, :purge, resource_name)
+    end
+
+    #
+    # Assert that an +portage_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as an
+    # +portage_package+:
+    #
+    #     portage_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +portage_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +portage_package+ was removed
+    #   expect(chef_run).to remove_portage_package('apache2')
+    #
+    # @example Assert that an +portage_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_portage_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +portage_package+ was removed with attributes
+    #   expect(chef_run).to remove_portage_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +portage_package+ was removed using a regex
+    #   expect(chef_run).to remove_portage_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +portage_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_portage_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_portage_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:portage_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that an +portage_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "apache2" as an
+    # +portage_package+:
+    #
+    #     portage_package 'apache2' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test an
+    # +portage_package+ resource with ChefSpec.
+    #
+    # @example Assert that an +portage_package+ was upgraded
+    #   expect(chef_run).to upgrade_portage_package('apache2')
+    #
+    # @example Assert that an +portage_package+ was upgraded with predicate matchers
+    #   expect(chef_run).to upgrade_portage_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that an +portage_package+ was upgraded with attributes
+    #   expect(chef_run).to upgrade_portage_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that an +portage_package+ was upgraded using a regex
+    #   expect(chef_run).to upgrade_portage_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that an +portage_package+ was _not_ upgraded
+    #   expect(chef_run).to_not upgrade_portage_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_portage_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:portage_package, :upgrade, resource_name)
+    end
+
+  end
+end
diff --git a/lib/chefspec/api/powershell_script.rb b/lib/chefspec/api/powershell_script.rb
new file mode 100644
index 0000000..28a3751
--- /dev/null
+++ b/lib/chefspec/api/powershell_script.rb
@@ -0,0 +1,43 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module PowershellScriptMatchers
+    ChefSpec.define_matcher :powershell_script
+
+    #
+    # Assert that a +powershell_script+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "/tmp" as a
+    # +powershell_script+:
+    #
+    #     powershell_script '/tmp' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +powershell_script+ resource with ChefSpec.
+    #
+    # @example Assert that a +powershell_script+ was run
+    #   expect(chef_run).to run_powershell_script('/tmp')
+    #
+    # @example Assert that a +powershell_script+ was run with predicate matchers
+    #   expect(chef_run).to run_powershell_script('/tmp').with_user('svargo')
+    #
+    # @example Assert that a +powershell_script+ was run with attributes
+    #   expect(chef_run).to run_powershell_script('/tmp').with(user: 'svargo')
+    #
+    # @example Assert that a +powershell_script+ was run using a regex
+    #   expect(chef_run).to run_powershell_script('/tmp').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +powershell_script+ was _not_ run
+    #   expect(chef_run).to_not run_powershell_script('/tmp')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_powershell_script(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:powershell_script, :run, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/reboot.rb b/lib/chefspec/api/reboot.rb
new file mode 100644
index 0000000..d187cb4
--- /dev/null
+++ b/lib/chefspec/api/reboot.rb
@@ -0,0 +1,18 @@
+module ChefSpec::API
+  # @since 0.5.0
+  module RebootMatchers
+    ChefSpec.define_matcher :reboot
+
+    def now_reboot(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:reboot, :reboot_now, resource_name)
+    end
+
+    def request_reboot(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:reboot, :request_reboot, resource_name)
+    end
+
+    def cancel_reboot(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:reboot, :cancel, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/registry_key.rb b/lib/chefspec/api/registry_key.rb
new file mode 100644
index 0000000..415d2e4
--- /dev/null
+++ b/lib/chefspec/api/registry_key.rb
@@ -0,0 +1,166 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module RegistryKeyMatchers
+    ChefSpec.define_matcher :registry_key
+
+    #
+    # Assert that a +registry_key+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "HKEY_LOCAL_MACHINE\\SOFTWARE" as a
+    # +registry_key+:
+    #
+    #     registry_key 'HKEY_LOCAL_MACHINE\\SOFTWARE' do
+    #       action :create
+    #     end
+    #
+    # To test the content rendered by a +registry_key+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +registry_key+ resource with ChefSpec.
+    #
+    # @example Assert that a +registry_key+ was created
+    #   expect(chef_run).to create_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE')
+    #
+    # @example Assert that a +registry_key+ was created with predicate matchers
+    #   expect(chef_run).to create_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with_recursive(false)
+    #
+    # @example Assert that a +registry_key+ was created with attributes
+    #   expect(chef_run).to create_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with(recursive: false)
+    #
+    # @example Assert that a +registry_key+ was created using a regex
+    #   expect(chef_run).to create_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with(values: Array)
+    #
+    # @example Assert that a +registry_key+ was _not_ created
+    #   expect(chef_run).to_not create_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_registry_key(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:registry_key, :create, resource_name)
+    end
+
+    #
+    # Assert that a +registry_key+ resource exists in the Chef run with the
+    # action +:create_if_missing+. Given a Chef Recipe that creates "HKEY_LOCAL_MACHINE\\SOFTWARE"
+    # if missing as a +registry_key+:
+    #
+    #     registry_key 'HKEY_LOCAL_MACHINE\\SOFTWARE' do
+    #       action :create_if_missing
+    #     end
+    #
+    # To test the content rendered by a +registry_key+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +registry_key+ resource with ChefSpec.
+    #
+    # @example Assert that a +registry_key+ was created if missing
+    #   expect(chef_run).to create_registry_key_if_missing('HKEY_LOCAL_MACHINE\\SOFTWARE')
+    #
+    # @example Assert that a +registry_key+ was created if missing with predicate matchers
+    #   expect(chef_run).to create_registry_key_if_missing('HKEY_LOCAL_MACHINE\\SOFTWARE').with_recursive(false)
+    #
+    # @example Assert that a +registry_key+ was created if missing with attributes
+    #   expect(chef_run).to create_registry_key_if_missing('HKEY_LOCAL_MACHINE\\SOFTWARE').with(recursive: false)
+    #
+    # @example Assert that a +registry_key+ was created if missing using a regex
+    #   expect(chef_run).to create_registry_key_if_missing('HKEY_LOCAL_MACHINE\\SOFTWARE').with(values: Array)
+    #
+    # @example Assert that a +registry_key+ was _not_ created if missing
+    #   expect(chef_run).to_not create_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_registry_key_if_missing(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:registry_key, :create_if_missing, resource_name)
+    end
+
+    #
+    # Assert that a +registry_key+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "HKEY_LOCAL_MACHINE\\SOFTWARE" as a
+    # +registry_key+:
+    #
+    #     registry_key 'HKEY_LOCAL_MACHINE\\SOFTWARE' do
+    #       action :delete
+    #     end
+    #
+    # To test the content rendered by a +registry_key+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +registry_key+ resource with ChefSpec.
+    #
+    # @example Assert that a +registry_key+ was deleted
+    #   expect(chef_run).to delete_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE')
+    #
+    # @example Assert that a +registry_key+ was deleted with predicate matchers
+    #   expect(chef_run).to delete_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with_recursive(false)
+    #
+    # @example Assert that a +registry_key+ was deleted with attributes
+    #   expect(chef_run).to delete_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with(recursive: false)
+    #
+    # @example Assert that a +registry_key+ was deleted using a regex
+    #   expect(chef_run).to delete_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with(values: Array)
+    #
+    # @example Assert that a +registry_key+ was _not_ deleted
+    #   expect(chef_run).to_not delete_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_registry_key(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:registry_key, :delete, resource_name)
+    end
+
+    #
+    # Assert that a +registry_key+ resource exists in the Chef run with the
+    # action +:delete_key+. Given a Chef Recipe that delete_keys "HKEY_LOCAL_MACHINE\\SOFTWARE" as a
+    # +registry_key+:
+    #
+    #     registry_key 'HKEY_LOCAL_MACHINE\\SOFTWARE' do
+    #       action :delete_key
+    #     end
+    #
+    # To test the content rendered by a +registry_key+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +registry_key+ resource with ChefSpec.
+    #
+    # @example Assert that a +registry_key+ was delete_keyd
+    #   expect(chef_run).to delete_key_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE')
+    #
+    # @example Assert that a +registry_key+ was delete_keyd with predicate matchers
+    #   expect(chef_run).to delete_key_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with_recursive(false)
+    #
+    # @example Assert that a +registry_key+ was delete_keyd with attributes
+    #   expect(chef_run).to delete_key_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with(recursive: false)
+    #
+    # @example Assert that a +registry_key+ was delete_keyd using a regex
+    #   expect(chef_run).to delete_key_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE').with(values: Array)
+    #
+    # @example Assert that a +registry_key+ was _not_ delete_keyd
+    #   expect(chef_run).to_not delete_key_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_key_registry_key(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:registry_key, :delete_key, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/remote_directory.rb b/lib/chefspec/api/remote_directory.rb
new file mode 100644
index 0000000..58daf1e
--- /dev/null
+++ b/lib/chefspec/api/remote_directory.rb
@@ -0,0 +1,120 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module RemoteDirectoryMatchers
+    ChefSpec.define_matcher :remote_directory
+
+    #
+    # Assert that a +remote_directory+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "/tmp" as a
+    # +remote_directory+:
+    #
+    #     remote_directory '/tmp' do
+    #       action :create
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +remote_directory+ resource with ChefSpec.
+    #
+    # @example Assert that a +remote_directory+ was createed
+    #   expect(chef_run).to create_remote_directory('/tmp')
+    #
+    # @example Assert that a +remote_directory+ was createed with predicate matchers
+    #   expect(chef_run).to create_remote_directory('/tmp').with_overwrite(true)
+    #
+    # @example Assert that a +remote_directory+ was createed with attributes
+    #   expect(chef_run).to create_remote_directory('/tmp').with(overwrite: true)
+    #
+    # @example Assert that a +remote_directory+ was createed using a regex
+    #   expect(chef_run).to create_remote_directory('/tmp').with(overwrite: /true/)
+    #
+    # @example Assert that a +remote_directory+ was _not_ createed
+    #   expect(chef_run).to_not create_remote_directory('/tmp')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_remote_directory(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:remote_directory, :create, resource_name)
+    end
+
+    #
+    # Assert that a +remote_directory+ resource exists in the Chef run with the
+    # action +:create_if_missing+. Given a Chef Recipe that creates "/tmp/config"
+    # if missing as a +remote_directory+:
+    #
+    #     remote_directory '/tmp/config' do
+    #       action :create_if_missing
+    #     end
+    #
+    # To test the content rendered by a +remote_directory+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +remote_directory+ resource with ChefSpec.
+    #
+    # @example Assert that a +remote_directory+ was created if missing
+    #   expect(chef_run).to create_remote_directory_if_missing('/tmp/config')
+    #
+    # @example Assert that a +remote_directory+ was created if missing with predicate matchers
+    #   expect(chef_run).to create_remote_directory_if_missing('/tmp/config').with_overwrite(true)
+    #
+    # @example Assert that a +remote_directory+ was created if missing with attributes
+    #   expect(chef_run).to create_remote_directory_if_missing('/tmp/config').with(overwrite: true)
+    #
+    # @example Assert that a +remote_directory+ was created if missing using a regex
+    #   expect(chef_run).to create_remote_directory_if_missing('/tmp/config').with(overwrite: /true/)
+    #
+    # @example Assert that a +remote_directory+ was _not_ created if missing
+    #   expect(chef_run).to_not create_remote_directory('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_remote_directory_if_missing(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:remote_directory, :create_if_missing, resource_name)
+    end
+
+    #
+    # Assert that a +remote_directory+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "/tmp" as a
+    # +remote_directory+:
+    #
+    #     remote_directory '/tmp' do
+    #       action :delete
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +remote_directory+ resource with ChefSpec.
+    #
+    # @example Assert that a +remote_directory+ was deleteed
+    #   expect(chef_run).to delete_remote_directory('/tmp')
+    #
+    # @example Assert that a +remote_directory+ was deleteed with predicate matchers
+    #   expect(chef_run).to delete_remote_directory('/tmp').with_overwrite(true)
+    #
+    # @example Assert that a +remote_directory+ was deleteed with attributes
+    #   expect(chef_run).to delete_remote_directory('/tmp').with(overwrite: true)
+    #
+    # @example Assert that a +remote_directory+ was deleteed using a regex
+    #   expect(chef_run).to delete_remote_directory('/tmp').with(overwrite: /true/)
+    #
+    # @example Assert that a +remote_directory+ was _not_ deleteed
+    #   expect(chef_run).to_not delete_remote_directory('/tmp')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_remote_directory(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:remote_directory, :delete, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/remote_file.rb b/lib/chefspec/api/remote_file.rb
new file mode 100644
index 0000000..8d92582
--- /dev/null
+++ b/lib/chefspec/api/remote_file.rb
@@ -0,0 +1,166 @@
+module ChefSpec::API
+  # @since 1.0.0
+  module RemoteFileMatchers
+    ChefSpec.define_matcher :remote_file
+
+    #
+    # Assert that a +remote_file+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "/tmp/config" as a
+    # +remote_file+:
+    #
+    #     remote_file '/tmp/config' do
+    #       action :create
+    #     end
+    #
+    # To test the content rendered by a +remote_file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +remote_file+ resource with ChefSpec.
+    #
+    # @example Assert that a +remote_file+ was created
+    #   expect(chef_run).to create_remote_file('/tmp/config')
+    #
+    # @example Assert that a +remote_file+ was created with predicate matchers
+    #   expect(chef_run).to create_remote_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +remote_file+ was created with attributes
+    #   expect(chef_run).to create_remote_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +remote_file+ was created using a regex
+    #   expect(chef_run).to create_remote_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +remote_file+ was _not_ created
+    #   expect(chef_run).to_not create_remote_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_remote_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:remote_file, :create, resource_name)
+    end
+
+    #
+    # Assert that a +remote_file+ resource exists in the Chef run with the
+    # action +:create_if_missing+. Given a Chef Recipe that creates "/tmp/config"
+    # if missing as a +remote_file+:
+    #
+    #     remote_file '/tmp/config' do
+    #       action :create_if_missing
+    #     end
+    #
+    # To test the content rendered by a +remote_file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +remote_file+ resource with ChefSpec.
+    #
+    # @example Assert that a +remote_file+ was created if missing
+    #   expect(chef_run).to create_remote_file_if_missing('/tmp/config')
+    #
+    # @example Assert that a +remote_file+ was created if missing with predicate matchers
+    #   expect(chef_run).to create_remote_file_if_missing('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +remote_file+ was created if missing with attributes
+    #   expect(chef_run).to create_remote_file_if_missing('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +remote_file+ was created if missing using a regex
+    #   expect(chef_run).to create_remote_file_if_missing('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +remote_file+ was _not_ created if missing
+    #   expect(chef_run).to_not create_remote_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_remote_file_if_missing(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:remote_file, :create_if_missing, resource_name)
+    end
+
+    #
+    # Assert that a +remote_file+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "/tmp/config" as a
+    # +remote_file+:
+    #
+    #     remote_file '/tmp/config' do
+    #       action :delete
+    #     end
+    #
+    # To test the content rendered by a +remote_file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +remote_file+ resource with ChefSpec.
+    #
+    # @example Assert that a +remote_file+ was deleted
+    #   expect(chef_run).to delete_remote_file('/tmp/config')
+    #
+    # @example Assert that a +remote_file+ was deleted with predicate matchers
+    #   expect(chef_run).to delete_remote_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +remote_file+ was deleted with attributes
+    #   expect(chef_run).to delete_remote_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +remote_file+ was deleted using a regex
+    #   expect(chef_run).to delete_remote_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +remote_file+ was _not_ deleted
+    #   expect(chef_run).to_not delete_remote_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_remote_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:remote_file, :delete, resource_name)
+    end
+
+    #
+    # Assert that a +remote_file+ resource exists in the Chef run with the
+    # action +:touch+. Given a Chef Recipe that touches "/tmp/config" as a
+    # +remote_file+:
+    #
+    #     remote_file '/tmp/config' do
+    #       action :touch
+    #     end
+    #
+    # To test the content rendered by a +remote_file+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +remote_file+ resource with ChefSpec.
+    #
+    # @example Assert that a +remote_file+ was touched
+    #   expect(chef_run).to touch_remote_file('/tmp/config')
+    #
+    # @example Assert that a +remote_file+ was touched with predicate matchers
+    #   expect(chef_run).to touch_remote_file('/tmp/config').with_backup(false)
+    #
+    # @example Assert that a +remote_file+ was touched with attributes
+    #   expect(chef_run).to touch_remote_file('/tmp/config').with(backup: false)
+    #
+    # @example Assert that a +remote_file+ was touched using a regex
+    #   expect(chef_run).to touch_remote_file('/tmp/config').with(user: /apa(.+)/)
+    #
+    # @example Assert that a +remote_file+ was _not_ touched
+    #   expect(chef_run).to_not touch_remote_file('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def touch_remote_file(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:remote_file, :touch, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/render_file.rb b/lib/chefspec/api/render_file.rb
new file mode 100644
index 0000000..9265b34
--- /dev/null
+++ b/lib/chefspec/api/render_file.rb
@@ -0,0 +1,35 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module RenderFileMatchers
+    #
+    # Assert that a file is rendered by the Chef run. This matcher works for
+    # +template+, +file+, and +cookbook_file+ resources. The content from the
+    # resource must be convertable to a string; verifying the content of a
+    # binary file is not permissible at this time.
+    #
+    # @example Assert a template is rendered
+    #   expect(chef_run).to render_file('/etc/foo')
+    #
+    # @example Assert a template is rendered with certain content
+    #   expect(template).to render_file('/etc/foo').with_content('This is a file')
+    #
+    # @example Assert a template is rendered with matching content
+    #   expect(template).to render_file('/etc/foo').with_content(/^This(.+)$/)
+    #
+    # @example Assert a template is rendered with content matching any RSpec matcher
+    #   expect(template).to render_file('/etc/foo').with_content(starts_with('This'))
+    #
+    # @example Assert a partial path to a template is rendered with matching content
+    #   expect(template).to render_file(/\/etc\/foo-(\d+)$/).with_content(/^This(.+)$/)
+    #
+    #
+    # @param [String] path
+    #   the path of the file to render
+    #
+    # @return [ChefSpec::Matchers::RenderFileMatcher]
+    #
+    def render_file(path)
+      ChefSpec::Matchers::RenderFileMatcher.new(path)
+    end
+  end
+end
diff --git a/lib/chefspec/api/route.rb b/lib/chefspec/api/route.rb
new file mode 100644
index 0000000..abba7b6
--- /dev/null
+++ b/lib/chefspec/api/route.rb
@@ -0,0 +1,80 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module RouteMatchers
+    ChefSpec.define_matcher :route
+
+    #
+    # Assert that a +route+ resource exists in the Chef run with the
+    # action +:add+. Given a Chef Recipe that adds "10.0.0.10/32" as a
+    # +route+:
+    #
+    #     route '10.0.0.10/32' do
+    #       action :add
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +route+ resource with ChefSpec.
+    #
+    # @example Assert that a +route+ was added
+    #   expect(chef_run).to add_route('10.0.0.10/32')
+    #
+    # @example Assert that a +route+ was added with predicate matchers
+    #   expect(chef_run).to add_route('10.0.0.10/32').with_device('eth0')
+    #
+    # @example Assert that a +route+ was added with attributes
+    #   expect(chef_run).to add_route('10.0.0.10/32').with(device: 'eth0')
+    #
+    # @example Assert that a +route+ was added using a regex
+    #   expect(chef_run).to add_route('10.0.0.10/32').with(device: /eth(\d+)/)
+    #
+    # @example Assert that a +route+ was _not_ added
+    #   expect(chef_run).to_not add_route('10.0.0.10/32')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def add_route(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:route, :add, resource_name)
+    end
+
+    #
+    # Assert that a +route+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "10.0.0.10/32" as a
+    # +route+:
+    #
+    #     route '10.0.0.10/32' do
+    #       action :delete
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +route+ resource with ChefSpec.
+    #
+    # @example Assert that a +route+ was deleteed
+    #   expect(chef_run).to delete_route('10.0.0.10/32')
+    #
+    # @example Assert that a +route+ was deleteed with predicate matchers
+    #   expect(chef_run).to delete_route('10.0.0.10/32').with_device('eth0')
+    #
+    # @example Assert that a +route+ was deleteed with attributes
+    #   expect(chef_run).to delete_route('10.0.0.10/32').with(device: 'eth0')
+    #
+    # @example Assert that a +route+ was deleteed using a regex
+    #   expect(chef_run).to delete_route('10.0.0.10/32').with(device: /eth(\d+)/)
+    #
+    # @example Assert that a +route+ was _not_ deleteed
+    #   expect(chef_run).to_not delete_route('10.0.0.10/32')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_route(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:route, :delete, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/rpm_package.rb b/lib/chefspec/api/rpm_package.rb
new file mode 100644
index 0000000..0f10853
--- /dev/null
+++ b/lib/chefspec/api/rpm_package.rb
@@ -0,0 +1,117 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module RpmPackageMatchers
+    ChefSpec.define_matcher :rpm_package
+
+    #
+    # Assert that a +rpm_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as a
+    # +rpm_package+:
+    #
+    #     rpm_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +rpm_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +rpm_package+ was installed
+    #   expect(chef_run).to install_rpm_package('apache2')
+    #
+    # @example Assert that a +rpm_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_rpm_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +rpm_package+ was installed with attributes
+    #   expect(chef_run).to install_rpm_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +rpm_package+ was installed using a regex
+    #   expect(chef_run).to install_rpm_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +rpm_package+ was _not_ installed
+    #   expect(chef_run).to_not install_rpm_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_rpm_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:rpm_package, :install, resource_name)
+    end
+
+    #
+    # Assert that a +rpm_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as a
+    # +rpm_package+:
+    #
+    #     rpm_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +rpm_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +rpm_package+ was removed
+    #   expect(chef_run).to remove_rpm_package('apache2')
+    #
+    # @example Assert that a +rpm_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_rpm_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +rpm_package+ was removed with attributes
+    #   expect(chef_run).to remove_rpm_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +rpm_package+ was removed using a regex
+    #   expect(chef_run).to remove_rpm_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +rpm_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_rpm_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_rpm_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:rpm_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that a +rpm_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "apache2" as a
+    # +rpm_package+:
+    #
+    #     rpm_package 'apache2' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +rpm_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +rpm_package+ was upgraded
+    #   expect(chef_run).to upgrade_rpm_package('apache2')
+    #
+    # @example Assert that a +rpm_package+ was upgraded with predicate matchers
+    #   expect(chef_run).to upgrade_rpm_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +rpm_package+ was upgraded with attributes
+    #   expect(chef_run).to upgrade_rpm_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +rpm_package+ was upgraded using a regex
+    #   expect(chef_run).to upgrade_rpm_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +rpm_package+ was _not_ upgraded
+    #   expect(chef_run).to_not upgrade_rpm_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_rpm_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:rpm_package, :upgrade, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/ruby_block.rb b/lib/chefspec/api/ruby_block.rb
new file mode 100644
index 0000000..96a8ebe
--- /dev/null
+++ b/lib/chefspec/api/ruby_block.rb
@@ -0,0 +1,37 @@
+module ChefSpec::API
+  # @since 0.5.0
+  module RubyBlockMatchers
+    ChefSpec.define_matcher :ruby_block
+
+    #
+    # Assert that a +ruby_block+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "do_something" as a
+    # +ruby_block+:
+    #
+    #     ruby_block 'do_something' do
+    #       block do
+    #         # ...
+    #       end
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +ruby_block+ resource with ChefSpec.
+    #
+    # @example Assert that a +ruby_block+ was run
+    #   expect(chef_run).to run_ruby_block('do_something')
+    #
+    # @example Assert that a +ruby_block+ was _not_ run
+    #   expect(chef_run).to_not run_ruby_block('do_something')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_ruby_block(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ruby_block, :run, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/script.rb b/lib/chefspec/api/script.rb
new file mode 100644
index 0000000..7317a2a
--- /dev/null
+++ b/lib/chefspec/api/script.rb
@@ -0,0 +1,239 @@
+module ChefSpec::API
+  # @since 1.0.0
+  module ScriptMatchers
+    #
+    # Assert that a +bash+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "command" using
+    # +bash+:
+    #
+    #     bash 'command' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +bash+ resource with ChefSpec.
+    #
+    # @example Assert that a +bash+ was run
+    #   expect(chef_run).to run_bash('command')
+    #
+    # @example Assert that a +bash+ was run with predicate matchers
+    #   expect(chef_run).to run_bash('command').with_cwd('/home')
+    #
+    # @example Assert that a +bash+ was run with attributes
+    #   expect(chef_run).to run_bash('command').with(cwd: '/home')
+    #
+    # @example Assert that a +bash+ was run using a regex
+    #   expect(chef_run).to run_bash('command').with(cwd: /\/(.+)/)
+    #
+    # @example Assert that a +bash+ was _not_ run
+    #   expect(chef_run).to_not run_bash('command')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_bash(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:bash, :run, resource_name)
+    end
+
+    ChefSpec.define_matcher :bash
+
+    #
+    # Assert that a +csh+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "command" using
+    # +csh+:
+    #
+    #     csh 'command' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +csh+ resource with ChefSpec.
+    #
+    # @example Assert that a +csh+ was run
+    #   expect(chef_run).to run_csh('command')
+    #
+    # @example Assert that a +csh+ was run with predicate matchers
+    #   expect(chef_run).to run_csh('command').with_cwd('/home')
+    #
+    # @example Assert that a +csh+ was run with attributes
+    #   expect(chef_run).to run_csh('command').with(cwd: '/home')
+    #
+    # @example Assert that a +csh+ was run using a regex
+    #   expect(chef_run).to run_csh('command').with(cwd: /\/(.+)/)
+    #
+    # @example Assert that a +csh+ was _not_ run
+    #   expect(chef_run).to_not run_csh('command')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_csh(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:csh, :run, resource_name)
+    end
+
+    ChefSpec.define_matcher :csh
+
+    #
+    # Assert that a +perl+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "command" using
+    # +perl+:
+    #
+    #     perl 'command' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +perl+ resource with ChefSpec.
+    #
+    # @example Assert that a +perl+ was run
+    #   expect(chef_run).to run_perl('command')
+    #
+    # @example Assert that a +perl+ was run with predicate matchers
+    #   expect(chef_run).to run_perl('command').with_cwd('/home')
+    #
+    # @example Assert that a +perl+ was run with attributes
+    #   expect(chef_run).to run_perl('command').with(cwd: '/home')
+    #
+    # @example Assert that a +perl+ was run using a regex
+    #   expect(chef_run).to run_perl('command').with(cwd: /\/(.+)/)
+    #
+    # @example Assert that a +perl+ was _not_ run
+    #   expect(chef_run).to_not run_perl('command')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_perl(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:perl, :run, resource_name)
+    end
+
+    ChefSpec.define_matcher :perl
+
+    #
+    # Assert that a +python+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "command" using
+    # +python+:
+    #
+    #     python 'command' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +python+ resource with ChefSpec.
+    #
+    # @example Assert that a +python+ was run
+    #   expect(chef_run).to run_python('command')
+    #
+    # @example Assert that a +python+ was run with predicate matchers
+    #   expect(chef_run).to run_python('command').with_cwd('/home')
+    #
+    # @example Assert that a +python+ was run with attributes
+    #   expect(chef_run).to run_python('command').with(cwd: '/home')
+    #
+    # @example Assert that a +python+ was run using a regex
+    #   expect(chef_run).to run_python('command').with(cwd: /\/(.+)/)
+    #
+    # @example Assert that a +python+ was _not_ run
+    #   expect(chef_run).to_not run_python('command')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_python(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:python, :run, resource_name)
+    end
+
+    ChefSpec.define_matcher :python
+
+    #
+    # Assert that a +ruby+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "command" using
+    # +ruby+:
+    #
+    #     ruby 'command' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +ruby+ resource with ChefSpec.
+    #
+    # @example Assert that a +ruby+ was run
+    #   expect(chef_run).to run_ruby('command')
+    #
+    # @example Assert that a +ruby+ was run with predicate matchers
+    #   expect(chef_run).to run_ruby('command').with_cwd('/home')
+    #
+    # @example Assert that a +ruby+ was run with attributes
+    #   expect(chef_run).to run_ruby('command').with(cwd: '/home')
+    #
+    # @example Assert that a +ruby+ was run using a regex
+    #   expect(chef_run).to run_ruby('command').with(cwd: /\/(.+)/)
+    #
+    # @example Assert that a +ruby+ was _not_ run
+    #   expect(chef_run).to_not run_ruby('command')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_ruby(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:ruby, :run, resource_name)
+    end
+
+    ChefSpec.define_matcher :ruby
+
+    #
+    # Assert that a +script+ resource exists in the Chef run with the
+    # action +:run+. Given a Chef Recipe that runs "command" using
+    # +script+:
+    #
+    #     script 'command' do
+    #       action :run
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +script+ resource with ChefSpec.
+    #
+    # @example Assert that a +script+ was run
+    #   expect(chef_run).to run_script('command')
+    #
+    # @example Assert that a +script+ was run with predicate matchers
+    #   expect(chef_run).to run_script('command').with_cwd('/home')
+    #
+    # @example Assert that a +script+ was run with attributes
+    #   expect(chef_run).to run_script('command').with(cwd: '/home')
+    #
+    # @example Assert that a +script+ was run using a regex
+    #   expect(chef_run).to run_script('command').with(cwd: /\/(.+)/)
+    #
+    # @example Assert that a +script+ was _not_ run
+    #   expect(chef_run).to_not run_script('command')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def run_script(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:script, :run, resource_name)
+    end
+
+
+    ChefSpec.define_matcher :script
+  end
+end
diff --git a/lib/chefspec/api/service.rb b/lib/chefspec/api/service.rb
new file mode 100644
index 0000000..dd54eb2
--- /dev/null
+++ b/lib/chefspec/api/service.rb
@@ -0,0 +1,246 @@
+module ChefSpec::API
+  # @since 0.0.1
+  module ServiceMatchers
+    ChefSpec.define_matcher :service
+
+    #
+    # Assert that a +service+ resource exists in the Chef run with the
+    # action +:disable+. Given a Chef Recipe that disables "apache2" as a
+    # +service+:
+    #
+    #     service 'apache2' do
+    #       action :disable
+    #     end
+    #
+    # To test the content rendered by a +service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +service+ resource with ChefSpec.
+    #
+    # @example Assert that a +service+ was disabled
+    #   expect(chef_run).to disable_service('apache2')
+    #
+    # @example Assert that a +service+ was disabled with predicate matchers
+    #   expect(chef_run).to disable_service('apache2').with_pattern('apa*')
+    #
+    # @example Assert that a +service+ was disabled with attributes
+    #   expect(chef_run).to disable_service('apache2').with(pattern: 'apa*')
+    #
+    # @example Assert that a +service+ was disabled using a regex
+    #   expect(chef_run).to disable_service('apache2').with(patthen: /(.+)/)
+    #
+    # @example Assert that a +service+ was _not_ disabled
+    #   expect(chef_run).to_not disable_service('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def disable_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:service, :disable, resource_name)
+    end
+
+    #
+    # Assert that a +service+ resource exists in the Chef run with the
+    # action +:enable+. Given a Chef Recipe that enables "apache2" as a
+    # +service+:
+    #
+    #     service 'apache2' do
+    #       action :enable
+    #     end
+    #
+    # To test the content rendered by a +service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +service+ resource with ChefSpec.
+    #
+    # @example Assert that a +service+ was enabled
+    #   expect(chef_run).to enable_service('apache2')
+    #
+    # @example Assert that a +service+ was enabled with predicate matchers
+    #   expect(chef_run).to enable_service('apache2').with_pattern('apa*')
+    #
+    # @example Assert that a +service+ was enabled with attributes
+    #   expect(chef_run).to enable_service('apache2').with(pattern: 'apa*')
+    #
+    # @example Assert that a +service+ was enabled using a regex
+    #   expect(chef_run).to enable_service('apache2').with(patthen: /(.+)/)
+    #
+    # @example Assert that a +service+ was _not_ enabled
+    #   expect(chef_run).to_not enable_service('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def enable_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:service, :enable, resource_name)
+    end
+
+    #
+    # Assert that a +service+ resource exists in the Chef run with the
+    # action +:reload+. Given a Chef Recipe that reloads "apache2" as a
+    # +service+:
+    #
+    #     service 'apache2' do
+    #       action :reload
+    #     end
+    #
+    # To test the content rendered by a +service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +service+ resource with ChefSpec.
+    #
+    # @example Assert that a +service+ was reload
+    #   expect(chef_run).to reload_service('apache2')
+    #
+    # @example Assert that a +service+ was reload with predicate matchers
+    #   expect(chef_run).to reload_service('apache2').with_pattern('apa*')
+    #
+    # @example Assert that a +service+ was reload with attributes
+    #   expect(chef_run).to reload_service('apache2').with(pattern: 'apa*')
+    #
+    # @example Assert that a +service+ was reload using a regex
+    #   expect(chef_run).to reload_service('apache2').with(patthen: /(.+)/)
+    #
+    # @example Assert that a +service+ was _not_ reload
+    #   expect(chef_run).to_not reload_service('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def reload_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:service, :reload, resource_name)
+    end
+
+    #
+    # Assert that a +service+ resource exists in the Chef run with the
+    # action +:restart+. Given a Chef Recipe that restarts "apache2" as a
+    # +service+:
+    #
+    #     service 'apache2' do
+    #       action :restart
+    #     end
+    #
+    # To test the content rendered by a +service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +service+ resource with ChefSpec.
+    #
+    # @example Assert that a +service+ was restarted
+    #   expect(chef_run).to restart_service('apache2')
+    #
+    # @example Assert that a +service+ was restarted with predicate matchers
+    #   expect(chef_run).to restart_service('apache2').with_pattern('apa*')
+    #
+    # @example Assert that a +service+ was restarted with attributes
+    #   expect(chef_run).to restart_service('apache2').with(pattern: 'apa*')
+    #
+    # @example Assert that a +service+ was restarted using a regex
+    #   expect(chef_run).to restart_service('apache2').with(patthen: /(.+)/)
+    #
+    # @example Assert that a +service+ was _not_ restarted
+    #   expect(chef_run).to_not restart_service('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def restart_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:service, :restart, resource_name)
+    end
+
+    #
+    # Assert that a +service+ resource exists in the Chef run with the
+    # action +:start+. Given a Chef Recipe that starts "apache2" as a
+    # +service+:
+    #
+    #     service 'apache2' do
+    #       action :start
+    #     end
+    #
+    # To test the content rendered by a +service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +service+ resource with ChefSpec.
+    #
+    # @example Assert that a +service+ was started
+    #   expect(chef_run).to start_service('apache2')
+    #
+    # @example Assert that a +service+ was started with predicate matchers
+    #   expect(chef_run).to start_service('apache2').with_pattern('apa*')
+    #
+    # @example Assert that a +service+ was started with attributes
+    #   expect(chef_run).to start_service('apache2').with(pattern: 'apa*')
+    #
+    # @example Assert that a +service+ was started using a regex
+    #   expect(chef_run).to start_service('apache2').with(patthen: /(.+)/)
+    #
+    # @example Assert that a +service+ was _not_ started
+    #   expect(chef_run).to_not start_service('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def start_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:service, :start, resource_name)
+    end
+
+    #
+    # Assert that a +service+ resource exists in the Chef run with the
+    # action +:stop+. Given a Chef Recipe that stops "apache2" as a
+    # +service+:
+    #
+    #     service 'apache2' do
+    #       action :stop
+    #     end
+    #
+    # To test the content rendered by a +service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +service+ resource with ChefSpec.
+    #
+    # @example Assert that a +service+ was stopped
+    #   expect(chef_run).to stop_service('apache2')
+    #
+    # @example Assert that a +service+ was stopped with predicate matchers
+    #   expect(chef_run).to stop_service('apache2').with_pattern('apa*')
+    #
+    # @example Assert that a +service+ was stopped with attributes
+    #   expect(chef_run).to stop_service('apache2').with(pattern: 'apa*')
+    #
+    # @example Assert that a +service+ was stopped using a regex
+    #   expect(chef_run).to stop_service('apache2').with(patthen: /(.+)/)
+    #
+    # @example Assert that a +service+ was _not_ stopped
+    #   expect(chef_run).to_not stop_service('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def stop_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:service, :stop, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/smartos_package.rb b/lib/chefspec/api/smartos_package.rb
new file mode 100644
index 0000000..35b5cb5
--- /dev/null
+++ b/lib/chefspec/api/smartos_package.rb
@@ -0,0 +1,117 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module SmartosPackageMatchers
+    ChefSpec.define_matcher :smartos_package
+
+    #
+    # Assert that a +smartos_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as a
+    # +smartos_package+:
+    #
+    #     smartos_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +smartos_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +smartos_package+ was installed
+    #   expect(chef_run).to install_smartos_package('apache2')
+    #
+    # @example Assert that a +smartos_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_smartos_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +smartos_package+ was installed with attributes
+    #   expect(chef_run).to install_smartos_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +smartos_package+ was installed using a regex
+    #   expect(chef_run).to install_smartos_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +smartos_package+ was _not_ installed
+    #   expect(chef_run).to_not install_smartos_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_smartos_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:smartos_package, :install, resource_name)
+    end
+
+    #
+    # Assert that a +smartos_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as a
+    # +smartos_package+:
+    #
+    #     smartos_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +smartos_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +smartos_package+ was removed
+    #   expect(chef_run).to remove_smartos_package('apache2')
+    #
+    # @example Assert that a +smartos_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_smartos_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +smartos_package+ was removed with attributes
+    #   expect(chef_run).to remove_smartos_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +smartos_package+ was removed using a regex
+    #   expect(chef_run).to remove_smartos_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +smartos_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_smartos_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_smartos_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:smartos_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that a +smartos_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "apache2" as a
+    # +smartos_package+:
+    #
+    #     smartos_package 'apache2' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +smartos_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +smartos_package+ was upgraded
+    #   expect(chef_run).to upgrade_smartos_package('apache2')
+    #
+    # @example Assert that a +smartos_package+ was upgraded with predicate matchers
+    #   expect(chef_run).to upgrade_smartos_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +smartos_package+ was upgraded with attributes
+    #   expect(chef_run).to upgrade_smartos_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +smartos_package+ was upgraded using a regex
+    #   expect(chef_run).to upgrade_smartos_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +smartos_package+ was _not_ upgraded
+    #   expect(chef_run).to_not upgrade_smartos_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_smartos_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:smartos_package, :upgrade, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/solaris_package.rb b/lib/chefspec/api/solaris_package.rb
new file mode 100644
index 0000000..a18d92d
--- /dev/null
+++ b/lib/chefspec/api/solaris_package.rb
@@ -0,0 +1,80 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module SolarisPackageMatchers
+    ChefSpec.define_matcher :solaris_package
+
+    #
+    # Assert that a +solaris_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as a
+    # +solaris_package+:
+    #
+    #     solaris_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +solaris_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +solaris_package+ was installed
+    #   expect(chef_run).to install_solaris_package('apache2')
+    #
+    # @example Assert that a +solaris_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_solaris_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +solaris_package+ was installed with attributes
+    #   expect(chef_run).to install_solaris_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +solaris_package+ was installed using a regex
+    #   expect(chef_run).to install_solaris_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +solaris_package+ was _not_ installed
+    #   expect(chef_run).to_not install_solaris_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_solaris_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:solaris_package, :install, resource_name)
+    end
+
+    #
+    # Assert that a +solaris_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as a
+    # +solaris_package+:
+    #
+    #     solaris_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +solaris_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +solaris_package+ was removed
+    #   expect(chef_run).to remove_solaris_package('apache2')
+    #
+    # @example Assert that a +solaris_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_solaris_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +solaris_package+ was removed with attributes
+    #   expect(chef_run).to remove_solaris_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +solaris_package+ was removed using a regex
+    #   expect(chef_run).to remove_solaris_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +solaris_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_solaris_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_solaris_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:solaris_package, :remove, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/state_attrs.rb b/lib/chefspec/api/state_attrs.rb
new file mode 100644
index 0000000..943810e
--- /dev/null
+++ b/lib/chefspec/api/state_attrs.rb
@@ -0,0 +1,28 @@
+module ChefSpec::API
+  # @since 3.1.0
+  module StateAttrsMatcher
+    #
+    # Assert that a Chef resource has certain state attributes (since Chef
+    # 11.8.0):
+    #
+    #     state_attrs :time, :temperature
+    #
+    # @see https://github.com/opscode/chef/blob/e43d7ebda/lib/chef/resource/file.rb#L32-L37
+    #
+    # The Examples section demonstrates the different ways to test a
+    # resource's +state_attrs+ with ChefSpec.
+    #
+    # @example Assert the +lwrp+ resource has two state attributes
+    #   expect(lwrp).to have_state_attrs(:time, :temperature)
+    #
+    #
+    # @param [Array] state_attrs
+    #   the list of state attributes to assert
+    #
+    # @return [ChefSpec::Matchers::StateAttrsMatcher]
+    #
+    def have_state_attrs(*state_attrs)
+      ChefSpec::Matchers::StateAttrsMatcher.new(state_attrs)
+    end
+  end
+end
diff --git a/lib/chefspec/api/subscriptions.rb b/lib/chefspec/api/subscriptions.rb
new file mode 100644
index 0000000..d551b0c
--- /dev/null
+++ b/lib/chefspec/api/subscriptions.rb
@@ -0,0 +1,35 @@
+module ChefSpec::API
+  # @since 3.2.0
+  module SubscriptionsMatchers
+    #
+    # Assert that a resource subscribes to another. Given a Chef Recipe that
+    # subscribes a template resource to restart apache:
+    #
+    #     service 'apache2' do
+    #       subscribes :create, 'template[/etc/apache2/config]'
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # subscription on a resource with ChefSpec.
+    #
+    # @example Assert a basic subscription
+    #   service = chef_run.service('apache2')
+    #   expect(service).to subscribe_to('template[/etc/apache2/config]')
+    #
+    # @example Assert a subscription with specified action
+    #   expect(service).to subscribe_to('template[/etc/apache2/config]').on(:restart)
+    #
+    # @example Assert a subscription with specified action and timing
+    #   expect(service).to subscribe_to('template[/etc/apache2/config]').on(:restart).immediately
+    #
+    #
+    # @param [String] signature
+    #   the signature of the notification to match
+    #
+    # @return [ChefSpec::Matchers::NotificationsMatcher]
+    #
+    def subscribe_to(signature)
+      ChefSpec::Matchers::SubscribesMatcher.new(signature)
+    end
+  end
+end
diff --git a/lib/chefspec/api/subversion.rb b/lib/chefspec/api/subversion.rb
new file mode 100644
index 0000000..f6edf7a
--- /dev/null
+++ b/lib/chefspec/api/subversion.rb
@@ -0,0 +1,154 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module SubversionMatchers
+    ChefSpec.define_matcher :subversion
+
+    #
+    # Assert that a +subversion+ resource exists in the Chef run with the
+    # action +:checkout+. Given a Chef Recipe that checks out "svn://..." as a
+    # +subversion+:
+    #
+    #     subversion 'svn://...' do
+    #       action :checkout
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +subversion+ resource with ChefSpec.
+    #
+    # @example Assert that a +subversion+ was checked out
+    #   expect(chef_run).to checkout_subversion('svn://...')
+    #
+    # @example Assert that a +subversion+ was checked out with predicate matchers
+    #   expect(chef_run).to checkout_subversion('svn://...').with_user('svargo')
+    #
+    # @example Assert that a +subversion+ was checked out with attributes
+    #   expect(chef_run).to checkout_subversion('svn://...').with(user: 'svargo')
+    #
+    # @example Assert that a +subversion+ was checked out using a regex
+    #   expect(chef_run).to checkout_subversion('svn://...').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +subversion+ was _not_ checked out
+    #   expect(chef_run).to_not checkout_subversion('svn://...')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def checkout_subversion(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:subversion, :checkout, resource_name)
+    end
+
+    #
+    # Assert that a +subversion+ resource exists in the Chef run with the
+    # action +:export+. Given a Chef Recipe that exports "svn://" as a
+    # +subversion+:
+    #
+    #     subversion 'svn://' do
+    #       action :export
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +subversion+ resource with ChefSpec.
+    #
+    # @example Assert that a +subversion+ was exported
+    #   expect(chef_run).to export_subversion('svn://')
+    #
+    # @example Assert that a +subversion+ was exported with predicate matchers
+    #   expect(chef_run).to export_subversion('svn://').with_user('svargo')
+    #
+    # @example Assert that a +subversion+ was exported with attributes
+    #   expect(chef_run).to export_subversion('svn://').with(user: 'svargo')
+    #
+    # @example Assert that a +subversion+ was exported using a regex
+    #   expect(chef_run).to export_subversion('svn://').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +subversion+ was _not_ exported
+    #   expect(chef_run).to_not export_subversion('svn://')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def export_subversion(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:subversion, :export, resource_name)
+    end
+
+    #
+    # Assert that a +subversion+ resource exists in the Chef run with the
+    # action +:force_export+. Given a Chef Recipe that force_exports "svn://" as a
+    # +subversion+:
+    #
+    #     subversion 'svn://' do
+    #       action :force_export
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +subversion+ resource with ChefSpec.
+    #
+    # @example Assert that a +subversion+ was force_exported
+    #   expect(chef_run).to force_export_subversion('svn://')
+    #
+    # @example Assert that a +subversion+ was force_exported with predicate matchers
+    #   expect(chef_run).to force_export_subversion('svn://').with_user('svargo')
+    #
+    # @example Assert that a +subversion+ was force_exported with attributes
+    #   expect(chef_run).to force_export_subversion('svn://').with(user: 'svargo')
+    #
+    # @example Assert that a +subversion+ was force_exported using a regex
+    #   expect(chef_run).to force_export_subversion('svn://').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +subversion+ was _not_ force_exported
+    #   expect(chef_run).to_not force_export_subversion('svn://')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def force_export_subversion(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:subversion, :force_export, resource_name)
+    end
+
+    #
+    # Assert that a +subversion+ resource exists in the Chef run with the
+    # action +:sync+. Given a Chef Recipe that syncs "svn://" as a
+    # +subversion+:
+    #
+    #     subversion 'svn://' do
+    #       action :sync
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +subversion+ resource with ChefSpec.
+    #
+    # @example Assert that a +subversion+ was synced
+    #   expect(chef_run).to sync_subversion('svn://')
+    #
+    # @example Assert that a +subversion+ was synced with predicate matchers
+    #   expect(chef_run).to sync_subversion('svn://').with_user('svargo')
+    #
+    # @example Assert that a +subversion+ was synced with attributes
+    #   expect(chef_run).to sync_subversion('svn://').with(user: 'svargo')
+    #
+    # @example Assert that a +subversion+ was synced using a regex
+    #   expect(chef_run).to sync_subversion('svn://').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +subversion+ was _not_ synced
+    #   expect(chef_run).to_not sync_subversion('svn://')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def sync_subversion(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:subversion, :sync, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/template.rb b/lib/chefspec/api/template.rb
new file mode 100644
index 0000000..2ae43cb
--- /dev/null
+++ b/lib/chefspec/api/template.rb
@@ -0,0 +1,166 @@
+module ChefSpec::API
+  # @since 0.0.1
+  module TemplateMatchers
+    ChefSpec.define_matcher :template
+
+    #
+    # Assert that a +template+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "/tmp/config" as a
+    # +template+:
+    #
+    #     template '/tmp/config' do
+    #       action :create
+    #     end
+    #
+    # To test the content rendered by a +template+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +template+ resource with ChefSpec.
+    #
+    # @example Assert that a +template+ was created
+    #   expect(chef_run).to create_template('/tmp/config')
+    #
+    # @example Assert that a +template+ was created with predicate matchers
+    #   expect(chef_run).to create_template('/tmp/config').with_user('svargo')
+    #
+    # @example Assert that a +template+ was created with attributes
+    #   expect(chef_run).to create_template('/tmp/config').with(user: 'svargo')
+    #
+    # @example Assert that a +template+ was created using a regex
+    #   expect(chef_run).to create_template('/tmp/config').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +template+ was _not_ created
+    #   expect(chef_run).to_not create_template('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_template(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:template, :create, resource_name)
+    end
+
+    #
+    # Assert that a +template+ resource exists in the Chef run with the
+    # action +:create_if_missing+. Given a Chef Recipe that creates "/tmp/config"
+    # if missing as a +template+:
+    #
+    #     template '/tmp/config' do
+    #       action :create_if_missing
+    #     end
+    #
+    # To test the content rendered by a +template+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +template+ resource with ChefSpec.
+    #
+    # @example Assert that a +template+ was created if missing
+    #   expect(chef_run).to create_template_if_missing('/tmp/config')
+    #
+    # @example Assert that a +template+ was created if missing with predicate matchers
+    #   expect(chef_run).to create_template_if_missing('/tmp/config').with_user('svargo')
+    #
+    # @example Assert that a +template+ was created if missing with attributes
+    #   expect(chef_run).to create_template_if_missing('/tmp/config').with(user: 'svargo')
+    #
+    # @example Assert that a +template+ was created if missing using a regex
+    #   expect(chef_run).to create_template_if_missing('/tmp/config').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +template+ was _not_ created if missing
+    #   expect(chef_run).to_not create_template('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_template_if_missing(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:template, :create_if_missing, resource_name)
+    end
+
+    #
+    # Assert that a +template+ resource exists in the Chef run with the
+    # action +:delete+. Given a Chef Recipe that deletes "/tmp/config" as a
+    # +template+:
+    #
+    #     template '/tmp/config' do
+    #       action :delete
+    #     end
+    #
+    # To test the content rendered by a +template+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +template+ resource with ChefSpec.
+    #
+    # @example Assert that a +template+ was deleted
+    #   expect(chef_run).to delete_template('/tmp/config')
+    #
+    # @example Assert that a +template+ was deleted with predicate matchers
+    #   expect(chef_run).to delete_template('/tmp/config').with_user('svargo')
+    #
+    # @example Assert that a +template+ was deleted with attributes
+    #   expect(chef_run).to delete_template('/tmp/config').with(user: 'svargo')
+    #
+    # @example Assert that a +template+ was deleted using a regex
+    #   expect(chef_run).to delete_template('/tmp/config').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +template+ was _not_ deleted
+    #   expect(chef_run).to_not delete_template('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def delete_template(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:template, :delete, resource_name)
+    end
+
+    #
+    # Assert that a +template+ resource exists in the Chef run with the
+    # action +:touch+. Given a Chef Recipe that touches "/tmp/config" as a
+    # +template+:
+    #
+    #     template '/tmp/config' do
+    #       action :touch
+    #     end
+    #
+    # To test the content rendered by a +template+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +template+ resource with ChefSpec.
+    #
+    # @example Assert that a +template+ was touched
+    #   expect(chef_run).to touch_template('/tmp/config')
+    #
+    # @example Assert that a +template+ was touched with predicate matchers
+    #   expect(chef_run).to touch_template('/tmp/config').with_user('svargo')
+    #
+    # @example Assert that a +template+ was touched with attributes
+    #   expect(chef_run).to touch_template('/tmp/config').with(user: 'svargo')
+    #
+    # @example Assert that a +template+ was touched using a regex
+    #   expect(chef_run).to touch_template('/tmp/config').with(user: /sva(.+)/)
+    #
+    # @example Assert that a +template+ was _not_ touched
+    #   expect(chef_run).to_not touch_template('/tmp/config')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def touch_template(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:template, :touch, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/user.rb b/lib/chefspec/api/user.rb
new file mode 100644
index 0000000..6bbff88
--- /dev/null
+++ b/lib/chefspec/api/user.rb
@@ -0,0 +1,228 @@
+module ChefSpec::API
+  # @since 0.3.0
+  module UserMatchers
+    ChefSpec.define_matcher :user
+
+    #
+    # Assert that a +user+ resource exists in the Chef run with the
+    # action +:create+. Given a Chef Recipe that creates "apache2" as a
+    # +user+:
+    #
+    #     user 'apache2' do
+    #       action :create
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +user+ resource with ChefSpec.
+    #
+    # @example Assert that a +user+ was createed
+    #   expect(chef_run).to create_user('apache2')
+    #
+    # @example Assert that a +user+ was createed with predicate matchers
+    #   expect(chef_run).to create_user('apache2').with_uid(1234)
+    #
+    # @example Assert that a +user+ was createed with attributes
+    #   expect(chef_run).to create_user('apache2').with(uid: 1234)
+    #
+    # @example Assert that a +user+ was createed using a regex
+    #   expect(chef_run).to create_user('apache2').with(uid: /\d+/)
+    #
+    # @example Assert that a +user+ was _not_ createed
+    #   expect(chef_run).to_not create_user('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def create_user(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:user, :create, resource_name)
+    end
+
+    #
+    # Assert that a +user+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as a
+    # +user+:
+    #
+    #     user 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +user+ resource with ChefSpec.
+    #
+    # @example Assert that a +user+ was remove
+    #   expect(chef_run).to remove_user('apache2')
+    #
+    # @example Assert that a +user+ was remove with predicate matchers
+    #   expect(chef_run).to remove_user('apache2').with_uid(1234)
+    #
+    # @example Assert that a +user+ was remove with attributes
+    #   expect(chef_run).to remove_user('apache2').with(uid: 1234)
+    #
+    # @example Assert that a +user+ was remove using a regex
+    #   expect(chef_run).to remove_user('apache2').with(uid: /\d+/)
+    #
+    # @example Assert that a +user+ was _not_ remove
+    #   expect(chef_run).to_not remove_user('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_user(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:user, :remove, resource_name)
+    end
+
+    #
+    # Assert that a +user+ resource exists in the Chef run with the
+    # action +:modify+. Given a Chef Recipe that modifies "apache2" as a
+    # +user+:
+    #
+    #     user 'apache2' do
+    #       action :modify
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +user+ resource with ChefSpec.
+    #
+    # @example Assert that a +user+ was modified
+    #   expect(chef_run).to modify_user('apache2')
+    #
+    # @example Assert that a +user+ was modified with predicate matchers
+    #   expect(chef_run).to modify_user('apache2').with_uid(1234)
+    #
+    # @example Assert that a +user+ was modified with attributes
+    #   expect(chef_run).to modify_user('apache2').with(uid: 1234)
+    #
+    # @example Assert that a +user+ was modified using a regex
+    #   expect(chef_run).to modify_user('apache2').with(uid: /\d+/)
+    #
+    # @example Assert that a +user+ was _not_ modified
+    #   expect(chef_run).to_not modify_user('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def modify_user(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:user, :modify, resource_name)
+    end
+
+    #
+    # Assert that a +user+ resource exists in the Chef run with the
+    # action +:manage+. Given a Chef Recipe that manages "apache2" as a
+    # +user+:
+    #
+    #     user 'apache2' do
+    #       action :manage
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +user+ resource with ChefSpec.
+    #
+    # @example Assert that a +user+ was managed
+    #   expect(chef_run).to manage_user('apache2')
+    #
+    # @example Assert that a +user+ was managed with predicate matchers
+    #   expect(chef_run).to manage_user('apache2').with_uid(1234)
+    #
+    # @example Assert that a +user+ was managed with attributes
+    #   expect(chef_run).to manage_user('apache2').with(uid: 1234)
+    #
+    # @example Assert that a +user+ was managed using a regex
+    #   expect(chef_run).to manage_user('apache2').with(uid: /\d+/)
+    #
+    # @example Assert that a +user+ was _not_ managed
+    #   expect(chef_run).to_not manage_user('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def manage_user(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:user, :manage, resource_name)
+    end
+
+    #
+    # Assert that a +user+ resource exists in the Chef run with the
+    # action +:lock+. Given a Chef Recipe that locks "apache2" as a
+    # +user+:
+    #
+    #     user 'apache2' do
+    #       action :lock
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +user+ resource with ChefSpec.
+    #
+    # @example Assert that a +user+ was locked
+    #   expect(chef_run).to lock_user('apache2')
+    #
+    # @example Assert that a +user+ was locked with predicate matchers
+    #   expect(chef_run).to lock_user('apache2').with_uid(1234)
+    #
+    # @example Assert that a +user+ was locked with attributes
+    #   expect(chef_run).to lock_user('apache2').with(uid: 1234)
+    #
+    # @example Assert that a +user+ was locked using a regex
+    #   expect(chef_run).to lock_user('apache2').with(uid: /\d+/)
+    #
+    # @example Assert that a +user+ was _not_ locked
+    #   expect(chef_run).to_not lock_user('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def lock_user(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:user, :lock, resource_name)
+    end
+
+    #
+    # Assert that a +user+ resource exists in the Chef run with the
+    # action +:unlock+. Given a Chef Recipe that unlocks "apache2" as a
+    # +user+:
+    #
+    #     user 'apache2' do
+    #       action :unlock
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +user+ resource with ChefSpec.
+    #
+    # @example Assert that a +user+ was unlocked
+    #   expect(chef_run).to unlock_user('apache2')
+    #
+    # @example Assert that a +user+ was unlocked with predicate matchers
+    #   expect(chef_run).to unlock_user('apache2').with_uid(1234)
+    #
+    # @example Assert that a +user+ was unlocked with attributes
+    #   expect(chef_run).to unlock_user('apache2').with(uid: 1234)
+    #
+    # @example Assert that a +user+ was unlocked using a regex
+    #   expect(chef_run).to unlock_user('apache2').with(uid: /\d+/)
+    #
+    # @example Assert that a +user+ was _not_ unlocked
+    #   expect(chef_run).to_not unlock_user('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def unlock_user(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:user, :unlock, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/windows_service.rb b/lib/chefspec/api/windows_service.rb
new file mode 100644
index 0000000..4ad39bd
--- /dev/null
+++ b/lib/chefspec/api/windows_service.rb
@@ -0,0 +1,286 @@
+module ChefSpec::API
+  # @since 0.5.0
+  module WindowsServiceMatchers
+    ChefSpec.define_matcher :windows_service
+
+    #
+    # Assert that a +windows_service+ resource exists in the Chef run with the
+    # action +:configure_startup+. Given a Chef Recipe that configures startup for "BITS" as a
+    # +windows_service+:
+    #
+    #     windows_service 'BITS' do
+    #       action :configure_startup
+    #     end
+    #
+    # To test the content rendered by a +windows_service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +windows_service+ resource with ChefSpec.
+    #
+    # @example Assert that a +windows_service+ had its startup configured
+    #   expect(chef_run).to disable_windows_service('BITS')
+    #
+    # @example Assert that a +windows_service+ had its startup configured with predicate matchers
+    #   expect(chef_run).to disable_windows_service('BITS').with_pattern('BI*')
+    #
+    # @example Assert that a +windows_service+ had its startup configured with attributes
+    #   expect(chef_run).to disable_windows_service('BITS').with(pattern: 'BI*')
+    #
+    # @example Assert that a +windows_service+ had its startup configured using a regex
+    #   expect(chef_run).to disable_windows_service('BITS').with(pattern: /(.+)/)
+    #
+    # @example Assert that a +windows_service+ did _not_ have its startup configured
+    #   expect(chef_run).to_not disable_windows_service('BITS')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def configure_startup_windows_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:windows_service, :configure_startup, resource_name)
+    end
+
+    #
+    # Assert that a +windows_service+ resource exists in the Chef run with the
+    # action +:disable+. Given a Chef Recipe that disables "BITS" as a
+    # +windows_service+:
+    #
+    #     windows_service 'BITS' do
+    #       action :disable
+    #     end
+    #
+    # To test the content rendered by a +windows_service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +windows_service+ resource with ChefSpec.
+    #
+    # @example Assert that a +windows_service+ was disabled
+    #   expect(chef_run).to disable_windows_service('BITS')
+    #
+    # @example Assert that a +windows_service+ was disabled with predicate matchers
+    #   expect(chef_run).to disable_windows_service('BITS').with_pattern('BI*')
+    #
+    # @example Assert that a +windows_service+ was disabled with attributes
+    #   expect(chef_run).to disable_windows_service('BITS').with(pattern: 'BI*')
+    #
+    # @example Assert that a +windows_service+ was disabled using a regex
+    #   expect(chef_run).to disable_windows_service('BITS').with(pattern: /(.+)/)
+    #
+    # @example Assert that a +windows_service+ was _not_ disabled
+    #   expect(chef_run).to_not disable_windows_service('BITS')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def disable_windows_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:windows_service, :disable, resource_name)
+    end
+
+    #
+    # Assert that a +windows_service+ resource exists in the Chef run with the
+    # action +:enable+. Given a Chef Recipe that enables "BITS" as a
+    # +windows_service+:
+    #
+    #     windows_service 'BITS' do
+    #       action :enable
+    #     end
+    #
+    # To test the content rendered by a +windows_service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +windows_service+ resource with ChefSpec.
+    #
+    # @example Assert that a +windows_service+ was enabled
+    #   expect(chef_run).to enable_windows_service('BITS')
+    #
+    # @example Assert that a +windows_service+ was enabled with predicate matchers
+    #   expect(chef_run).to enable_windows_service('BITS').with_pattern('BI*')
+    #
+    # @example Assert that a +windows_service+ was enabled with attributes
+    #   expect(chef_run).to enable_windows_service('BITS').with(pattern: 'BI*')
+    #
+    # @example Assert that a +windows_service+ was enabled using a regex
+    #   expect(chef_run).to enable_windows_service('BITS').with(pattern: /(.+)/)
+    #
+    # @example Assert that a +windows_service+ was _not_ enabled
+    #   expect(chef_run).to_not enable_windows_service('BITS')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def enable_windows_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:windows_service, :enable, resource_name)
+    end
+
+    #
+    # Assert that a +windows_service+ resource exists in the Chef run with the
+    # action +:reload+. Given a Chef Recipe that reloads "BITS" as a
+    # +windows_service+:
+    #
+    #     windows_service 'BITS' do
+    #       action :reload
+    #     end
+    #
+    # To test the content rendered by a +windows_service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +windows_service+ resource with ChefSpec.
+    #
+    # @example Assert that a +windows_service+ was reload
+    #   expect(chef_run).to reload_windows_service('BITS')
+    #
+    # @example Assert that a +windows_service+ was reload with predicate matchers
+    #   expect(chef_run).to reload_windows_service('BITS').with_pattern('BI*')
+    #
+    # @example Assert that a +windows_service+ was reload with attributes
+    #   expect(chef_run).to reload_windows_service('BITS').with(pattern: 'BI*')
+    #
+    # @example Assert that a +windows_service+ was reload using a regex
+    #   expect(chef_run).to reload_windows_service('BITS').with(pattern: /(.+)/)
+    #
+    # @example Assert that a +windows_service+ was _not_ reload
+    #   expect(chef_run).to_not reload_windows_service('BITS')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def reload_windows_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:windows_service, :reload, resource_name)
+    end
+
+    #
+    # Assert that a +windows_service+ resource exists in the Chef run with the
+    # action +:restart+. Given a Chef Recipe that restarts "BITS" as a
+    # +windows_service+:
+    #
+    #     windows_service 'BITS' do
+    #       action :restart
+    #     end
+    #
+    # To test the content rendered by a +windows_service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +windows_service+ resource with ChefSpec.
+    #
+    # @example Assert that a +windows_service+ was restarted
+    #   expect(chef_run).to restart_windows_service('BITS')
+    #
+    # @example Assert that a +windows_service+ was restarted with predicate matchers
+    #   expect(chef_run).to restart_windows_service('BITS').with_pattern('BI*')
+    #
+    # @example Assert that a +windows_service+ was restarted with attributes
+    #   expect(chef_run).to restart_windows_service('BITS').with(pattern: 'BI*')
+    #
+    # @example Assert that a +windows_service+ was restarted using a regex
+    #   expect(chef_run).to restart_windows_service('BITS').with(pattern: /(.+)/)
+    #
+    # @example Assert that a +windows_service+ was _not_ restarted
+    #   expect(chef_run).to_not restart_windows_service('BITS')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def restart_windows_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:windows_service, :restart, resource_name)
+    end
+
+    #
+    # Assert that a +windows_service+ resource exists in the Chef run with the
+    # action +:start+. Given a Chef Recipe that starts "BITS" as a
+    # +windows_service+:
+    #
+    #     windows_service 'BITS' do
+    #       action :start
+    #     end
+    #
+    # To test the content rendered by a +windows_service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +windows_service+ resource with ChefSpec.
+    #
+    # @example Assert that a +windows_service+ was started
+    #   expect(chef_run).to start_windows_service('BITS')
+    #
+    # @example Assert that a +windows_service+ was started with predicate matchers
+    #   expect(chef_run).to start_windows_service('BITS').with_pattern('BI*')
+    #
+    # @example Assert that a +windows_service+ was started with attributes
+    #   expect(chef_run).to start_windows_service('BITS').with(pattern: 'BI*')
+    #
+    # @example Assert that a +windows_service+ was started using a regex
+    #   expect(chef_run).to start_windows_service('BITS').with(pattern: /(.+)/)
+    #
+    # @example Assert that a +windows_service+ was _not_ started
+    #   expect(chef_run).to_not start_windows_service('BITS')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def start_windows_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:windows_service, :start, resource_name)
+    end
+
+    #
+    # Assert that a +windows_service+ resource exists in the Chef run with the
+    # action +:stop+. Given a Chef Recipe that stops "BITS" as a
+    # +windows_service+:
+    #
+    #     windows_service 'BITS' do
+    #       action :stop
+    #     end
+    #
+    # To test the content rendered by a +windows_service+, see
+    # {ChefSpec::API::RenderFileMatchers}.
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +windows_service+ resource with ChefSpec.
+    #
+    # @example Assert that a +windows_service+ was stopped
+    #   expect(chef_run).to stop_windows_service('BITS')
+    #
+    # @example Assert that a +windows_service+ was stopped with predicate matchers
+    #   expect(chef_run).to stop_windows_service('BITS').with_pattern('BI*')
+    #
+    # @example Assert that a +windows_service+ was stopped with attributes
+    #   expect(chef_run).to stop_windows_service('BITS').with(pattern: 'BI*')
+    #
+    # @example Assert that a +windows_service+ was stopped using a regex
+    #   expect(chef_run).to stop_windows_service('BITS').with(pattern: /(.+)/)
+    #
+    # @example Assert that a +windows_service+ was _not_ stopped
+    #   expect(chef_run).to_not stop_windows_service('BITS')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def stop_windows_service(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:windows_service, :stop, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/api/yum_package.rb b/lib/chefspec/api/yum_package.rb
new file mode 100644
index 0000000..d6f702d
--- /dev/null
+++ b/lib/chefspec/api/yum_package.rb
@@ -0,0 +1,154 @@
+module ChefSpec::API
+  # @since 3.0.0
+  module YumPackageMatchers
+    ChefSpec.define_matcher :yum_package
+
+    #
+    # Assert that a +yum_package+ resource exists in the Chef run with the
+    # action +:install+. Given a Chef Recipe that installs "apache2" as a
+    # +yum_package+:
+    #
+    #     yum_package 'apache2' do
+    #       action :install
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +yum_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +yum_package+ was installed
+    #   expect(chef_run).to install_yum_package('apache2')
+    #
+    # @example Assert that a +yum_package+ was installed with predicate matchers
+    #   expect(chef_run).to install_yum_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +yum_package+ was installed with attributes
+    #   expect(chef_run).to install_yum_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +yum_package+ was installed using a regex
+    #   expect(chef_run).to install_yum_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +yum_package+ was _not_ installed
+    #   expect(chef_run).to_not install_yum_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def install_yum_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:yum_package, :install, resource_name)
+    end
+
+    #
+    # Assert that a +yum_package+ resource exists in the Chef run with the
+    # action +:purge+. Given a Chef Recipe that purges "apache2" as a
+    # +yum_package+:
+    #
+    #     yum_package 'apache2' do
+    #       action :purge
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +yum_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +yum_package+ was purged
+    #   expect(chef_run).to purge_yum_package('apache2')
+    #
+    # @example Assert that a +yum_package+ was purged with predicate matchers
+    #   expect(chef_run).to purge_yum_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +yum_package+ was purged with attributes
+    #   expect(chef_run).to purge_yum_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +yum_package+ was purged using a regex
+    #   expect(chef_run).to purge_yum_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +yum_package+ was _not_ purged
+    #   expect(chef_run).to_not purge_yum_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def purge_yum_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:yum_package, :purge, resource_name)
+    end
+
+    #
+    # Assert that a +yum_package+ resource exists in the Chef run with the
+    # action +:remove+. Given a Chef Recipe that removes "apache2" as a
+    # +yum_package+:
+    #
+    #     yum_package 'apache2' do
+    #       action :remove
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +yum_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +yum_package+ was removed
+    #   expect(chef_run).to remove_yum_package('apache2')
+    #
+    # @example Assert that a +yum_package+ was removed with predicate matchers
+    #   expect(chef_run).to remove_yum_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +yum_package+ was removed with attributes
+    #   expect(chef_run).to remove_yum_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +yum_package+ was removed using a regex
+    #   expect(chef_run).to remove_yum_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +yum_package+ was _not_ removed
+    #   expect(chef_run).to_not remove_yum_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def remove_yum_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:yum_package, :remove, resource_name)
+    end
+
+    #
+    # Assert that a +yum_package+ resource exists in the Chef run with the
+    # action +:upgrade+. Given a Chef Recipe that upgrades "apache2" as a
+    # +yum_package+:
+    #
+    #     yum_package 'apache2' do
+    #       action :upgrade
+    #     end
+    #
+    # The Examples section demonstrates the different ways to test a
+    # +yum_package+ resource with ChefSpec.
+    #
+    # @example Assert that a +yum_package+ was upgraded
+    #   expect(chef_run).to upgrade_yum_package('apache2')
+    #
+    # @example Assert that a +yum_package+ was upgraded with predicate matchers
+    #   expect(chef_run).to upgrade_yum_package('apache2').with_version('1.2.3')
+    #
+    # @example Assert that a +yum_package+ was upgraded with attributes
+    #   expect(chef_run).to upgrade_yum_package('apache2').with(version: '1.2.3')
+    #
+    # @example Assert that a +yum_package+ was upgraded using a regex
+    #   expect(chef_run).to upgrade_yum_package('apache2').with(version: /(\d+\.){2}\.\d+/)
+    #
+    # @example Assert that a +yum_package+ was _not_ upgraded
+    #   expect(chef_run).to_not upgrade_yum_package('apache2')
+    #
+    #
+    # @param [String, Regex] resource_name
+    #   the name of the resource to match
+    #
+    # @return [ChefSpec::Matchers::ResourceMatcher]
+    #
+    def upgrade_yum_package(resource_name)
+      ChefSpec::Matchers::ResourceMatcher.new(:yum_package, :upgrade, resource_name)
+    end
+  end
+end
diff --git a/lib/chefspec/berkshelf.rb b/lib/chefspec/berkshelf.rb
new file mode 100644
index 0000000..4a5a466
--- /dev/null
+++ b/lib/chefspec/berkshelf.rb
@@ -0,0 +1,57 @@
+begin
+  require 'berkshelf'
+rescue LoadError
+  raise ChefSpec::Error::GemLoadError.new(gem: 'berkshelf', name: 'Berkshelf')
+end
+
+module ChefSpec
+  class Berkshelf
+    class << self
+      extend Forwardable
+      def_delegators :instance, :setup!, :teardown!
+    end
+
+    include Singleton
+
+    def initialize
+      @tmpdir = Dir.mktmpdir
+    end
+
+    #
+    # Setup and install the necessary dependencies in the temporary directory.
+    #
+    def setup!
+      berksfile = ::Berkshelf::Berksfile.from_file('Berksfile')
+
+      # Grab a handle to tmpdir, since Berkshelf 2 modifies it a bit
+      tmpdir = File.join(@tmpdir, 'cookbooks')
+
+      ::Berkshelf.ui.mute do
+        if ::Berkshelf::Berksfile.method_defined?(:vendor)
+          # Berkshelf 3.0 requires the directory to not exist
+          FileUtils.rm_rf(tmpdir)
+          berksfile.vendor(tmpdir)
+        else
+          berksfile.install(path: tmpdir)
+        end
+      end
+
+      filter = Coverage::BerkshelfFilter.new(berksfile)
+      Coverage.add_filter(filter)
+
+      ::RSpec.configure { |config| config.cookbook_path = tmpdir }
+    end
+
+    #
+    # Destroy the installed Berkshelf at the temporary directory.
+    #
+    def teardown!
+      FileUtils.rm_rf(@tmpdir) if File.exist?(@tmpdir)
+    end
+  end
+end
+
+RSpec.configure do |config|
+  config.before(:suite) { ChefSpec::Berkshelf.setup! }
+  config.after(:suite)  { ChefSpec::Berkshelf.teardown! }
+end
diff --git a/lib/chefspec/cacher.rb b/lib/chefspec/cacher.rb
new file mode 100644
index 0000000..8b9fb72
--- /dev/null
+++ b/lib/chefspec/cacher.rb
@@ -0,0 +1,63 @@
+module ChefSpec
+  #
+  # The cacher module allows for ultra-fast tests by caching the results of a
+  # CCR in memory across an example group. In testing, this can reduce the
+  # total testing time by a factor of 10x. This strategy is _not_ the default
+  # behavior, because it has implications surrounding stubbing and is _not_
+  # threadsafe!
+  #
+  # The credit for this approach and code belongs to Juri Timošin (DracoAter).
+  # Please see his original blog post below for an in-depth explanation of how
+  # and why this approach is faster.
+  #
+  # @example Using the Cacher module
+  #   First, require the Cacher module in your +spec_helper.rb+:
+  #
+  #     RSpec.configure do |config|
+  #       config.extend(ChefSpec::Cacher)
+  #     end
+  #
+  #   Next, change your +let+ blocks to +cached+ blocks:
+  #
+  #     let(:chef_run) { ... } #=> cached(:chef_run) { ... }
+  #
+  #   Finally, celebrate!
+  #
+  # @warn
+  #   This strategy is only recommended for advanced users, as it makes
+  #   stubbing slightly more difficult and indirect!
+  #
+  # @see http://dracoater.blogspot.com/2013/12/testing-chef-cookbooks-part-25-speeding.html
+  #
+  module Cacher
+    @@cache = {}
+    FINALIZER = lambda { |id| @@cache.delete(id) }
+
+    def cached(name, &block)
+      location = ancestors.first.metadata[:location]
+      unless ancestors.first.metadata[:description].nil? || location.nil?
+        location += ancestors.first.metadata[:description]
+      end
+      location ||= ancestors.first.metadata[:parent_example_group][:location]
+
+      define_method(name) do
+        key = [location, name.to_s].join('.')
+        unless @@cache.has_key?(Thread.current.object_id)
+          ObjectSpace.define_finalizer(Thread.current, FINALIZER)
+        end
+        @@cache[Thread.current.object_id] ||= {}
+        @@cache[Thread.current.object_id][key] ||= instance_eval(&block)
+      end
+    end
+
+    def cached!(name, &block)
+      cached(name, &block)
+
+      before { send(name) }
+    end
+  end
+end
+
+RSpec.configure do |config|
+  config.extend(ChefSpec::Cacher)
+end
diff --git a/lib/chefspec/chef_backwards_compat.rb b/lib/chefspec/chef_backwards_compat.rb
new file mode 100644
index 0000000..16916e1
--- /dev/null
+++ b/lib/chefspec/chef_backwards_compat.rb
@@ -0,0 +1,79 @@
+#
+# This file contains the backwards compatible hacks required to make ChefSpec
+# work with the previous version of Chef. ChefSpec only promises to be backwards
+# compatible with the last major release of Chef, but it may work with earlier
+# versions.
+#
+# The hacks are kept in this file so as to avoid a bunch of branching logic
+# throughout the codebase. It also makes dropping backwards compatability much
+# easier without the risk of breaking things.
+#
+# This file must be loaded AFTER the rest of ChefSpec has been initialized!
+#
+
+module ChefSpec
+  class ServerRunner
+    #
+    # The method airty for the Chef::CookbookUploader used to accept a list of
+    # cookbook paths. This restores that behavior.
+    #
+    def cookbook_uploader_for(loader)
+      Chef::CookbookUploader.new(loader.cookbooks, loader.cookbook_paths)
+    end
+  end
+
+  module RemoveExistingLWRP
+    def self.extended(klass)
+      class << klass
+        alias_method :build_from_file_without_removal, :build_from_file
+        alias_method :build_from_file, :build_from_file_with_removal
+      end
+    end
+
+    #
+    # Override Chef provider to remove any existing LWRPs to suppress constant
+    # re-definition warnings.
+    #
+    # @param [String] cookbook_name
+    #   the name of the cookbook
+    # @param [String] filename
+    #   file to load as a LWRP
+    # @param [Chef::RunContext] run_context
+    #   context of a Chef Run
+    #
+    # @return [Chef::Provider]
+    #
+    def build_from_file_with_removal(cookbook_name, filename, run_context)
+      provider_name = filename_to_qualified_string(cookbook_name, filename)
+      class_name    = convert_to_class_name(provider_name)
+
+      remove_existing_lwrp(class_name)
+      build_from_file_without_removal(cookbook_name, filename, run_context)
+    end
+
+    #
+    # Remove any existing Chef provider or resource with the specified name.
+    #
+    # @param [String] class_name
+    #   The class name. Must be a valid constant name.
+    #
+    def remove_existing_lwrp(class_name)
+      [self, superclass].each do |resource_holder|
+        look_in_parents = false
+        if resource_holder.const_defined?(class_name, look_in_parents)
+          old_class = resource_holder.send(:remove_const, class_name)
+
+          if resource_holder.respond_to?(:resource_classes)
+            resource_holder.resource_classes.delete(old_class)
+          end
+        end
+      end
+    end
+  end
+end
+
+
+# Only remove existing LWRPs for older versions of Chef. Newer versions of
+# Chef do not break things as much...
+Chef::Provider::LWRPBase.extend(ChefSpec::RemoveExistingLWRP)
+Chef::Resource::LWRPBase.extend(ChefSpec::RemoveExistingLWRP)
diff --git a/lib/chefspec/coverage.rb b/lib/chefspec/coverage.rb
new file mode 100644
index 0000000..c212f19
--- /dev/null
+++ b/lib/chefspec/coverage.rb
@@ -0,0 +1,200 @@
+require_relative 'coverage/filters'
+
+module ChefSpec
+  class Coverage
+    EXIT_FAILURE = 1
+    EXIT_SUCCESS = 0
+
+    class << self
+      def method_added(name)
+        # Only delegate public methods
+        if method_defined?(name)
+          instance_eval <<-EOH, __FILE__, __LINE__ + 1
+            def #{name}(*args, &block)
+              instance.public_send(:#{name}, *args, &block)
+            end
+          EOH
+        end
+      end
+    end
+
+    include Singleton
+
+    attr_reader :filters
+
+    #
+    # Create a new coverage object singleton.
+    #
+    def initialize
+      @collection = {}
+      @filters    = {}
+    end
+
+    #
+    # Start the coverage reporting analysis. This method also adds the the
+    # +at_exit+ handler for printing the coverage report.
+    #
+    def start!(&block)
+      instance_eval(&block) if block
+      at_exit { ChefSpec::Coverage.report! }
+    end
+
+    #
+    # Add a filter to the converage analysis.
+    #
+    # @param [Filter, String, Regexp] filter
+    #   the filter to add
+    # @param [Proc] block
+    #   the block to use as a filter
+    #
+    # @return [true]
+    #
+    def add_filter(filter = nil, &block)
+      id = "#{filter.inspect}/#{block.inspect}".hash
+
+      @filters[id] = if filter.kind_of?(Filter)
+                       filter
+                     elsif filter.kind_of?(String)
+                       StringFilter.new(filter)
+                     elsif filter.kind_of?(Regexp)
+                       RegexpFilter.new(filter)
+                     elsif block
+                       BlockFilter.new(block)
+                     else
+                       raise ArgumentError, 'Please specify either a string, ' \
+                         'filter, or block to filter source files with!'
+                     end
+
+      true
+    end
+
+    #
+    # Add a resource to the resource collection. Only new resources are added
+    # and only resources that match the given filter are covered (which is *
+    # by default).
+    #
+    # @param [Chef::Resource] resource
+    #
+    def add(resource)
+      if !exists?(resource) && !filtered?(resource)
+        @collection[resource.to_s] = ResourceWrapper.new(resource)
+      end
+    end
+
+    #
+    # Called when a resource is matched to indicate it has been tested.
+    #
+    # @param [Chef::Resource] resource
+    #
+    def cover!(resource)
+      if wrapper = find(resource)
+        wrapper.touch!
+      end
+    end
+
+    #
+    # Called to check if a resource belongs to a cookbook from the specified
+    # directories.
+    #
+    # @param [Chef::Resource] resource
+    #
+    def filtered?(resource)
+      filters.any? { |_, filter| filter.matches?(resource) }
+    end
+
+    #
+    # Generate a coverage report. This report **must** be generated +at_exit+
+    # or else the entire resource collection may not be complete!
+    #
+    # @example Generating a report
+    #
+    #   ChefSpec::Coverage.report!
+    #
+    def report!
+      # Borrowed from simplecov#41
+      #
+      # If an exception is thrown that isn't a "SystemExit", we need to capture
+      # that error and re-raise.
+      if $!
+        exit_status = $!.is_a?(SystemExit) ? $!.status : EXIT_FAILURE
+      else
+        exit_status = EXIT_SUCCESS
+      end
+
+      report = {}.tap do |h|
+        h[:total]     = @collection.size
+        h[:touched]   = @collection.count { |_, resource| resource.touched? }
+        h[:coverage]  = ((h[:touched]/h[:total].to_f)*100).round(2)
+      end
+
+      report[:untouched_resources] = @collection.collect do |_, resource|
+        resource unless resource.touched?
+      end.compact
+
+      template = ChefSpec.root.join('templates', 'coverage', 'human.erb')
+      erb = Erubis::Eruby.new(File.read(template))
+      puts erb.evaluate(report)
+
+      # Ensure we exit correctly (#351)
+      Kernel.exit(exit_status) if exit_status && exit_status > 0
+    end
+
+    private
+
+    def find(resource)
+      @collection[resource.to_s]
+    end
+
+    def exists?(resource)
+      !find(resource).nil?
+    end
+
+    class ResourceWrapper
+      attr_reader :resource
+
+      def initialize(resource = nil)
+        @resource = resource
+      end
+
+      def to_s
+        @resource.to_s
+      end
+
+      def source_file
+        @source_file ||= if @resource.source_line
+          shortname(@resource.source_line.split(':').first)
+        else
+          'Unknown'
+        end
+      end
+
+      def source_line
+        @source_line ||= if @resource.source_line
+          @resource.source_line.split(':', 2).last.to_i
+        else
+          'Unknown'
+        end
+      end
+
+      def touch!
+        @touched = true
+      end
+
+      def touched?
+        !!@touched
+      end
+
+      private
+
+      def shortname(file)
+        if file.include?(Dir.pwd)
+          file.split(Dir.pwd, 2).last
+        elsif file.include?('cookbooks')
+          file.split('cookbooks/', 2).last
+        else
+          file
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/coverage/filters.rb b/lib/chefspec/coverage/filters.rb
new file mode 100644
index 0000000..00adb55
--- /dev/null
+++ b/lib/chefspec/coverage/filters.rb
@@ -0,0 +1,79 @@
+module ChefSpec
+  class Coverage
+    class Filter
+      def initialize(filter)
+        @filter = filter
+      end
+
+      def matches?
+        raise RuntimeError, "Must override Filter#matches?"
+      end
+    end
+
+    #
+    # @example Match resources based on a regular expression.
+    #   add_filter /^test/
+    #
+    class RegexpFilter < Filter
+      def matches?(resource)
+        return true if resource.source_line.nil?
+        @filter =~ resource.source_line
+      end
+    end
+
+    #
+    # @example Match resources based on a regular expression.
+    #   add_filter 'test/bar/zip'
+    #
+    class StringFilter < RegexpFilter
+      def initialize(filter)
+        super(Regexp.new("^#{filter}"))
+      end
+    end
+
+    #
+    # @example Match resources based on a custom block.
+    #   # Ignore internal cookbooks
+    #   add_filter do |resource|
+    #     resource.name =~ /^acme-(.+)/
+    #   end
+    #
+    class BlockFilter < Filter
+      def matches?(resource)
+        return true if resource.source_line.nil?
+        @filter.call(resource)
+      end
+    end
+
+    #
+    # @example Ignore dependent cookbooks (via Berkshelf)
+    #   add_filter BerkshelfFilter.new(berksfile)
+    #
+    class BerkshelfFilter < Filter
+      def initialize(berksfile)
+        @berksfile = berksfile
+
+        @metadatas = if berksfile.respond_to?(:dependencies)
+          berksfile.dependencies
+            .select(&:metadata?)
+            .map(&:name)
+        else
+          berksfile.sources.collect do |source|
+            location = source.location
+            if location.respond_to?(:metadata?) && location.metadata?
+              source
+            else
+              nil
+            end
+          end.compact.map(&:name)
+        end
+      end
+
+      def matches?(resource)
+        return true if resource.source_line.nil?
+        normalized_source_line = resource.source_line.gsub("\\", "/")
+        normalized_source_line=~ /cookbooks\/(?!#{@metadatas.join('|')})/
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/deprecations.rb b/lib/chefspec/deprecations.rb
new file mode 100644
index 0000000..d53fb0e
--- /dev/null
+++ b/lib/chefspec/deprecations.rb
@@ -0,0 +1,51 @@
+module Kernel
+  # Kernel extension to print deprecation notices.
+  #
+  # @example printing a deprecation warning
+  #   deprecated 'no longer in use' #=> "[DEPRECATION] no longer in use"
+  #
+  # @param [Array<String>] messages
+  def deprecated(*messages)
+    messages.each do |message|
+      calling_spec = caller.find { |line| line =~ /(\/spec)|(_spec\.rb)/ }
+      calling_spec = 'spec/' + calling_spec.split('/spec/').last
+      warn "[DEPRECATION] #{message} (called from #{calling_spec})"
+    end
+  end
+end
+
+module ChefSpec
+  class Runner
+    # @deprecated {ChefSpec::Runner.define_runner_method} is deprecated. Please
+    #   use {ChefSpec.define_matcher} instead.
+    def self.define_runner_method(resource_name)
+      deprecated "`ChefSpec::Runner.define_runner_method' is deprecated." \
+        " It is being used in the #{resource_name} resource matcher." \
+        " Please use `ChefSpec.define_matcher' instead."
+
+      ChefSpec.define_matcher(resource_name)
+    end
+
+    # @deprecated {ChefSpec::Runner.new} is deprecated. Please use
+    #   {ChefSpec::SoloRunner} or {ChefSpec::ServerRunner} instead.
+    def self.new(*args, &block)
+      deprecated "`ChefSpec::Runner' is deprecated. Please use" \
+        " `ChefSpec::SoloRunner' or `ChefSpec::ServerRunner' instead."
+
+      ChefSpec::SoloRunner.new(*args, &block)
+    end
+  end
+
+  class Server
+    def self.method_missing(m, *args, &block)
+      deprecated "`ChefSpec::Server.#{m}' is deprecated. There is no longer" \
+        " a global Chef Server instance. Please use a ChefSpec::ServerRunner" \
+        " instead. More documentation can be found in the ChefSpec README."
+      raise ChefSpec::Error::NoConversionError
+    end
+  end
+end
+
+module ChefSpec::Error
+  class NoConversionError < ChefSpecError;  end
+end
diff --git a/lib/chefspec/errors.rb b/lib/chefspec/errors.rb
new file mode 100644
index 0000000..6e184ba
--- /dev/null
+++ b/lib/chefspec/errors.rb
@@ -0,0 +1,40 @@
+module ChefSpec
+  module Error
+    class ChefSpecError < StandardError
+      def initialize(options = {})
+        class_name = self.class.to_s.split('::').last
+        filename   = options.delete(:_template) || Util.underscore(class_name)
+        template   = ChefSpec.root.join('templates', 'errors', "#{filename}.erb")
+
+        erb = Erubis::Eruby.new(File.read(template))
+        super erb.evaluate(options)
+      end
+    end
+
+    class NotStubbed < ChefSpecError
+      def initialize(options = {})
+        name  = self.class.name.to_s.split('::').last
+        type  = Util.underscore(name).gsub('_not_stubbed', '')
+        klass = Stubs.const_get(name.gsub('NotStubbed', '') + 'Stub')
+        stub  = klass.new(*options[:args]).and_return('...').signature
+
+        signature = "#{type}(#{options[:args].map(&:inspect).join(', ')})"
+
+        super({
+          type:      type,
+          signature: signature,
+          stub:      stub,
+          _template: :not_stubbed,
+        }.merge(options))
+      end
+    end
+
+    class CommandNotStubbed < NotStubbed; end
+    class SearchNotStubbed < NotStubbed; end
+    class DataBagNotStubbed < NotStubbed; end
+    class DataBagItemNotStubbed < NotStubbed; end
+
+    class CookbookPathNotFound < ChefSpecError; end
+    class GemLoadError < ChefSpecError; end
+  end
+end
diff --git a/lib/chefspec/expect_exception.rb b/lib/chefspec/expect_exception.rb
new file mode 100644
index 0000000..4e77700
--- /dev/null
+++ b/lib/chefspec/expect_exception.rb
@@ -0,0 +1,50 @@
+class RSpec::Matchers::BuiltIn::RaiseError
+  class << self
+    attr_accessor :last_run
+  end
+
+  attr_reader :expected_message
+
+  def last_error_for_chefspec
+    @expected_error
+  end
+
+  alias_method :old_matches?, :matches?
+  def matches?(*args)
+    self.class.last_run = self
+    old_matches?(*args)
+  end
+end
+
+module ChefSpec
+  class ExpectException
+    def initialize(formatter_exception, formatter_message = nil)
+      @formatter_exception = formatter_exception
+      @formatter_message   = formatter_message
+      @matcher             = RSpec::Matchers::BuiltIn::RaiseError.last_run
+    end
+
+    def expected?
+      return false if @matcher.nil?
+      exception_matched? && message_matched?
+    end
+
+    private
+
+    def exception_matched?
+      @formatter_exception == @matcher.last_error_for_chefspec ||
+      @matcher.last_error_for_chefspec === @formatter_exception
+    end
+
+    def message_matched?
+      case @formatter_message
+      when nil
+        true
+      when Regexp
+        @matcher.expected_message =~ @formatter_message
+      else
+        @matcher.expected_message == @formatter_message
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/extensions/chef/client.rb b/lib/chefspec/extensions/chef/client.rb
new file mode 100644
index 0000000..bdd2364
--- /dev/null
+++ b/lib/chefspec/extensions/chef/client.rb
@@ -0,0 +1,22 @@
+# Force loading Chef Config to fix a bad dependency tree. See
+# https://github.com/opscode/chef/issues/2703 for more information.
+require 'chef/config'
+
+# Providers has to be included before client... probably a weird
+# include missing in Chef-land, but we can make sure we get it right anyway.
+require 'chef/providers'
+require 'chef/client'
+
+# @private
+class Chef::Client
+  attr_reader :events
+
+  #
+  # Don't actually run ohai (we have fake data for that)
+  #
+  # @see Chef::Client#run_ohai
+  #
+  def run_ohai
+    # noop
+  end
+end
diff --git a/lib/chefspec/extensions/chef/conditional.rb b/lib/chefspec/extensions/chef/conditional.rb
new file mode 100644
index 0000000..3c7ed98
--- /dev/null
+++ b/lib/chefspec/extensions/chef/conditional.rb
@@ -0,0 +1,14 @@
+require 'chef/resource/conditional'
+
+class Chef::Resource::Conditional
+  # @see Chef::Resource::Conditional#evaluate_command
+  def evaluate_command
+    stub = ChefSpec::Stubs::CommandRegistry.stub_for(@command)
+
+    if stub.nil?
+      raise ChefSpec::Error::CommandNotStubbed.new(args: [@command])
+    end
+
+    stub.result
+  end
+end
diff --git a/lib/chefspec/extensions/chef/cookbook_uploader.rb b/lib/chefspec/extensions/chef/cookbook_uploader.rb
new file mode 100644
index 0000000..3d819b4
--- /dev/null
+++ b/lib/chefspec/extensions/chef/cookbook_uploader.rb
@@ -0,0 +1,11 @@
+require 'chef/cookbook_uploader'
+
+class Chef::CookbookUploader
+  #
+  # Don't validate uploaded cookbooks. Validating a cookbook takes *forever*
+  # to complete. It's just not worth it...
+  #
+  def validate_cookbooks
+    # noop
+  end
+end
diff --git a/lib/chefspec/extensions/chef/data_query.rb b/lib/chefspec/extensions/chef/data_query.rb
new file mode 100644
index 0000000..7a5c441
--- /dev/null
+++ b/lib/chefspec/extensions/chef/data_query.rb
@@ -0,0 +1,47 @@
+require 'chef/dsl/data_query'
+
+module Chef::DSL::DataQuery
+  # @see Chef::DSL::DataQuery#search
+  alias_method :old_search, :search
+  def search(*args, &block)
+    return old_search(*args, &block) unless Chef::Config[:solo]
+
+    type  = args[0]
+    query = args[1] || '*:*'
+    stub = ChefSpec::Stubs::SearchRegistry.stub_for(type, query)
+
+    if stub.nil?
+      raise ChefSpec::Error::SearchNotStubbed.new(args: [type, query])
+    end
+
+    stub.result
+  end
+
+  # @see Chef::DSL::DataQuery#data_bag
+  alias_method :old_data_bag, :data_bag
+  def data_bag(bag)
+    return old_data_bag(bag) unless Chef::Config[:solo]
+
+    stub = ChefSpec::Stubs::DataBagRegistry.stub_for(bag)
+
+    if stub.nil?
+      raise ChefSpec::Error::DataBagNotStubbed.new(args: [bag])
+    end
+
+    stub.result
+  end
+
+  # @see Chef::DSL::DataQuery#data_bag_item
+  alias_method :old_data_bag_item, :data_bag_item
+  def data_bag_item(bag, id)
+    return old_data_bag_item(bag, id) unless Chef::Config[:solo]
+
+    stub = ChefSpec::Stubs::DataBagItemRegistry.stub_for(bag, id)
+
+    if stub.nil?
+      raise ChefSpec::Error::DataBagItemNotStubbed.new(args: [bag, id])
+    end
+
+    stub.result
+  end
+end
diff --git a/lib/chefspec/extensions/chef/lwrp_base.rb b/lib/chefspec/extensions/chef/lwrp_base.rb
new file mode 100644
index 0000000..1195dfe
--- /dev/null
+++ b/lib/chefspec/extensions/chef/lwrp_base.rb
@@ -0,0 +1,23 @@
+# Override Chef LWRP creation to remove existing class to avoid redefinition warnings.
+class Chef
+  class Provider
+    class LWRPBase < Provider
+      module InlineResources
+        module ClassMethods
+          #
+          # For LWRPs that call +use_inline_resources+, do not create another
+          # +resource_collection+ so that everything is added to the parent
+          # +resource_collection+.
+          #
+          # @param [String] name
+          #
+          # @override Chef::Provider::LWRPBase::InlineResources::ClassMethods#action
+          #
+          def action(name, &block)
+            define_method("action_#{name}", &block)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/extensions/chef/resource.rb b/lib/chefspec/extensions/chef/resource.rb
new file mode 100644
index 0000000..6b267ad
--- /dev/null
+++ b/lib/chefspec/extensions/chef/resource.rb
@@ -0,0 +1,49 @@
+require 'chef/resource'
+
+class Chef::Resource
+  alias_method :old_initialize, :initialize
+  def initialize(*args)
+    @performed_actions = {}
+    old_initialize(*args)
+  end
+
+  alias_method :old_run_action, :run_action
+  def run_action(action, notification_type = nil, notifying_resource = nil)
+    resolve_notification_references
+    validate_action(action)
+
+    Chef::Log.info("Processing #{self} action #{action} (#{defined_at})")
+
+    ChefSpec::Coverage.add(self)
+
+    unless should_skip?(action)
+      if node.runner.step_into?(self)
+        instance_eval { @not_if = []; @only_if = [] }
+        old_run_action(action, notification_type, notifying_resource)
+      end
+
+      if node.runner.compiling?
+        perform_action(action, compile_time: true)
+      else
+        perform_action(action, converge_time: true)
+      end
+    end
+  end
+
+  def perform_action(action, options = {})
+    @performed_actions[action.to_sym] ||= {}
+    @performed_actions[action.to_sym].merge!(options)
+  end
+
+  def performed_action(action)
+    @performed_actions[action.to_sym]
+  end
+
+  def performed_action?(action)
+    !!performed_action(action)
+  end
+
+  def performed_actions
+    @performed_actions.keys
+  end
+end
diff --git a/lib/chefspec/extensions/chef/resource/freebsd_package.rb b/lib/chefspec/extensions/chef/resource/freebsd_package.rb
new file mode 100644
index 0000000..d78b0ed
--- /dev/null
+++ b/lib/chefspec/extensions/chef/resource/freebsd_package.rb
@@ -0,0 +1,19 @@
+require 'chef/resource/freebsd_package'
+
+class Chef
+  class Resource
+    class FreebsdPackage < Chef::Resource::Package
+      #
+      # Chef decided it was a good idea to just shellout inside of a resource.
+      # Not only is that a horrible fucking idea, but I got flak when I asked
+      # to change it. So we are just going to monkey patch the fucking thing so
+      # it does not shell out.
+      #
+      # @return [false]
+      #
+      def supports_pkgng?
+        false
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/extensions/chef/securable.rb b/lib/chefspec/extensions/chef/securable.rb
new file mode 100644
index 0000000..76cc0f2
--- /dev/null
+++ b/lib/chefspec/extensions/chef/securable.rb
@@ -0,0 +1,19 @@
+require 'chef/mixin/securable'
+
+class Chef
+  module Mixin
+    module Securable
+      # In Chef, this module is only included if the RUBY_PLATFORM is
+      # Windows-like. In ChefSpec, we want to include this, regardless of the
+      # platform, becuase this module holds the `inherits` attribute, which is
+      # critical in testing Windows resources.
+      include WindowsSecurableAttributes
+
+      def self.included(including_class)
+        including_class.extend(WindowsMacros)
+        including_class.rights_attribute(:rights)
+        including_class.rights_attribute(:deny_rights)
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/formatter.rb b/lib/chefspec/formatter.rb
new file mode 100644
index 0000000..1a9c72d
--- /dev/null
+++ b/lib/chefspec/formatter.rb
@@ -0,0 +1,270 @@
+require 'chef/formatters/base'
+require 'chef/formatters/error_mapper'
+
+module ChefSpec
+  class ChefFormatter < Chef::Formatters::Base
+    cli_name :chefspec
+
+    # Called at the very start of a Chef Run
+    def run_start(version); end
+
+    def run_started(run_status); end
+
+    # Called at the end a successful Chef run.
+    def run_completed(node); end
+
+    # Called at the end of a failed Chef run.
+    def run_failed(exception); end
+
+    # Called right after ohai runs.
+    def ohai_completed(node); end
+
+    # Already have a client key, assuming this node has registered.
+    def skipping_registration(node_name, config); end
+
+    # About to attempt to register as +node_name+
+    def registration_start(node_name, config); end
+
+    def registration_completed; end
+
+    # Failed to register this client with the server.
+    def registration_failed(node_name, exception, config)
+      expecting_exception(exception) do
+        description = Chef::Formatters::ErrorMapper.registration_failed(node_name, exception, config)
+        display_error(description)
+      end
+    end
+
+    # Called before Chef client loads the node data from the server
+    def node_load_start(node_name, config); end
+
+    # Failed to load node data from the server
+    def node_load_failed(node_name, exception, config)
+      expecting_exception(exception) do
+        description = Chef::Formatters::ErrorMapper.node_load_failed(node_name, exception, config)
+        display_error(description)
+      end
+    end
+
+    # Error expanding the run list
+    def run_list_expand_failed(node, exception)
+      expecting_exception(exception) do
+        description = Chef::Formatters::ErrorMapper.run_list_expand_failed(node, exception)
+        display_error(description)
+      end
+    end
+
+    # Called after Chef client has loaded the node data.
+    # Default and override attrs from roles have been computed, but not yet applied.
+    # Normal attrs from JSON have been added to the node.
+    def node_load_completed(node, expanded_run_list, config); end
+
+    # Called before the cookbook collection is fetched from the server.
+    def cookbook_resolution_start(expanded_run_list); end
+
+    # Called when there is an error getting the cookbook collection from the
+    # server.
+    def cookbook_resolution_failed(expanded_run_list, exception)
+      expecting_exception(exception) do
+        description = Chef::Formatters::ErrorMapper.cookbook_resolution_failed(expanded_run_list, exception)
+        display_error(description)
+      end
+    end
+
+    # Called when the cookbook collection is returned from the server.
+    def cookbook_resolution_complete(cookbook_collection); end
+
+    # Called before unneeded cookbooks are removed
+    def cookbook_clean_start; end
+
+    # Called after the file at +path+ is removed. It may be removed if the
+    # cookbook containing it was removed from the run list, or if the file was
+    # removed from the cookbook.
+    def removed_cookbook_file(path); end
+
+    # Called when cookbook cleaning is finished.
+    def cookbook_clean_complete; end
+
+    # Called before cookbook sync starts
+    def cookbook_sync_start(cookbook_count); end
+
+    # Called when cookbook +cookbook_name+ has been sync'd
+    def synchronized_cookbook(cookbook_name); end
+
+    # Called when an individual file in a cookbook has been updated
+    def updated_cookbook_file(cookbook_name, path); end
+
+    # Called when an error occurs during cookbook sync
+    def cookbook_sync_failed(cookbooks, exception)
+      expecting_exception(exception) do
+        description = Chef::Formatters::ErrorMapper.cookbook_sync_failed(cookbooks, exception)
+        display_error(description)
+      end
+    end
+
+    # Called after all cookbooks have been sync'd.
+    def cookbook_sync_complete; end
+
+    # Called when library file loading starts
+    def library_load_start(file_count); end
+
+    # Called when library file has been loaded
+    def library_file_loaded(path); end
+
+    # Called when a library file has an error on load.
+    def library_file_load_failed(path, exception)
+      file_load_failed(path, exception)
+    end
+
+    # Called when library file loading has finished
+    def library_load_complete; end
+
+    # Called when LWRP loading starts
+    def lwrp_load_start(lwrp_file_count); end
+
+    # Called after a LWR or LWP has been loaded
+    def lwrp_file_loaded(path); end
+
+    # Called after a LWR or LWP file errors on load
+    def lwrp_file_load_failed(path, exception)
+      file_load_failed(path, exception)
+    end
+
+    # Called when LWRPs are finished loading
+    def lwrp_load_complete; end
+
+    # Called before attribute files are loaded
+    def attribute_load_start(attribute_file_count); end
+
+    # Called after the attribute file is loaded
+    def attribute_file_loaded(path); end
+
+    # Called when an attribute file fails to load.
+    def attribute_file_load_failed(path, exception)
+      file_load_failed(path, exception)
+    end
+
+    # Called when attribute file loading is finished
+    def attribute_load_complete; end
+
+    # Called before resource definitions are loaded
+    def definition_load_start(definition_file_count); end
+
+    # Called when a resource definition has been loaded
+    def definition_file_loaded(path); end
+
+    # Called when a resource definition file fails to load
+    def definition_file_load_failed(path, exception)
+      file_load_failed(path, exception)
+    end
+
+    # Called when resource defintions are done loading
+    def definition_load_complete; end
+
+    # Called before recipes are loaded
+    def recipe_load_start(recipe_count); end
+
+    # Called after the recipe has been loaded
+    def recipe_file_loaded(path); end
+
+    # Called after a recipe file fails to load
+    def recipe_file_load_failed(path, exception)
+      file_load_failed(path, exception)
+    end
+
+    # Called when a recipe cannot be resolved
+    def recipe_not_found(exception)
+      expecting_exception(exception) do
+        description = Chef::Formatters::ErrorMapper.file_load_failed(nil, exception)
+        display_error(description)
+      end
+    end
+
+    # Called when recipes have been loaded.
+    def recipe_load_complete; end
+
+    # Called before convergence starts
+    def converge_start(run_context); end
+
+    # Called when the converge phase is finished.
+    def converge_complete; end
+
+    # Called before action is executed on a resource.
+    def resource_action_start(resource, action, notification_type=nil, notifier=nil); end
+
+    # Called when a resource fails, but will retry.
+    def resource_failed_retriable(resource, action, retry_count, exception); end
+
+    # Called when a resource fails and will not be retried.
+    def resource_failed(resource, action, exception)
+      expecting_exception(exception) do
+        description = Chef::Formatters::ErrorMapper.resource_failed(resource, action, exception)
+        display_error(description)
+      end
+    end
+
+    # Called when a resource action has been skipped b/c of a conditional
+    def resource_skipped(resource, action, conditional); end
+
+    # Called when a resource action has been completed
+    def resource_completed(resource); end
+
+    # Called after #load_current_resource has run.
+    def resource_current_state_loaded(resource, action, current_resource); end
+
+    # Called when resource current state load is skipped due to the provider
+    # not supporting whyrun mode.
+    def resource_current_state_load_bypassed(resource, action, current_resource); end
+
+    # Called when evaluating a resource that does not support whyrun in whyrun mode
+    def resource_bypassed(resource, action, current_resource); end
+
+    # Called when a resource has no converge actions, e.g., it was already correct.
+    def resource_up_to_date(resource, action); end
+
+    # Called when a change has been made to a resource. May be called multiple
+    # times per resource, e.g., a file may have its content updated, and then
+    # its permissions updated.
+    def resource_update_applied(resource, action, update); end
+
+    # Called after a resource has been completely converged, but only if
+    # modifications were made.
+    def resource_updated(resource, action); end
+
+    # Called before handlers run
+    def handlers_start(handler_count); end
+
+    # Called after an individual handler has run
+    def handler_executed(handler); end
+
+    # Called after all handlers have executed
+    def handlers_completed; end
+
+    # Called when an assertion declared by a provider fails
+    def provider_requirement_failed(action, resource, exception, message); end
+
+    # Called when a provider makes an assumption after a failed assertion
+    # in whyrun mode, in order to allow execution to continue
+    def whyrun_assumption(action, resource, message); end
+
+    # An uncategorized message. This supports the case that a user needs to
+    # pass output that doesn't fit into one of the callbacks above. Note that
+    # there's no semantic information about the content or importance of the
+    # message. That means that if you're using this too often, you should add a
+    # callback for it.
+    def msg(message); end
+
+    private
+
+    def file_load_failed(path, exception)
+      expecting_exception(exception) do
+        description = Chef::Formatters::ErrorMapper.file_load_failed(path, exception)
+        display_error(description)
+      end
+    end
+
+    def expecting_exception(exception, &block)
+      yield unless ChefSpec::ExpectException.new(exception).expected?
+    end
+  end
+end
diff --git a/lib/chefspec/librarian.rb b/lib/chefspec/librarian.rb
new file mode 100644
index 0000000..d21a03e
--- /dev/null
+++ b/lib/chefspec/librarian.rb
@@ -0,0 +1,50 @@
+begin
+  require 'librarian/chef/environment'
+  require 'librarian/action/resolve'
+  require 'librarian/action/install'
+rescue LoadError
+  raise ChefSpec::Error::GemLoadError.new(
+    gem: 'librarian-chef', name: 'Librarian')
+end
+
+module ChefSpec
+  class Librarian
+    class << self
+      extend Forwardable
+      def_delegators :instance, :setup!, :teardown!
+    end
+
+    include Singleton
+
+    def initialize
+      @tmpdir = Dir.mktmpdir
+    end
+
+    #
+    # Setup and install the necessary dependencies in the temporary directory.
+    #
+    def setup!
+      env = ::Librarian::Chef::Environment.new(project_path: Dir.pwd)
+      @originalpath, env.config_db.local['path'] = env.config_db.local['path'], @tmpdir
+      ::Librarian::Action::Resolve.new(env).run
+      ::Librarian::Action::Install.new(env).run
+
+      ::RSpec.configure { |config| config.cookbook_path = @tmpdir }
+    end
+
+    #
+    # Remove the temporary directory and restore the librarian-chef cookbook path.
+    #
+    def teardown!
+      env = ::Librarian::Chef::Environment.new(project_path: Dir.pwd)
+      env.config_db.local['path'] = @originalpath
+
+      FileUtils.rm_rf(@tmpdir) if File.exist?(@tmpdir)
+    end
+  end
+end
+
+RSpec.configure do |config|
+  config.before(:suite) { ChefSpec::Librarian.setup! }
+  config.after(:suite)  { ChefSpec::Librarian.teardown! }
+end
diff --git a/lib/chefspec/macros.rb b/lib/chefspec/macros.rb
new file mode 100644
index 0000000..22139d2
--- /dev/null
+++ b/lib/chefspec/macros.rb
@@ -0,0 +1,222 @@
+module ChefSpec
+  module Macros
+    extend self
+
+    #
+    # The name of the currently running cookbook spec. Given the top-level
+    # +describe+ block is of the format:
+    #
+    #     describe 'my_cookbook::my_recipe' do
+    #       # ...
+    #     end
+    #
+    # The value of +described_cookbook+ is "my_cookbook".
+    #
+    # @example Using +described_cookbook+ in a context block
+    #   context "#{described_recipe} installs foo" do
+    #     # ...
+    #   end
+    #
+    #
+    # @return [String]
+    #
+    def described_cookbook
+      described_recipe.split('::').first
+    end
+
+    #
+    # The name of the currently running recipe spec. Given the top-level
+    # +describe+ block is of the format:
+    #
+    #     describe 'my_cookbook::my_recipe' do
+    #       # ...
+    #     end
+    #
+    # The value of +described_recipe+ is "my_cookbook::my_recipe".
+    #
+    # @example Using +described_recipe+ in the +ChefSpec::SoloRunner+
+    #   let(:chef_run) { ChefSpec::SoloRunner.new.converge(described_recipe) }
+    #
+    #
+    # @return [String]
+    #
+    def described_recipe
+      scope = self.is_a?(Class) ? self : self.class
+
+      metahash = scope.metadata
+      while metahash.has_key?(:parent_example_group)
+        metahash = metahash[:parent_example_group]
+      end
+
+      metahash[:description].to_s
+    end
+
+    #
+    # Stub a shell command to return a particular value without
+    # shelling out to the system.
+    #
+    # @example stubbing a command to return true
+    #   stub_command('test -f /tmp/bacon').and_return(true)
+    #
+    # @example stubbing a block that is evaluated at runtime
+    #   stub_command('test -f /tmp/bacon') { MyClass.check? }
+    #
+    # @example stubbing a command that matches a pattern
+    #   stub_command(/test \-f/).and_return(true)
+    #
+    # @example stubbing a command that raises an exception
+    #   stub_command('test -f /tmp/bacon').and_raise(SomeException)
+    #
+    #
+    # @param [String, Regexp] command
+    #   the command to stub
+    #
+    # @return [ChefSpec::CommandStub]
+    #
+    def stub_command(command, &block)
+      Stubs::CommandRegistry.register(Stubs::CommandStub.new(command, &block))
+    end
+
+    #
+    # Stub a Chef call to load a data_bag.
+    #
+    # @example stubbing a data_bag to return an empty Array
+    #   stub_data_bag('users').and_return([])
+    #
+    # @example stubbing a data_bag with a block that is evaluated at runtime
+    #   stub_data_bag('users') { JSON.parse(File.read('fixtures/data_bag.json')) }
+    #
+    # @example stubbing a data_bag to return an Array of Hashes
+    #   stub_data_bag('users').and_return([
+    #     { id: 'bacon', comment: 'delicious' },
+    #     { id: 'ham', comment: 'also good' }
+    #   ])
+    #
+    # @example stubbing a data_bag to raise an exception
+    #   stub_data_bag('users').and_raise(Chef::Exceptions::PrivateKeyMissing)
+    #
+    #
+    # @param [String, Symbol] bag
+    #   the name of the data bag to stub
+    #
+    # @return [ChefSpec::DataBagStub]
+    #
+    def stub_data_bag(bag, &block)
+      Stubs::DataBagRegistry.register(Stubs::DataBagStub.new(bag, &block))
+    end
+
+    #
+    # Stub a Chef data_bag call.
+    #
+    # @example stubbing a data_bag_item to return a Hash of data
+    #   stub_data_bag_item('users', 'svargo').and_return({
+    #     id: 'svargo',
+    #     name: 'Seth Vargo'
+    #   })
+    #
+    # @example stubbing a data_bag_item with a block that is evaluated at runtime
+    #   stub_data_bag_item('users', 'svargo') { JSON.parse(File.read('fixtures/data_bag_item.json')) }
+    #
+    # @example stubbing a data_bag_item to raise an exception
+    #   stub_data_bag('users', 'svargo').and_raise(Chef::Exceptions::PrivateKeyMissing)
+    #
+    #
+    # @param [String, Symbol] bag
+    #   the name of the data bag to find the item in
+    # @param [String] id
+    #   the id of the data bag item to stub
+    #
+    # @return [ChefSpec::DataBagItemStub]
+    #
+    def stub_data_bag_item(bag, id, &block)
+      Stubs::DataBagItemRegistry.register(Stubs::DataBagItemStub.new(bag, id, &block))
+    end
+
+    #
+    # Creates a fake Chef::Node for use in testing.
+    #
+    # @example mocking a simple node
+    #   stub_node('mynode.example')
+    #
+    # @example mocking a specific platform and version
+    #   stub_node('mynode.example', platform: 'ubuntu', version: '12.04')
+    #
+    # @example overriding a specific ohai attribute
+    #   stub_node('mynode.example', ohai: { ipaddress: '1.2.3.4' })
+    #
+    # @example stubbing a node based on a JSON fixture
+    #   stub_node('mynode.example', path: File.join('fixtures', 'nodes', 'default.json'))
+    #
+    # @example setting specific attributes
+    #   stub_node('mynode.example') do |node|
+    #     node.default['attribute']['thing'] = 'value'
+    #   end
+    #
+    #
+    # @param [String] name
+    #   the name of the node
+    # @param [Hash] options
+    #   the list of options for the node
+    #
+    # @option options [Symbol] :platform
+    #   the platform to mock
+    # @option options [Symbol] :version
+    #   the version of the platform to mock
+    # @option options [Symbol] :path
+    #   filepath to a JSON file to pull a node from
+    # @option options [Hash] :ohai
+    #   a Hash of Ohai attributes to mock on the node
+    #
+    # @return [Chef::Node]
+    #
+    def stub_node(*args, &block)
+      options = args.last.is_a?(Hash) ? args.pop : {}
+      name    = args.first || 'node.example'
+
+      fauxhai = Fauxhai.mock(options).data
+      fauxhai = fauxhai.merge(options[:ohai] || {})
+      fauxhai = Mash.new(fauxhai)
+
+      node = Chef::Node.new
+      node.name(name)
+      node.automatic_attrs = fauxhai
+      node.instance_eval(&block) if block_given?
+      node
+    end
+
+    #
+    # Stub a Chef search to return pre-defined data. When providing a value,
+    # the value is automatically mashified (to the best of ChefSpec's abilities)
+    # to ease in use.
+    #
+    # @example stubbing a search to return nothing
+    #   stub_search(:node)
+    #
+    # @example stubbing a search with a query
+    #   stub_search(:node, 'name:*')
+    #
+    # @example stubbing a search with a query as a regex
+    #   stub_search(:node, /name:(.+)/)
+    #
+    # @example stubbing a search with a block that is evaluated at runtime
+    #   stub_search(:node) { JSON.parse(File.read('fixtures/nodes.json')) }
+    #
+    # @example stubbing a search to return a fixed value
+    #   stub_search(:node).and_return([ { a: 'b' } ])
+    #
+    # @example stubbing a search to raise an exception
+    #   stub_search(:node).and_raise(Chef::Exceptions::PrivateKeyMissing)
+    #
+    #
+    # @param [String, Symbol] type
+    #   the type to search to stub
+    # @param [String, Symbol, nil] query
+    #   the query to stub
+    #
+    # @return [ChefSpec::SearchStub]
+    #
+    def stub_search(type, query = '*:*', &block)
+      Stubs::SearchRegistry.register(Stubs::SearchStub.new(type, query, &block))
+    end
+  end
+end
diff --git a/lib/chefspec/matchers.rb b/lib/chefspec/matchers.rb
new file mode 100644
index 0000000..b85d41f
--- /dev/null
+++ b/lib/chefspec/matchers.rb
@@ -0,0 +1,12 @@
+module ChefSpec
+  module Matchers
+    require_relative 'matchers/do_nothing_matcher'
+    require_relative 'matchers/include_recipe_matcher'
+    require_relative 'matchers/link_to_matcher'
+    require_relative 'matchers/notifications_matcher'
+    require_relative 'matchers/render_file_matcher'
+    require_relative 'matchers/resource_matcher'
+    require_relative 'matchers/state_attrs_matcher'
+    require_relative 'matchers/subscribes_matcher'
+  end
+end
diff --git a/lib/chefspec/matchers/do_nothing_matcher.rb b/lib/chefspec/matchers/do_nothing_matcher.rb
new file mode 100644
index 0000000..29010c8
--- /dev/null
+++ b/lib/chefspec/matchers/do_nothing_matcher.rb
@@ -0,0 +1,52 @@
+module ChefSpec::Matchers
+  class DoNothingMatcher
+    def matches?(resource)
+      @resource = resource
+
+      if @resource
+        ChefSpec::Coverage.cover!(@resource)
+
+        actions = @resource.performed_actions
+        actions.empty? || actions == [:nothing]
+      else
+        false
+      end
+    end
+
+    def description
+      'do nothing'
+    end
+
+    def failure_message
+      if @resource
+        message =  %|expected #{@resource} to do nothing, but the following |
+        message << %|actions were performed:|
+        message << %|\n\n|
+        @resource.performed_actions.each do |action|
+          message << %|  :#{action}|
+        end
+        message
+      else
+        message =  %|expected _something_ to do nothing, but the _something_ |
+        message << %|you gave me was nil! If you are running a test like:|
+        message << %|\n\n|
+        message << %|  expect(_something_).to do_nothing|
+        message << %|\n\n|
+        message << %|make sure that `_something_` exists, because I got nil!|
+        message
+      end
+    end
+
+    def failure_message_when_negated
+      if @resource
+        message =  %|expected #{@resource} to do something, but no actions |
+        message << %|were performed.|
+        message
+      else
+        message =  %|expected _something_ to do something, but no actions |
+        message << %|were performed.|
+        message
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/matchers/include_recipe_matcher.rb b/lib/chefspec/matchers/include_recipe_matcher.rb
new file mode 100644
index 0000000..c61ff3a
--- /dev/null
+++ b/lib/chefspec/matchers/include_recipe_matcher.rb
@@ -0,0 +1,46 @@
+module ChefSpec::Matchers
+  class IncludeRecipeMatcher
+    def initialize(recipe_name)
+      @recipe_name = with_default(recipe_name)
+    end
+
+    def matches?(runner)
+      @runner = runner
+      loaded_recipes.include?(@recipe_name)
+    end
+
+    def description
+      %Q{include recipe "#{@recipe_name}"}
+    end
+
+    def failure_message
+      %Q{expected #{loaded_recipes.inspect} to include "#{@recipe_name}"}
+    end
+
+    def failure_message_when_negated
+      %Q{expected "#{@recipe_name}" to not be included}
+    end
+
+    private
+
+    #
+    # Automatically appends "+::default+" to recipes that need them.
+    #
+    # @param [String] name
+    #
+    # @return [String]
+    #
+    def with_default(name)
+      name.include?('::') ? name : "#{name}::default"
+    end
+
+    #
+    # The list of loaded recipes on the Chef run (normalized)
+    #
+    # @return [Array<String>]
+    #
+    def loaded_recipes
+      @runner.run_context.loaded_recipes.map { |name| with_default(name) }
+    end
+  end
+end
diff --git a/lib/chefspec/matchers/link_to_matcher.rb b/lib/chefspec/matchers/link_to_matcher.rb
new file mode 100644
index 0000000..c5fd15c
--- /dev/null
+++ b/lib/chefspec/matchers/link_to_matcher.rb
@@ -0,0 +1,37 @@
+module ChefSpec::Matchers
+  class LinkToMatcher
+    def initialize(path)
+      @path = path
+    end
+
+    def matches?(link)
+      @link = link
+
+      if @link
+        ChefSpec::Coverage.cover!(@link)
+
+        @link.is_a?(Chef::Resource::Link) &&
+        @link.performed_action?(:create) &&
+        @path === @link.to
+      else
+        false
+      end
+    end
+
+    def description
+      %Q{link to "#{@path}"}
+    end
+
+    def failure_message
+      if @link.nil?
+        %Q{expected "link[#{@path}]" with action :create to be in Chef run}
+      else
+        %Q{expected "#{@link}" to link to "#{@path}" but was "#{@link.to}"}
+      end
+    end
+
+    def failure_message_when_negated
+      %Q{expected "#{@link}" to not link to "#{@path}"}
+    end
+  end
+end
diff --git a/lib/chefspec/matchers/notifications_matcher.rb b/lib/chefspec/matchers/notifications_matcher.rb
new file mode 100644
index 0000000..0780114
--- /dev/null
+++ b/lib/chefspec/matchers/notifications_matcher.rb
@@ -0,0 +1,121 @@
+module ChefSpec::Matchers
+  class NotificationsMatcher
+    include ChefSpec::Normalize
+
+    def initialize(signature)
+      signature.match(/^([^\[]*)\[(.*)\]$/)
+      @expected_resource_type = $1
+      @expected_resource_name = $2
+    end
+
+    def matches?(resource)
+      @resource = resource
+
+      if @resource
+        block = Proc.new do |notified|
+          resource_name(notified.resource).to_s == @expected_resource_type &&
+          (@expected_resource_name === notified.resource.identity.to_s || @expected_resource_name === notified.resource.name.to_s) &&
+          matches_action?(notified)
+        end
+
+        if @immediately
+          immediate_notifications.any?(&block)
+        elsif @delayed
+          delayed_notifications.any?(&block)
+        else
+          all_notifications.any?(&block)
+        end
+      end
+    end
+
+    def to(action)
+      @action = action.to_sym
+      self
+    end
+
+    def immediately
+      @immediately = true
+      self
+    end
+
+    def delayed
+      @delayed = true
+      self
+    end
+
+    def description
+      message = %Q{notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
+      message << " with action :#{@action}" if @action
+      message << " immediately" if @immediately
+      message << " delayed" if @delayed
+      message
+    end
+
+    def failure_message
+      if @resource
+        message = %Q{expected "#{@resource}" to notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
+        message << " with action :#{@action}" if @action
+        message << " immediately" if @immediately
+        message << " delayed" if @delayed
+        message << ", but did not."
+        message << "\n\n"
+        message << "Other notifications were:\n\n#{format_notifications}"
+        message << "\n "
+        message
+      else
+        message = %Q{expected _something_ to notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
+        message << " with action :#{@action}" if @action
+        message << " immediately" if @immediately
+        message << " delayed" if @delayed
+        message << ", but the _something_ you gave me was nil! If you are running a test like:"
+        message << "\n\n"
+        message << "  expect(_something_).to notify('...')"
+        message << "\n\n"
+        message << "Make sure that `_something_` exists, because I got nil"
+        message << "\n "
+        message
+      end
+    end
+
+    def failure_message_when_negated
+      if @resource
+        message = %Q{expected "#{@resource}" to not notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
+        message << ", but it did."
+        message
+      end
+    end
+
+    private
+
+    def all_notifications
+      immediate_notifications + delayed_notifications
+    end
+
+    def immediate_notifications
+      @resource.immediate_notifications
+    end
+
+    def delayed_notifications
+      @resource.delayed_notifications
+    end
+
+    def matches_action?(notification)
+      return true if @action.nil?
+      @action == notification.action.to_sym
+    end
+
+    def format_notification(notification)
+      notifying_resource = notification.notifying_resource
+      resource = notification.resource
+      type = notification.notifying_resource.immediate_notifications.include?(notification) ? :immediately : :delayed
+
+      %Q{  "#{notifying_resource.to_s}" notifies "#{resource_name(resource)}[#{resource.name}]" to :#{notification.action}, :#{type}}
+    end
+
+    def format_notifications
+      all_notifications.map do |notification|
+        '  ' + format_notification(notification)
+      end.join("\n")
+    end
+  end
+end
diff --git a/lib/chefspec/matchers/render_file_matcher.rb b/lib/chefspec/matchers/render_file_matcher.rb
new file mode 100644
index 0000000..ba0c77a
--- /dev/null
+++ b/lib/chefspec/matchers/render_file_matcher.rb
@@ -0,0 +1,129 @@
+module ChefSpec::Matchers
+  class RenderFileMatcher
+    def initialize(path)
+      @path = path
+    end
+
+    def matches?(runner)
+      @runner = runner
+
+      if resource
+        ChefSpec::Coverage.cover!(resource)
+        has_create_action? && matches_content?
+      else
+        false
+      end
+    end
+
+    def with_content(expected_content = nil, &block)
+      if expected_content && block
+        raise ArgumentError, "Cannot specify expected content and a block!"
+      elsif expected_content
+        @expected_content = expected_content
+      elsif block
+        @expected_content = block
+      else
+        raise ArgumentError, "Must specify expected content or a block!"
+      end
+
+      self
+    end
+
+    def description
+      message = %Q{render file "#{@path}"}
+      if @expected_content
+        if @expected_content.to_s.include?("\n")
+          message << " with content <suppressed>"
+        else
+          message << " with content #{@expected_content.inspect}"
+        end
+      end
+      message
+    end
+
+    def failure_message
+      message = %Q{expected Chef run to render "#{@path}"}
+      if @expected_content
+        message << " matching:"
+        message << "\n\n"
+        message << expected_content_message
+        message << "\n\n"
+        message << "but got:"
+        message << "\n\n"
+        message << @actual_content.to_s
+        message << "\n "
+      end
+      message
+    end
+
+    def failure_message_when_negated
+      message = %Q{expected file "#{@path}"}
+      if @expected_content
+        message << " matching:"
+        message << "\n\n"
+        message << expected_content_message
+        message << "\n\n"
+      end
+      message << " to not be in Chef run"
+      message
+    end
+
+    private
+
+    def expected_content_message
+      if RSpec::Matchers.is_a_matcher?(@expected_content) && @expected_content.respond_to?(:description)
+        @expected_content.description
+      elsif @expected_content.is_a?(Proc)
+        "(the result of a proc)"
+      else
+        @expected_content.to_s
+      end
+    end
+
+    def resource
+      @resource ||= @runner.find_resource(:cookbook_file, @path) ||
+                    @runner.find_resource(:file, @path) ||
+                    @runner.find_resource(:template, @path)
+    end
+
+    #
+    # Determines if the given resource has a create-like action.
+    #
+    # @param [Chef::Resource] resource
+    #
+    # @return [true, false]
+    #
+    def has_create_action?
+      [:create, :create_if_missing].any? { |action| resource.performed_action?(action) }
+    end
+
+    #
+    # Determines if the resources content matches the expected content.
+    #
+    # @param [Chef::Resource] resource
+    #
+    # @return [true, false]
+    #
+    def matches_content?
+      return true if @expected_content.nil?
+
+      @actual_content = ChefSpec::Renderer.new(@runner, resource).content
+
+      return false if @actual_content.nil?
+
+      if @expected_content.is_a?(Regexp)
+        @actual_content =~ @expected_content
+      elsif RSpec::Matchers.is_a_matcher?(@expected_content)
+        @expected_content.matches?(@actual_content)
+      elsif @expected_content.is_a?(Proc)
+        @expected_content.call(@actual_content)
+        # Weird RSpecish, but that block will return false for a negated check,
+        # so we always return true. The block will raise an exception if the
+        # assertion fails.
+        true
+      else
+        @actual_content.include?(@expected_content)
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/matchers/resource_matcher.rb b/lib/chefspec/matchers/resource_matcher.rb
new file mode 100644
index 0000000..1396c01
--- /dev/null
+++ b/lib/chefspec/matchers/resource_matcher.rb
@@ -0,0 +1,173 @@
+module ChefSpec::Matchers
+  class ResourceMatcher
+    def initialize(resource_name, expected_action, expected_identity)
+      @resource_name     = resource_name
+      @expected_action   = expected_action
+      @expected_identity = expected_identity
+    end
+
+    def with(parameters = {})
+      params.merge!(parameters)
+      self
+    end
+
+    def at_compile_time
+      raise ArgumentError, 'Cannot specify both .at_converge_time and .at_compile_time!' if @converge_time
+      @compile_time = true
+      self
+    end
+
+    def at_converge_time
+      raise ArgumentError, 'Cannot specify both .at_compile_time and .at_converge_time!' if @compile_time
+      @converge_time = true
+      self
+    end
+
+    #
+    # Allow users to specify fancy #with matchers.
+    #
+    def method_missing(m, *args, &block)
+      if m.to_s =~ /^with_(.+)$/
+        with($1.to_sym => args.first)
+        self
+      else
+        super
+      end
+    end
+
+    def description
+      %Q{#{@expected_action} #{@resource_name} "#{@expected_identity}"}
+    end
+
+    def matches?(runner)
+      @runner = runner
+
+      if resource
+        ChefSpec::Coverage.cover!(resource)
+        resource.performed_action?(@expected_action) && unmatched_parameters.empty? && correct_phase?
+      else
+        false
+      end
+    end
+
+    def failure_message
+      if resource
+        if resource.performed_action?(@expected_action)
+          if unmatched_parameters.empty?
+            if @compile_time
+              %Q{expected "#{resource.to_s}" to be run at compile time}
+            else
+              %Q{expected "#{resource.to_s}" to be run at converge time}
+            end
+          else
+            %Q{expected "#{resource.to_s}" to have parameters:} \
+            "\n\n" \
+            "  " + unmatched_parameters.collect { |parameter, h|
+              "#{parameter} #{h[:expected].inspect}, was #{h[:actual].inspect}"
+            }.join("\n  ")
+          end
+        else
+          %Q{expected "#{resource.to_s}" actions #{resource.performed_actions.inspect}} \
+          " to include :#{@expected_action}"
+        end
+      else
+        %Q{expected "#{@resource_name}[#{@expected_identity}]"} \
+        " with action :#{@expected_action} to be in Chef run." \
+        " Other #{@resource_name} resources:" \
+        "\n\n" \
+        "  " + similar_resources.map(&:to_s).join("\n  ") + "\n "
+      end
+    end
+
+    def failure_message_when_negated
+      if resource
+        message = %Q{expected "#{resource.to_s}" actions #{resource.performed_actions.inspect} to not exist}
+      else
+        message = %Q{expected "#{resource.to_s}" to not exist}
+      end
+
+      message << " at compile time"  if @compile_time
+      message << " at converge time" if @converge_time
+      message
+    end
+
+    private
+
+    def unmatched_parameters
+      return @_unmatched_parameters if @_unmatched_parameters
+
+      @_unmatched_parameters = {}
+
+      params.each do |parameter, expected|
+        unless matches_parameter?(parameter, expected)
+          @_unmatched_parameters[parameter] = {
+            expected: expected,
+            actual:   safe_send(parameter),
+          }
+        end
+      end
+
+      @_unmatched_parameters
+    end
+
+    def matches_parameter?(parameter, expected)
+      value = safe_send(parameter)
+      if parameter == :source
+        # Chef 11+ stores the source parameter internally as an Array
+        Array(expected) == Array(value)
+      elsif expected.kind_of?(Class)
+        # Ruby can't compare classes with ===
+        expected == value
+      else
+        expected === value
+      end
+    end
+
+    def correct_phase?
+      if @compile_time
+        resource.performed_action(@expected_action)[:compile_time]
+      elsif @converge_time
+        resource.performed_action(@expected_action)[:converge_time]
+      else
+        true
+      end
+    end
+
+    def safe_send(parameter)
+      resource.send(parameter)
+    rescue NoMethodError
+      nil
+    end
+
+    #
+    # Any other resources in the Chef run that have the same resource
+    # type. Used by {failure_message} to be ultra helpful.
+    #
+    # @return [Array<Chef::Resource>]
+    #
+    def similar_resources
+      @_similar_resources ||= @runner.find_resources(@resource_name)
+    end
+
+    #
+    # Find the resource in the Chef run by the given class name and
+    # resource identity/name.
+    #
+    # @see ChefSpec::SoloRunner#find_resource
+    #
+    # @return [Chef::Resource, nil]
+    #
+    def resource
+      @_resource ||= @runner.find_resource(@resource_name, @expected_identity)
+    end
+
+    #
+    # The list of parameters passed to the {with} matcher.
+    #
+    # @return [Hash]
+    #
+    def params
+      @_params ||= {}
+    end
+  end
+end
diff --git a/lib/chefspec/matchers/state_attrs_matcher.rb b/lib/chefspec/matchers/state_attrs_matcher.rb
new file mode 100644
index 0000000..f12a5bf
--- /dev/null
+++ b/lib/chefspec/matchers/state_attrs_matcher.rb
@@ -0,0 +1,71 @@
+module ChefSpec::Matchers
+  class StateAttrsMatcher
+    #
+    # Create a new state_attrs matcher.
+    #
+    # @param [Array] state_attrs
+    #
+    def initialize(state_attrs)
+      @expected_attrs = state_attrs.map(&:to_sym)
+    end
+
+    def matches?(resource)
+      @resource = resource
+      @resource && matches_state_attrs?
+    end
+
+    def description
+      %Q{have state attributes #{@expected_attrs.inspect}}
+    end
+
+    def failure_message
+      if @resource
+        "expected #{state_attrs.inspect} to equal #{@expected_attrs.inspect}"
+      else
+        "expected _something_ to have state attributes, but the " \
+        "_something_ you gave me was nil!" \
+        "\n" \
+        "Ensure the resource exists before making assertions:" \
+        "\n\n" \
+        "  expect(resource).to be" \
+        "\n "
+      end
+    end
+
+    def failure_message_when_negated
+      if @resource
+        "expected #{state_attrs.inspect} to not equal " \
+        "#{@expected_attrs.inspect}"
+      else
+        "expected _something_ to not have state attributes, but the " \
+        "_something_ you gave me was nil!" \
+        "\n" \
+        "Ensure the resource exists before making assertions:" \
+        "\n\n" \
+        "  expect(resource).to be" \
+        "\n "
+      end
+    end
+
+    private
+
+    #
+    # Determine if all the expected state attributes are present on the
+    # given resource.
+    #
+    # @return [true, false]
+    #
+    def matches_state_attrs?
+      @expected_attrs == state_attrs
+    end
+
+    #
+    # The list of state attributes declared on the given resource.
+    #
+    # @return [Array<Symbol>]
+    #
+    def state_attrs
+      @resource.class.state_attrs.map(&:to_sym)
+    end
+  end
+end
diff --git a/lib/chefspec/matchers/subscribes_matcher.rb b/lib/chefspec/matchers/subscribes_matcher.rb
new file mode 100644
index 0000000..be2bc64
--- /dev/null
+++ b/lib/chefspec/matchers/subscribes_matcher.rb
@@ -0,0 +1,63 @@
+module ChefSpec::Matchers
+  class SubscribesMatcher
+    include ChefSpec::Normalize
+
+    def initialize(signature)
+      signature.match(/^([^\[]*)\[(.*)\]$/)
+      @expected_resource_type = $1
+      @expected_resource_name = $2
+    end
+
+    def matches?(resource)
+      @instance = ChefSpec::Matchers::NotificationsMatcher.new(resource.to_s)
+
+      if @action
+        @instance.to(@action)
+      end
+
+      if @immediately
+        @instance.immediately
+      end
+
+      if @delayed
+        @instance.delayed
+      end
+
+      if resource
+        runner   = resource.run_context.node.runner
+        expected = runner.find_resource(@expected_resource_type, @expected_resource_name)
+
+        @instance.matches?(expected)
+      else
+        @instance.matches?(nil)
+      end
+    end
+
+    def on(action)
+      @action = action
+      self
+    end
+
+    def immediately
+      @immediately = true
+      self
+    end
+
+    def delayed
+      @delayed = true
+      self
+    end
+
+    def description
+      @instance.description
+    end
+
+    def failure_message
+      @instance.failure_message
+    end
+
+    def failure_message_when_negated
+      @instance.failure_message_when_negated
+    end
+  end
+end
diff --git a/lib/chefspec/mixins/normalize.rb b/lib/chefspec/mixins/normalize.rb
new file mode 100644
index 0000000..10ba95c
--- /dev/null
+++ b/lib/chefspec/mixins/normalize.rb
@@ -0,0 +1,22 @@
+module ChefSpec
+  module Normalize
+    #
+    # Calculate the name of a resource, replacing dashes with underscores
+    # and converting symbols to strings and back again.
+    #
+    # @param [String, Chef::Resource] thing
+    #
+    # @return [Symbol]
+    #
+    def resource_name(thing)
+      if thing.respond_to?(:declared_type)
+        name = thing.declared_type
+      elsif thing.respond_to?(:resource_name)
+        name = thing.resource_name
+      else
+        name = thing
+      end
+      name.to_s.gsub('-', '_').to_sym
+    end
+  end
+end
diff --git a/lib/chefspec/renderer.rb b/lib/chefspec/renderer.rb
new file mode 100644
index 0000000..7928656
--- /dev/null
+++ b/lib/chefspec/renderer.rb
@@ -0,0 +1,145 @@
+begin
+  require 'chef/mixin/template'
+  require 'chef/provider/template_finder'
+rescue LoadError
+end
+
+module ChefSpec
+  class Renderer
+    include ChefSpec::Normalize
+
+    # @return [Chef::Runner]
+    attr_reader :chef_run
+
+    # @return [Chef::Resource]
+    attr_reader :resource
+
+    #
+    # Create a new Renderer for the given Chef run and resource.
+    #
+    # @param [Chef::Runner] chef_run
+    #   the Chef run containing the resources
+    # @param [Chef::Resource] resource
+    #   the resource to render content from
+    #
+    def initialize(chef_run, resource)
+      @chef_run = chef_run
+      @resource = resource
+    end
+
+    #
+    # The content of the resource (this method delegates to the)
+    # various private rendering methods.
+    #
+    # @return [String, nil]
+    #   the contents of the file as a string, or nil if the resource
+    #   does not contain or respond to a content renderer.
+    #
+    def content
+      case resource_name(resource)
+      when :template
+        content_from_template(chef_run, resource)
+      when :file
+        content_from_file(chef_run, resource)
+      when :cookbook_file
+        content_from_cookbook_file(chef_run, resource)
+      else
+        nil
+      end
+    end
+
+    private
+
+    #
+    # Compute the contents of a template using Chef's templating logic.
+    #
+    # @param [Chef::RunContext] chef_run
+    #   the run context for the node
+    # @param [Chef::Provider::Template] template
+    #   the template resource
+    #
+    # @return [String]
+    #
+    def content_from_template(chef_run, template)
+      cookbook_name = template.cookbook || template.cookbook_name
+      template_location = cookbook_collection(chef_run.node)[cookbook_name].preferred_filename_on_disk_location(chef_run.node, :templates, template.source)
+
+      if Chef::Mixin::Template.const_defined?(:TemplateContext) # Chef 11+
+        template_context = Chef::Mixin::Template::TemplateContext.new([])
+        template_context.update({
+          :node => chef_run.node,
+          :template_finder => template_finder(chef_run, cookbook_name),
+        }.merge(template.variables))
+        if template.respond_to?(:helper_modules) # Chef 11.4+
+          template_context._extend_modules(template.helper_modules)
+        end
+        template_context.render_template(template_location)
+      else
+        template.provider.new(template, chef_run.run_context).send(:render_with_context, template_location) do |file|
+          File.read(file.path)
+        end
+      end
+    end
+
+    #
+    # Get the contents of a file resource.
+    #
+    # @param [Chef::RunContext] chef_run
+    #   the run context for the node
+    # @param [Chef::Provider::File] file
+    #   the file resource
+    #
+    # @return [String]
+    #
+    def content_from_file(chef_run, file)
+      file.content
+    end
+
+    #
+    # Get the contents of a cookbook file using Chef.
+    #
+    # @param [Chef::RunContext] chef_run
+    #   the run context for the node
+    # @param [Chef::Provider::CookbookFile] cookbook_file
+    #   the file resource
+    #
+    def content_from_cookbook_file(chef_run, cookbook_file)
+      cookbook_name = cookbook_file.cookbook || cookbook_file.cookbook_name
+      cookbook = cookbook_collection(chef_run.node)[cookbook_name]
+      File.read(cookbook.preferred_filename_on_disk_location(chef_run.node, :files, cookbook_file.source))
+    end
+
+    # The cookbook collection for the current Chef run context. Handles
+    # the differing cases between Chef 10 and Chef 11.
+    #
+    # @param [Chef::Node] node
+    #   the Chef node to get the cookbook collection from
+    #
+    # @return [Array<Chef::Cookbook>]
+    def cookbook_collection(node)
+      if chef_run.respond_to?(:run_context)
+        chef_run.run_context.cookbook_collection # Chef 11.8+
+      elsif node.respond_to?(:run_context)
+        node.run_context.cookbook_collection # Chef 11+
+      else
+        node.cookbook_collection # Chef 10
+      end
+    end
+
+    # Return a new instance of the TemplateFinder if we are running on Chef 11.
+    #
+    # @param [Chef::RunContext] chef_run
+    #   the run context for the noe
+    # @param [String] cookbook_name
+    #   the name of the cookbook
+    #
+    # @return [Chef::Provider::TemplateFinder, nil]
+    def template_finder(chef_run, cookbook_name)
+      if Chef::Provider.const_defined?(:TemplateFinder) # Chef 11+
+        Chef::Provider::TemplateFinder.new(chef_run.run_context, cookbook_name, chef_run.node)
+      else
+        nil
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/rspec.rb b/lib/chefspec/rspec.rb
new file mode 100644
index 0000000..7fceada
--- /dev/null
+++ b/lib/chefspec/rspec.rb
@@ -0,0 +1,20 @@
+RSpec.configure do |config|
+  config.include(ChefSpec::API)
+  config.include(ChefSpec::Macros)
+
+  config.after(:each) do
+    ChefSpec::Stubs::CommandRegistry.reset!
+    ChefSpec::Stubs::DataBagRegistry.reset!
+    ChefSpec::Stubs::DataBagItemRegistry.reset!
+    ChefSpec::Stubs::SearchRegistry.reset!
+  end
+
+  config.add_setting :cookbook_path
+  config.add_setting :role_path
+  config.add_setting :environment_path
+  config.add_setting :file_cache_path
+  config.add_setting :log_level, default: :warn
+  config.add_setting :path
+  config.add_setting :platform
+  config.add_setting :version
+end
diff --git a/lib/chefspec/server.rb b/lib/chefspec/server.rb
new file mode 100644
index 0000000..02d5d68
--- /dev/null
+++ b/lib/chefspec/server.rb
@@ -0,0 +1,4 @@
+# @todo Remove in v5.0.0
+require_relative 'deprecations'
+deprecated "require 'chefspec/server' is no longer required and " \
+  "will be removed in the next major release."
diff --git a/lib/chefspec/server_methods.rb b/lib/chefspec/server_methods.rb
new file mode 100644
index 0000000..a0f4ead
--- /dev/null
+++ b/lib/chefspec/server_methods.rb
@@ -0,0 +1,176 @@
+module ChefSpec
+  # This module contains the list of methods that are specific to creating and
+  # managing resources within an Chef Zero instance. It is designed to be
+  # included in a class which exposes a `server` instance variable or method
+  # that returns a {ChefZero::Server} instance.
+  module ServerMethods
+    #
+    # The actual Chef Zero Server object.
+    #
+    # @return [ChefZero::Server]
+    #
+    def server
+      @server ||= ChefZero::Server.new(
+        # Set the log level from RSpec, defaulting to warn
+        log_level:  RSpec.configuration.log_level || :warn,
+
+        # Set a random port so ChefSpec may be run in multiple contexts
+        port: port,
+      )
+    end
+
+    #
+    # @macro entity
+    #   @method create_$1(name, data = {})
+    #     Create a new $1 on the Chef Server
+    #
+    #     @param [String] name
+    #       the name of the $1
+    #     @param [Hash] data
+    #       the list of data to load
+    #
+    #
+    #   @method $1(name)
+    #     Find a $1 at the given name
+    #
+    #     @param [String] name
+    #       the name of the $1
+    #
+    #     @return [$2, nil]
+    #
+    #
+    #   @method $3
+    #     The list of $1 on the Chef Server
+    #
+    #     @return [Array<Hash>]
+    #       all the $1 on the Chef Server
+    #
+    #
+    #   @method has_$1?(name)
+    #     Determine if the Chef Server has the given $1
+    #
+    #     @param [String] name
+    #       the name of the $1 to find
+    #
+    #     @return [true, false]
+    #
+    def self.entity(method, klass, key)
+      class_eval <<-EOH, __FILE__, __LINE__ + 1
+        def create_#{method}(name, data = {})
+          # Automatically set the "name" if no explicit one was given
+          data[:name] ||= name
+
+          # Convert it to JSON
+          data = JSON.fast_generate(data)
+
+          load_data(name, '#{key}', data)
+        end
+
+        def get_#{method}(name)
+          data = get('#{key}', name)
+          json = JSON.parse(data)
+
+          if #{klass}.respond_to?(:json_create)
+            #{klass}.json_create(json)
+          else
+            #{klass}.new(json)
+          end
+        rescue ChefZero::DataStore::DataNotFoundError
+          nil
+        end
+
+        def get_#{key}
+          get('#{key}')
+        end
+
+        def has_#{method}?(name)
+          !get('#{key}', name).nil?
+        rescue ChefZero::DataStore::DataNotFoundError
+          false
+        end
+      EOH
+    end
+
+    entity :client,      Chef::Client, 'clients'
+    entity :data_bag,    Chef::DataBag, 'data'
+    entity :environment, Chef::Environment, 'environments'
+    entity :node,        Chef::Node, 'nodes'
+    entity :role,        Chef::Role, 'roles'
+
+    #
+    # Create a new data_bag on the Chef Server. This overrides the method
+    # created by {entity}
+    #
+    # @param [String] name
+    #   the name of the data bag
+    # @param [Hash] data
+    #   the data to load into the data bag
+    #
+    def create_data_bag(name, data = {})
+      load_data(name, 'data', data)
+    end
+
+    #
+    # Create a new node on the Chef Server. This overrides the method created
+    # by {entity}, permitting users to pass a raw +Chef::Node+ object in
+    # addition to a hash.
+    #
+    # @example Create a node from a hash
+    #
+    #   create_node('bacon', attribute: 'value')
+    #
+    # @example Create a node from a +Chef::Node+ object
+    #
+    #   node = stub_node('bacon', platform: 'ubuntu', version: '12.04')
+    #   create_node(node)
+    #
+    # @param [String, Chef::Node] object
+    #   the object to create; this can be the name of the node, or an actual
+    #   +Chef::Node+ object
+    # @param [Hash] data
+    #   the list of data to populate the node with; this is ignored if an
+    #   actual node object is given
+    #
+    def create_node(object, data = {})
+      if object.is_a?(Chef::Node)
+        name = object.name
+        data = object.to_json
+      else
+        name = object.to_s
+        data[:name] ||= name
+        data = JSON.fast_generate(data)
+      end
+
+      load_data(name, 'nodes', data)
+    end
+    alias_method :update_node, :create_node
+
+    #
+    # Shortcut method for loading data into Chef Zero.
+    #
+    # @param [String] name
+    #   the name or id of the item to load
+    # @param [String, Symbol] key
+    #   the key to load
+    # @param [Hash] data
+    #   the data for the object, which will be converted to JSON and uploaded
+    #   to the server
+    #
+    def load_data(name, key, data = {})
+      @server.load_data({ key => { name => data } })
+    end
+
+    #
+    # Get the path to an item in the data store.
+    #
+    def get(*args)
+      args.unshift('organizations', 'chef')
+
+      if args.size == 3
+        @server.data_store.list(args)
+      else
+        @server.data_store.get(args)
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/server_runner.rb b/lib/chefspec/server_runner.rb
new file mode 100644
index 0000000..5d6be1f
--- /dev/null
+++ b/lib/chefspec/server_runner.rb
@@ -0,0 +1,103 @@
+require 'chef_zero/server'
+require 'chef/cookbook_loader'
+require 'chef/cookbook_uploader'
+
+require_relative 'server_methods'
+require_relative 'solo_runner'
+
+module ChefSpec
+  class ServerRunner < SoloRunner
+    include ChefSpec::ServerMethods
+
+    # @see (SoloRunner#initialize)
+    def initialize(options = {})
+      # Call super, but do not pass in the block because we want to customize
+      # our yielding.
+      super(options, &nil)
+
+      Chef::Config[:client_key]      = client_key
+      Chef::Config[:client_name]     = 'chefspec'
+      Chef::Config[:node_name]       = 'chefspec'
+      Chef::Config[:solo]            = false
+
+      Chef::Config[:chef_server_url]  = server.url
+      Chef::Config[:http_retry_count] = 0
+
+      # Start the Chef Zero instance in the background
+      server.start_background
+      at_exit { server.stop if server.running? }
+
+      # Unlike the SoloRunner, the node AND server object are yielded for
+      # customization
+      yield node, self if block_given?
+    end
+
+    #
+    # Upload the cookbooks to the Chef Server.
+    #
+    def upload_cookbooks!
+      loader = Chef::CookbookLoader.new(Chef::Config[:cookbook_path])
+      loader.load_cookbooks
+      cookbook_uploader_for(loader).upload_cookbooks
+    end
+
+    #
+    # The uploader for the cookbooks.
+    #
+    # @param [Chef::CookbookLoader] loader
+    #   the Chef cookbook loader
+    #
+    # @return [Chef::CookbookUploader]
+    #
+    def cookbook_uploader_for(loader)
+      Chef::CookbookUploader.new(loader.cookbooks)
+    end
+
+    # @see (SoloRunner#converge)
+    def converge(*recipe_names)
+      upload_cookbooks!
+
+      super do
+        yield if block_given?
+
+        # Save the node back to the server for searching purposes
+        client.register
+        node.save
+      end
+    end
+
+    private
+
+    #
+    # The path to the insecure Chef Zero private key on disk. Because Chef
+    # requires the path to a file instead of the contents of the key (why),
+    # this method dynamically writes the +ChefZero::PRIVATE_KEY+ to disk and
+    # then returns that path.
+    #
+    # @return [String]
+    #   the path to the client key on disk
+    #
+    def client_key
+      tmp = Dir.mktmpdir
+      path = File.join(tmp, 'client.pem')
+      File.open(path, 'wb') { |f| f.write(ChefZero::PRIVATE_KEY) }
+      at_exit { FileUtils.rm_rf(tmp) }
+      path
+    end
+
+    #
+    # A randomly assigned, open port for run the Chef Zero server.
+    #
+    # @return [Fixnum]
+    #
+    def port
+      return @port if @port
+
+      @server = TCPServer.new('127.0.0.1', 0)
+      @port   = @server.addr[1].to_i
+      @server.close
+
+      return @port
+    end
+  end
+end
diff --git a/lib/chefspec/solo_runner.rb b/lib/chefspec/solo_runner.rb
new file mode 100644
index 0000000..abcbae9
--- /dev/null
+++ b/lib/chefspec/solo_runner.rb
@@ -0,0 +1,403 @@
+require 'fauxhai'
+require 'chef/client'
+require 'chef/mash'
+require 'chef/providers'
+require 'chef/resources'
+
+module ChefSpec
+  class SoloRunner
+    #
+    # Handy class method for just converging a runner if you do not care about
+    # initializing the runner with custom options.
+    #
+    # @example
+    #   ChefSpec::SoloRunner.converge('cookbook::recipe')
+    #
+    def self.converge(*recipe_names)
+      new.tap do |instance|
+        instance.converge(*recipe_names)
+      end
+    end
+
+    include ChefSpec::Normalize
+
+    # @return [Hash]
+    attr_reader :options
+
+    # @return [Chef::RunContext]
+    attr_reader :run_context
+
+    #
+    # Instantiate a new SoloRunner to run examples with.
+    #
+    # @example Instantiate a new Runner
+    #   ChefSpec::SoloRunner.new
+    #
+    # @example Specifying the platform and version
+    #   ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '12.04')
+    #
+    # @example Specifying the cookbook path
+    #   ChefSpec::SoloRunner.new(cookbook_path: ['/cookbooks'])
+    #
+    # @example Specifying the log level
+    #   ChefSpec::SoloRunner.new(log_level: :info)
+    #
+    #
+    # @param [Hash] options
+    #   The options for the new runner
+    #
+    # @option options [Symbol] :log_level
+    #   The log level to use (default is :warn)
+    # @option options [String] :platform
+    #   The platform to load Ohai attributes from (must be present in fauxhai)
+    # @option options [String] :version
+    #   The version of the platform to load Ohai attributes from (must be present in fauxhai)
+    # @option options [String] :path
+    #   Path of a json file that will be passed to fauxhai as :path option
+    # @option options [Array<String>] :step_into
+    #   The list of LWRPs to evaluate
+    # @option options String] :file_cache_path
+    #   File caching path, if absent ChefSpec will use a temporary directory generated on the fly
+    #
+    # @yield [node] Configuration block for Chef::Node
+    #
+    def initialize(options = {})
+      @options = with_default_options(options)
+
+      Chef::Log.level = @options[:log_level]
+
+      Chef::Config.reset!
+      Chef::Config.formatters.clear
+      Chef::Config.add_formatter('chefspec')
+      Chef::Config[:cache_type]      = 'Memory'
+      Chef::Config[:client_key]      = nil
+      Chef::Config[:client_name]     = nil
+      Chef::Config[:node_name]       = nil
+      Chef::Config[:file_cache_path] = @options[:file_cache_path] || file_cache_path
+      Chef::Config[:cookbook_path]   = Array(@options[:cookbook_path])
+      Chef::Config[:no_lazy_load]    = true
+      Chef::Config[:role_path]       = Array(@options[:role_path])
+      Chef::Config[:force_logger]    = true
+      Chef::Config[:solo]            = true
+      Chef::Config[:environment_path] = @options[:environment_path]
+
+      yield node if block_given?
+    end
+
+    #
+    # Execute the given `run_list` on the node, without actually converging
+    # the node. Each time {#converge} is called, the `run_list` is reset to the
+    # new value (it is **not** additive).
+    #
+    # @example Converging a single recipe
+    #   chef_run.converge('example::default')
+    #
+    # @example Converging multiple recipes
+    #   chef_run.converge('example::default', 'example::secondary')
+    #
+    #
+    # @param [Array] recipe_names
+    #   The names of the recipe or recipes to converge
+    #
+    # @return [ChefSpec::SoloRunner]
+    #   A reference to the calling Runner (for chaining purposes)
+    #
+    def converge(*recipe_names)
+      node.run_list.reset!
+      recipe_names.each { |recipe_name| node.run_list.add(recipe_name) }
+
+      return self if dry_run?
+
+      # Expand the run_list
+      expand_run_list!
+
+      # Setup the run_context
+      @run_context = client.setup_run_context
+
+      # Allow stubbing/mocking after the cookbook has been compiled but before the converge
+      yield if block_given?
+
+      @converging = true
+      converge_val = @client.converge(@run_context)
+      if converge_val.is_a?(Exception)
+        raise converge_val
+      end
+      self
+    end
+
+    #
+    # The +Chef::Node+ corresponding to this Runner.
+    #
+    # @return [Chef::Node]
+    #
+    def node
+      return @node if @node
+
+      @node = client.build_node
+      @node.instance_variable_set(:@runner, self)
+      @node.class.send(:attr_reader, :runner)
+      @node
+    end
+
+    #
+    # The full collection of resources for this Runner.
+    #
+    # @return [Hash<String, Chef::Resource>]
+    #
+    def resource_collection
+      @resource_collection ||= @run_context.resource_collection
+    end
+
+    #
+    # Find the resource with the declared type and resource name.
+    #
+    # @example Find a template at `/etc/foo`
+    #   chef_run.find_resource(:template, '/etc/foo') #=> #<Chef::Resource::Template>
+    #
+    #
+    # @param [Symbol] type
+    #   The type of resource (sometimes called `resource_name`) such as `file`
+    #   or `directory`.
+    # @param [String, Regexp] name
+    #   The value of the name attribute or identity attribute for the resource.
+    #
+    # @return [Chef::Resource, nil]
+    #   The matching resource, or nil if one is not found
+    #
+    def find_resource(type, name)
+      begin
+        return resource_collection.lookup("#{type}[#{name}]")
+      rescue Chef::Exceptions::ResourceNotFound; end
+
+      resource_collection.all_resources.find do |resource|
+        resource_name(resource) == type && (name === resource.identity || name === resource.name)
+      end
+    end
+
+    #
+    # Find the resource with the declared type.
+    #
+    # @example Find all template resources
+    #   chef_run.find_resources(:template) #=> [#<Chef::Resource::Template>, #...]
+    #
+    #
+    # @param [Symbol] type
+    #   The type of resource such as `:file` or `:directory`.
+    #
+    # @return [Array<Chef::Resource>]
+    #   The matching resources
+    #
+    def find_resources(type)
+      resource_collection.all_resources.select do |resource|
+        resource_name(resource) == type.to_sym
+      end
+    end
+
+    #
+    # Boolean method to determine the current phase of the Chef run (compiling
+    # or converging)
+    #
+    # @return [true, false]
+    #
+    def compiling?
+      !@converging
+    end
+
+    #
+    # Determines if the runner should step into the given resource. The
+    # +step_into+ option takes a string, but this method coerces everything
+    # to symbols for safety.
+    #
+    # This method also substitutes any dashes (+-+) with underscores (+_+),
+    # because that's what Chef does under the hood. (See GitHub issue #254
+    # for more background)
+    #
+    # @param [Chef::Resource] resource
+    #   the Chef resource to try and step in to
+    #
+    # @return [true, false]
+    #
+    def step_into?(resource)
+      key = resource_name(resource)
+      Array(options[:step_into]).map(&method(:resource_name)).include?(key)
+    end
+
+    #
+    # Boolean method to determine if this Runner is in `dry_run` mode.
+    #
+    # @return [true, false]
+    #
+    def dry_run?
+      !!options[:dry_run]
+    end
+
+    #
+    # This runner as a string.
+    #
+    # @return [String] Currently includes the run_list. Format of the string
+    # may change between versions of this gem.
+    #
+    def to_s
+      "#<#{self.class.name} run_list: [#{node.run_list}]>"
+    end
+
+    #
+    # The runner as a String with helpful output.
+    #
+    # @return [String]
+    #
+    def inspect
+      "#<#{self.class.name}" \
+      " options: #{options.inspect}," \
+      " run_list: [#{node.run_list}]>"
+    end
+
+    #
+    # Respond to custom matchers defined by the user.
+    #
+    def method_missing(m, *args, &block)
+      if block = ChefSpec.matchers[resource_name(m.to_sym)]
+        instance_exec(args.first, &block)
+      else
+        super
+      end
+    end
+
+    #
+    # Inform Ruby that we respond to methods that are defined as custom
+    # matchers.
+    #
+    def respond_to_missing?(m, include_private = false)
+      ChefSpec.matchers.key?(m.to_sym) || super
+    end
+
+    private
+
+    #
+    # The path to cache files on disk. This value is created using
+    # {Dir.mktmpdir}. The method adds a {Kernel.at_exit} handler to ensure the
+    # temporary directory is deleted when the system exits.
+    #
+    # **This method creates a new temporary directory on each call!** As such,
+    # you should cache the result to a variable inside you system.
+    #
+    def file_cache_path
+      path = Dir.mktmpdir
+      at_exit { FileUtils.rm_rf(path) }
+      path
+    end
+
+    #
+    # Set the default options, with the given options taking precedence.
+    #
+    # @param [Hash] options
+    #   the list of options to take precedence
+    #
+    # @return [Hash] options
+    #
+    def with_default_options(options)
+      config = RSpec.configuration
+
+      {
+        cookbook_path: config.cookbook_path || calling_cookbook_path(caller),
+        role_path:     config.role_path || default_role_path,
+        environment_path: config.environment_path || default_environment_path,
+        file_cache_path: config.file_cache_path,
+        log_level:     config.log_level,
+        path:          config.path,
+        platform:      config.platform,
+        version:       config.version,
+      }.merge(options)
+    end
+
+    #
+    # The inferred path from the calling spec.
+    #
+    # @param [Array<String>] kaller
+    #   the calling trace
+    #
+    # @return [String]
+    #
+    def calling_cookbook_path(kaller)
+      calling_spec = kaller.find { |line| line =~ /\/spec/ }
+      raise Error::CookbookPathNotFound if calling_spec.nil?
+
+      bits = calling_spec.split(/:[0-9]/, 2).first.split(File::SEPARATOR)
+      spec_dir = bits.index('spec') || 0
+
+      File.expand_path(File.join(bits.slice(0, spec_dir), '..'))
+    end
+
+    #
+    # The inferred path to roles.
+    #
+    # @return [String, nil]
+    #
+    def default_role_path
+      Pathname.new(Dir.pwd).ascend do |path|
+        possible = File.join(path, 'roles')
+        return possible if File.exist?(possible)
+      end
+
+      nil
+    end
+
+    #
+    # The inferred path to environments.
+    #
+    # @return [String, nil]
+    #
+    def default_environment_path
+      Pathname.new(Dir.pwd).ascend do |path|
+        possible = File.join(path, 'environments')
+        return possible if File.exist?(possible)
+      end
+
+      nil
+    end
+
+    #
+    # The +Chef::Client+ for this runner.
+    #
+    # @return [Chef::Runner]
+    #
+    def client
+      return @client if @client
+
+      @client = Chef::Client.new
+      @client.ohai.data = Mash.from_hash(Fauxhai.mock(options).data)
+      @client.load_node
+      @client.build_node
+      @client.save_updated_node
+      @client
+    end
+
+    #
+    # We really need a way to just expand the run_list, but that's done by
+    # +Chef::Client#build_node+. However, that same method also resets the
+    # automatic attributes, making it impossible to mock them. So we are
+    # stuck +instance_eval+ing against the client and manually expanding
+    # the mode object.
+    #
+    # @todo Remove in Chef 13
+    #
+    def expand_run_list!
+      # Recent versions of Chef include a method to expand the +run_list+,
+      # setting the correct instance variables on the policy builder. We use
+      # that, unless the user is running an older version of Chef which
+      # doesn't include this method.
+      if client.respond_to?(:expanded_run_list)
+        client.expanded_run_list
+      else
+        # Sadly, if we got this far, it means that the current Chef version
+        # does not include the +expanded_run_list+ method, so we need to
+        # manually expand the +run_list+. The following code has been known
+        # to make kittens cry, so please read with extreme caution.
+        client.instance_eval do
+          @run_list_expansion = expand_run_list
+          @expanded_run_list_with_versions = @run_list_expansion.recipes.with_version_constraints_strings
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/command_registry.rb b/lib/chefspec/stubs/command_registry.rb
new file mode 100644
index 0000000..89ae2af
--- /dev/null
+++ b/lib/chefspec/stubs/command_registry.rb
@@ -0,0 +1,11 @@
+require_relative 'registry'
+
+module ChefSpec
+  module Stubs
+    class CommandRegistry < Registry
+      def stub_for(command)
+        @stubs.find { |stub| stub.command === command }
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/command_stub.rb b/lib/chefspec/stubs/command_stub.rb
new file mode 100644
index 0000000..e3e2f4c
--- /dev/null
+++ b/lib/chefspec/stubs/command_stub.rb
@@ -0,0 +1,37 @@
+require_relative 'stub'
+
+module ChefSpec
+  module Stubs
+    class CommandStub < Stub
+      attr_reader :block
+      attr_reader :command
+      attr_reader :value
+
+      def initialize(command, &block)
+        @command = command
+        @block   = block
+      end
+
+      def and_return(value)
+        @value = value
+        self
+      end
+
+      def result
+        if @block
+          @block.call
+        else
+          @value
+        end
+      end
+
+      def signature
+        if @block
+          "stub_command(#{@command.inspect}) { # Ruby code }"
+        else
+          "stub_command(#{@command.inspect}).and_return(#{@value})"
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/data_bag_item_registry.rb b/lib/chefspec/stubs/data_bag_item_registry.rb
new file mode 100644
index 0000000..26b7f4a
--- /dev/null
+++ b/lib/chefspec/stubs/data_bag_item_registry.rb
@@ -0,0 +1,13 @@
+require_relative 'registry'
+
+module ChefSpec
+  module Stubs
+    class DataBagItemRegistry < Registry
+      def stub_for(bag, id)
+        @stubs.find do |stub|
+          stub.bag.to_s == bag.to_s && stub.id === id
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/data_bag_item_stub.rb b/lib/chefspec/stubs/data_bag_item_stub.rb
new file mode 100644
index 0000000..d7413fa
--- /dev/null
+++ b/lib/chefspec/stubs/data_bag_item_stub.rb
@@ -0,0 +1,25 @@
+require_relative 'stub'
+
+module ChefSpec
+  module Stubs
+    class DataBagItemStub < Stub
+      attr_reader :block
+      attr_reader :id
+      attr_reader :bag
+
+      def initialize(bag, id, &block)
+        @bag   = bag.to_s
+        @id    = id
+        @block = block
+      end
+
+      def signature
+        if @block
+          "stub_data_bag_item(#{@bag.inspect}, #{@id.inspect}) { # Ruby code }"
+        else
+          "stub_data_bag_item(#{@bag.inspect}, #{@id.inspect}).and_return(#{@value})"
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/data_bag_registry.rb b/lib/chefspec/stubs/data_bag_registry.rb
new file mode 100644
index 0000000..aa63b3b
--- /dev/null
+++ b/lib/chefspec/stubs/data_bag_registry.rb
@@ -0,0 +1,13 @@
+require_relative 'registry'
+
+module ChefSpec
+  module Stubs
+    class DataBagRegistry < Registry
+      def stub_for(bag)
+        @stubs.find do |stub|
+          stub.bag.to_s == bag.to_s
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/data_bag_stub.rb b/lib/chefspec/stubs/data_bag_stub.rb
new file mode 100644
index 0000000..07d46d9
--- /dev/null
+++ b/lib/chefspec/stubs/data_bag_stub.rb
@@ -0,0 +1,23 @@
+require_relative 'stub'
+
+module ChefSpec
+  module Stubs
+    class DataBagStub < Stub
+      attr_reader :block
+      attr_reader :bag
+
+      def initialize(bag, &block)
+        @bag   = bag.to_s
+        @block = block
+      end
+
+      def signature
+        if @block
+          "stub_data_bag(#{@bag.inspect}) { # Ruby code }"
+        else
+          "stub_data_bag(#{@bag.inspect}).and_return(#{@value})"
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/registry.rb b/lib/chefspec/stubs/registry.rb
new file mode 100644
index 0000000..5fa2d21
--- /dev/null
+++ b/lib/chefspec/stubs/registry.rb
@@ -0,0 +1,32 @@
+module ChefSpec
+  module Stubs
+    class Registry
+      class << self
+        extend Forwardable
+        def_delegators :instance, :reset!, :register, :stubs, :stubs=, :stub_for
+      end
+
+      include Singleton
+
+      # @return [Hash<Symbol, Array<SearchStub>>]
+      attr_accessor :stubs
+
+      def initialize
+        reset!
+      end
+
+      def reset!
+        @stubs = []
+      end
+
+      def register(stub)
+        @stubs.insert(0, stub)
+        stub
+      end
+
+      def stub_for(*args)
+        raise ArgumentError, '#stub_for is an abstract function'
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/search_registry.rb b/lib/chefspec/stubs/search_registry.rb
new file mode 100644
index 0000000..a9ff158
--- /dev/null
+++ b/lib/chefspec/stubs/search_registry.rb
@@ -0,0 +1,13 @@
+require_relative 'registry'
+
+module ChefSpec
+  module Stubs
+    class SearchRegistry < Registry
+      def stub_for(type, query = '*:*')
+        @stubs.find do |stub|
+          stub.type.to_s == type.to_s && stub.query === query
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/search_stub.rb b/lib/chefspec/stubs/search_stub.rb
new file mode 100644
index 0000000..a84eab1
--- /dev/null
+++ b/lib/chefspec/stubs/search_stub.rb
@@ -0,0 +1,25 @@
+require_relative 'stub'
+
+module ChefSpec
+  module Stubs
+    class SearchStub < Stub
+      attr_reader :block
+      attr_reader :query
+      attr_reader :type
+
+      def initialize(type, query = '*:*', &block)
+        @type  = type.to_s
+        @query = query
+        @block = block
+      end
+
+      def signature
+        if @block
+          "stub_search(#{@type.inspect}, #{@query.inspect}) { # Ruby code }"
+        else
+          "stub_search(#{@type.inspect}, #{@query.inspect}).and_return(#{@value})"
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/stubs/stub.rb b/lib/chefspec/stubs/stub.rb
new file mode 100644
index 0000000..1570308
--- /dev/null
+++ b/lib/chefspec/stubs/stub.rb
@@ -0,0 +1,38 @@
+module ChefSpec
+  module Stubs
+    class Stub
+      attr_reader :value
+
+      def and_return(value)
+        @value = value
+        self
+      end
+
+      def and_raise(exception)
+        @block = Proc.new { raise exception }
+        self
+      end
+
+      def result
+        if @block
+          recursively_mashify(@block.call)
+        else
+          recursively_mashify(@value)
+        end
+      end
+
+      private
+
+      def recursively_mashify(thing)
+        case thing
+        when Array
+          thing.collect { |item| recursively_mashify(item) }
+        when Hash
+          Mash.from_hash(thing)
+        else
+          thing
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/util.rb b/lib/chefspec/util.rb
new file mode 100644
index 0000000..b38298f
--- /dev/null
+++ b/lib/chefspec/util.rb
@@ -0,0 +1,58 @@
+module ChefSpec
+  module Util
+    extend self
+
+    #
+    # Covert the given CaMelCaSeD string to under_score. Graciously borrowed
+    # from http://stackoverflow.com/questions/1509915.
+    #
+    # @param [String] string
+    #   the string to use for transformation
+    #
+    # @return [String]
+    #
+    def underscore(string)
+      string
+        .to_s
+        .gsub(/::/, '/')
+        .gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
+        .gsub(/([a-z\d])([A-Z])/,'\1_\2')
+        .tr('-', '_')
+        .downcase
+    end
+
+    #
+    # Convert an underscored string to it's camelcase equivalent constant.
+    #
+    # @param [String] string
+    #   the string to convert
+    #
+    # @return [String]
+    #
+    def camelize(string)
+      string
+        .to_s
+        .split('_')
+        .map { |e| e.capitalize }
+        .join
+    end
+
+    #
+    # Truncate the given string to a certain number of characters.
+    #
+    # @param [String] string
+    #   the string to truncate
+    # @param [Hash] options
+    #   the list of options (such as +length+)
+    #
+    def truncate(string, options = {})
+      length = options[:length] || 30
+
+      if string.length > length
+        string[0..length-3] + '...'
+      else
+        string
+      end
+    end
+  end
+end
diff --git a/lib/chefspec/version.rb b/lib/chefspec/version.rb
new file mode 100644
index 0000000..d31361e
--- /dev/null
+++ b/lib/chefspec/version.rb
@@ -0,0 +1,3 @@
+module ChefSpec
+  VERSION = '4.3.0'
+end
diff --git a/metadata.yml b/metadata.yml
new file mode 100644
index 0000000..f7f1cc3
--- /dev/null
+++ b/metadata.yml
@@ -0,0 +1,891 @@
+--- !ruby/object:Gem::Specification
+name: chefspec
+version: !ruby/object:Gem::Version
+  version: 4.3.0
+platform: ruby
+authors:
+- Andrew Crump
+- Seth Vargo
+autorequire: 
+bindir: bin
+cert_chain: []
+date: 2015-07-16 00:00:00.000000000 Z
+dependencies:
+- !ruby/object:Gem::Dependency
+  name: chef
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ">="
+      - !ruby/object:Gem::Version
+        version: '11.14'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ">="
+      - !ruby/object:Gem::Version
+        version: '11.14'
+- !ruby/object:Gem::Dependency
+  name: fauxhai
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '2.0'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '2.0'
+- !ruby/object:Gem::Dependency
+  name: rspec
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '3.0'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '3.0'
+- !ruby/object:Gem::Dependency
+  name: rake
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ">="
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ">="
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: redcarpet
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '3.0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '3.0'
+- !ruby/object:Gem::Dependency
+  name: yard
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '0.8'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '0.8'
+- !ruby/object:Gem::Dependency
+  name: aruba
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '0.5'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: '0.5'
+description: ChefSpec is a unit testing and resource coverage (code coverage) 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.
+email:
+- andrew.crump at ieee.org
+- sethvargo at gmail.com
+executables: []
+extensions: []
+extra_rdoc_files: []
+files:
+- ".gitignore"
+- ".travis.yml"
+- CHANGELOG.md
+- CONTRIBUTING.md
+- Gemfile
+- ISSUES.md
+- LICENSE
+- README.md
+- Rakefile
+- chefspec.gemspec
+- examples/apt_package/recipes/install.rb
+- examples/apt_package/recipes/purge.rb
+- examples/apt_package/recipes/reconfig.rb
+- examples/apt_package/recipes/remove.rb
+- examples/apt_package/recipes/upgrade.rb
+- examples/apt_package/spec/install_spec.rb
+- examples/apt_package/spec/purge_spec.rb
+- examples/apt_package/spec/reconfig_spec.rb
+- examples/apt_package/spec/remove_spec.rb
+- examples/apt_package/spec/upgrade_spec.rb
+- examples/attributes/attributes/default.rb
+- examples/attributes/recipes/default.rb
+- examples/attributes/spec/default_spec.rb
+- examples/batch/recipes/run.rb
+- examples/batch/spec/run_spec.rb
+- examples/cached/recipes/default.rb
+- examples/cached/spec/default_spec.rb
+- examples/chef_gem/recipes/install.rb
+- examples/chef_gem/recipes/purge.rb
+- examples/chef_gem/recipes/reconfig.rb
+- examples/chef_gem/recipes/remove.rb
+- examples/chef_gem/recipes/upgrade.rb
+- examples/chef_gem/spec/install_spec.rb
+- examples/chef_gem/spec/purge_spec.rb
+- examples/chef_gem/spec/reconfig_spec.rb
+- examples/chef_gem/spec/remove_spec.rb
+- examples/chef_gem/spec/upgrade_spec.rb
+- examples/compile_time/recipes/default.rb
+- examples/compile_time/spec/default_spec.rb
+- examples/cookbook_file/recipes/create.rb
+- examples/cookbook_file/recipes/create_if_missing.rb
+- examples/cookbook_file/recipes/delete.rb
+- examples/cookbook_file/recipes/touch.rb
+- examples/cookbook_file/spec/create_if_missing_spec.rb
+- examples/cookbook_file/spec/create_spec.rb
+- examples/cookbook_file/spec/delete_spec.rb
+- examples/cookbook_file/spec/touch_spec.rb
+- examples/cron/recipes/create.rb
+- examples/cron/recipes/delete.rb
+- examples/cron/spec/create_spec.rb
+- examples/cron/spec/delete_spec.rb
+- examples/custom_matcher/libraries/matcher.rb
+- examples/custom_matcher/providers/thing.rb
+- examples/custom_matcher/recipes/install.rb
+- examples/custom_matcher/recipes/remove.rb
+- examples/custom_matcher/resources/thing.rb
+- examples/custom_matcher/spec/install_spec.rb
+- examples/custom_matcher/spec/remove_spec.rb
+- examples/deploy/recipes/deploy.rb
+- examples/deploy/recipes/force_deploy.rb
+- examples/deploy/recipes/rollback.rb
+- examples/deploy/spec/deploy_spec.rb
+- examples/deploy/spec/force_deploy_spec.rb
+- examples/deploy/spec/rollback_spec.rb
+- examples/directory/recipes/create.rb
+- examples/directory/recipes/delete.rb
+- examples/directory/spec/create_spec.rb
+- examples/directory/spec/delete_spec.rb
+- examples/do_nothing/recipes/default.rb
+- examples/do_nothing/spec/default_spec.rb
+- examples/dpkg_package/recipes/install.rb
+- examples/dpkg_package/recipes/purge.rb
+- examples/dpkg_package/recipes/remove.rb
+- examples/dpkg_package/spec/install_spec.rb
+- examples/dpkg_package/spec/purge_spec.rb
+- examples/dpkg_package/spec/remove_spec.rb
+- examples/easy_install_package/recipes/install.rb
+- examples/easy_install_package/recipes/purge.rb
+- examples/easy_install_package/recipes/remove.rb
+- examples/easy_install_package/recipes/upgrade.rb
+- examples/easy_install_package/spec/install_spec.rb
+- examples/easy_install_package/spec/purge_spec.rb
+- examples/easy_install_package/spec/remove_spec.rb
+- examples/easy_install_package/spec/upgrade_spec.rb
+- examples/env/recipes/create.rb
+- examples/env/recipes/delete.rb
+- examples/env/recipes/modify.rb
+- examples/env/spec/create_spec.rb
+- examples/env/spec/delete_spec.rb
+- examples/env/spec/modify_spec.rb
+- examples/erl_call/recipes/run.rb
+- examples/erl_call/spec/run_spec.rb
+- examples/execute/recipes/run.rb
+- examples/execute/spec/run_spec.rb
+- examples/expect_exception/recipes/compile_error.rb
+- examples/expect_exception/recipes/converge_error.rb
+- examples/expect_exception/recipes/no_error.rb
+- examples/expect_exception/spec/compile_error_spec.rb
+- examples/expect_exception/spec/converge_error_spec.rb
+- examples/expect_exception/spec/no_error_spec.rb
+- examples/file/recipes/create.rb
+- examples/file/recipes/create_if_missing.rb
+- examples/file/recipes/delete.rb
+- examples/file/recipes/touch.rb
+- examples/file/spec/create_if_missing_spec.rb
+- examples/file/spec/create_spec.rb
+- examples/file/spec/delete_spec.rb
+- examples/file/spec/touch_spec.rb
+- examples/freebsd_package/recipes/install.rb
+- examples/freebsd_package/recipes/remove.rb
+- examples/freebsd_package/spec/install_spec.rb
+- examples/freebsd_package/spec/remove_spec.rb
+- examples/gem_package/recipes/install.rb
+- examples/gem_package/recipes/purge.rb
+- examples/gem_package/recipes/reconfig.rb
+- examples/gem_package/recipes/remove.rb
+- examples/gem_package/recipes/upgrade.rb
+- examples/gem_package/spec/install_spec.rb
+- examples/gem_package/spec/purge_spec.rb
+- examples/gem_package/spec/reconfig_spec.rb
+- examples/gem_package/spec/remove_spec.rb
+- examples/gem_package/spec/upgrade_spec.rb
+- examples/git/recipes/checkout.rb
+- examples/git/recipes/export.rb
+- examples/git/recipes/sync.rb
+- examples/git/spec/checkout_spec.rb
+- examples/git/spec/export_spec.rb
+- examples/git/spec/sync_spec.rb
+- examples/group/recipes/create.rb
+- examples/group/recipes/manage.rb
+- examples/group/recipes/modify.rb
+- examples/group/recipes/remove.rb
+- examples/group/spec/create_spec.rb
+- examples/group/spec/manage_spec.rb
+- examples/group/spec/modify_spec.rb
+- examples/group/spec/remove_spec.rb
+- examples/guards/recipes/default.rb
+- examples/guards/spec/default_spec.rb
+- examples/heavy_provider_light_resource/libraries/resource_service.rb
+- examples/heavy_provider_light_resource/providers/service.rb
+- examples/heavy_provider_light_resource/recipes/default.rb
+- examples/heavy_provider_light_resource/spec/provider_service_spec.rb
+- examples/http_request/recipes/delete.rb
+- examples/http_request/recipes/get.rb
+- examples/http_request/recipes/head.rb
+- examples/http_request/recipes/options.rb
+- examples/http_request/recipes/post.rb
+- examples/http_request/recipes/put.rb
+- examples/http_request/spec/delete_spec.rb
+- examples/http_request/spec/get_spec.rb
+- examples/http_request/spec/head_spec.rb
+- examples/http_request/spec/options_spec.rb
+- examples/http_request/spec/post_spec.rb
+- examples/http_request/spec/put_spec.rb
+- examples/ifconfig/recipes/add.rb
+- examples/ifconfig/recipes/delete.rb
+- examples/ifconfig/recipes/disable.rb
+- examples/ifconfig/recipes/enable.rb
+- examples/ifconfig/spec/add_spec.rb
+- examples/ifconfig/spec/delete_spec.rb
+- examples/ifconfig/spec/disable_spec.rb
+- examples/ifconfig/spec/enable_spec.rb
+- examples/include_recipe/recipes/default.rb
+- examples/include_recipe/recipes/not.rb
+- examples/include_recipe/recipes/other.rb
+- examples/include_recipe/spec/default_spec.rb
+- examples/ips_package/recipes/install.rb
+- examples/ips_package/recipes/remove.rb
+- examples/ips_package/recipes/upgrade.rb
+- examples/ips_package/spec/install_spec.rb
+- examples/ips_package/spec/remove_spec.rb
+- examples/ips_package/spec/upgrade_spec.rb
+- examples/link/recipes/create.rb
+- examples/link/recipes/delete.rb
+- examples/link/recipes/link_to.rb
+- examples/link/spec/create_spec.rb
+- examples/link/spec/delete_spec.rb
+- examples/link/spec/link_to_spec.rb
+- examples/log/recipes/write.rb
+- examples/log/spec/write_spec.rb
+- examples/macports_package/recipes/install.rb
+- examples/macports_package/recipes/purge.rb
+- examples/macports_package/recipes/remove.rb
+- examples/macports_package/recipes/upgrade.rb
+- examples/macports_package/spec/install_spec.rb
+- examples/macports_package/spec/purge_spec.rb
+- examples/macports_package/spec/remove_spec.rb
+- examples/macports_package/spec/upgrade_spec.rb
+- examples/mdadm/recipes/assemble.rb
+- examples/mdadm/recipes/create.rb
+- examples/mdadm/recipes/stop.rb
+- examples/mdadm/spec/assemble_spec.rb
+- examples/mdadm/spec/create_spec.rb
+- examples/mdadm/spec/stop_spec.rb
+- examples/mount/recipes/disable.rb
+- examples/mount/recipes/enable.rb
+- examples/mount/recipes/mount.rb
+- examples/mount/recipes/remount.rb
+- examples/mount/recipes/umount.rb
+- examples/mount/spec/disable_spec.rb
+- examples/mount/spec/enable_spec.rb
+- examples/mount/spec/mount_spec.rb
+- examples/mount/spec/remount_spec.rb
+- examples/mount/spec/umount_spec.rb
+- examples/multiple_actions/recipes/default.rb
+- examples/multiple_actions/recipes/sequential.rb
+- examples/multiple_actions/spec/default_spec.rb
+- examples/multiple_actions/spec/sequential_spec.rb
+- examples/multiple_run_action/recipes/default.rb
+- examples/multiple_run_action/spec/default_spec.rb
+- examples/notifications/recipes/chained.rb
+- examples/notifications/recipes/default.rb
+- examples/notifications/recipes/delayed.rb
+- examples/notifications/recipes/immediately.rb
+- examples/notifications/spec/chained_spec.rb
+- examples/notifications/spec/default_spec.rb
+- examples/notifications/spec/delayed_spec.rb
+- examples/notifications/spec/immediately_spec.rb
+- examples/ohai/recipes/reload.rb
+- examples/ohai/spec/reload_spec.rb
+- examples/package/recipes/install.rb
+- examples/package/recipes/purge.rb
+- examples/package/recipes/reconfig.rb
+- examples/package/recipes/remove.rb
+- examples/package/recipes/upgrade.rb
+- examples/package/spec/install_spec.rb
+- examples/package/spec/purge_spec.rb
+- examples/package/spec/reconfig_spec.rb
+- examples/package/spec/remove_spec.rb
+- examples/package/spec/upgrade_spec.rb
+- examples/pacman_package/recipes/install.rb
+- examples/pacman_package/recipes/purge.rb
+- examples/pacman_package/recipes/remove.rb
+- examples/pacman_package/recipes/upgrade.rb
+- examples/pacman_package/spec/install_spec.rb
+- examples/pacman_package/spec/purge_spec.rb
+- examples/pacman_package/spec/remove_spec.rb
+- examples/pacman_package/spec/upgrade_spec.rb
+- examples/portage_package/recipes/install.rb
+- examples/portage_package/recipes/purge.rb
+- examples/portage_package/recipes/remove.rb
+- examples/portage_package/recipes/upgrade.rb
+- examples/portage_package/spec/install_spec.rb
+- examples/portage_package/spec/purge_spec.rb
+- examples/portage_package/spec/remove_spec.rb
+- examples/portage_package/spec/upgrade_spec.rb
+- examples/powershell_script/recipes/run.rb
+- examples/powershell_script/spec/run_spec.rb
+- examples/reboot/recipes/cancel.rb
+- examples/reboot/recipes/now.rb
+- examples/reboot/recipes/request.rb
+- examples/reboot/spec/cancel_spec.rb
+- examples/reboot/spec/now_spec.rb
+- examples/reboot/spec/request_spec.rb
+- examples/registry_key/recipes/create.rb
+- examples/registry_key/recipes/create_if_missing.rb
+- examples/registry_key/recipes/delete.rb
+- examples/registry_key/recipes/delete_key.rb
+- examples/registry_key/spec/create_if_missing_spec.rb
+- examples/registry_key/spec/create_spec.rb
+- examples/registry_key/spec/delete_key_spec.rb
+- examples/registry_key/spec/delete_spec.rb
+- examples/remote_directory/recipes/create.rb
+- examples/remote_directory/recipes/create_if_missing.rb
+- examples/remote_directory/recipes/delete.rb
+- examples/remote_directory/spec/create_if_missing_spec.rb
+- examples/remote_directory/spec/create_spec.rb
+- examples/remote_directory/spec/delete_spec.rb
+- examples/remote_file/recipes/create.rb
+- examples/remote_file/recipes/create_if_missing.rb
+- examples/remote_file/recipes/delete.rb
+- examples/remote_file/recipes/touch.rb
+- examples/remote_file/spec/create_if_missing_spec.rb
+- examples/remote_file/spec/create_spec.rb
+- examples/remote_file/spec/delete_spec.rb
+- examples/remote_file/spec/touch_spec.rb
+- examples/render_file/files/default/cookbook_file
+- examples/render_file/recipes/default.rb
+- examples/render_file/recipes/template_helpers.rb
+- examples/render_file/spec/default_spec.rb
+- examples/render_file/spec/template_helpers_spec.rb
+- examples/render_file/templates/default/_partial.erb
+- examples/render_file/templates/default/partial.erb
+- examples/render_file/templates/default/template.erb
+- examples/render_file/templates/default/template_with_helper.erb
+- examples/roles/recipes/another.rb
+- examples/roles/recipes/default.rb
+- examples/roles/roles/role.rb
+- examples/roles/spec/default_spec.rb
+- examples/route/recipes/add.rb
+- examples/route/recipes/delete.rb
+- examples/route/spec/add_spec.rb
+- examples/route/spec/delete_spec.rb
+- examples/rpm_package/recipes/install.rb
+- examples/rpm_package/recipes/remove.rb
+- examples/rpm_package/recipes/upgrade.rb
+- examples/rpm_package/spec/install_spec.rb
+- examples/rpm_package/spec/remove_spec.rb
+- examples/rpm_package/spec/upgrade_spec.rb
+- examples/ruby_block/recipes/run.rb
+- examples/ruby_block/spec/run_spec.rb
+- examples/script/recipes/run_bash.rb
+- examples/script/recipes/run_csh.rb
+- examples/script/recipes/run_perl.rb
+- examples/script/recipes/run_python.rb
+- examples/script/recipes/run_ruby.rb
+- examples/script/recipes/run_script.rb
+- examples/script/spec/run_bash_spec.rb
+- examples/script/spec/run_csh_spec.rb
+- examples/script/spec/run_perl_spec.rb
+- examples/script/spec/run_python_spec.rb
+- examples/script/spec/run_ruby_spec.rb
+- examples/script/spec/run_script_spec.rb
+- examples/server/recipes/client.rb
+- examples/server/recipes/data_bag.rb
+- examples/server/recipes/environment.rb
+- examples/server/recipes/node.rb
+- examples/server/recipes/render_with_cached.rb
+- examples/server/recipes/role.rb
+- examples/server/recipes/search.rb
+- examples/server/spec/client_spec.rb
+- examples/server/spec/data_bag_spec.rb
+- examples/server/spec/environment_spec.rb
+- examples/server/spec/node_spec.rb
+- examples/server/spec/render_with_cached_spec.rb
+- examples/server/spec/role_spec.rb
+- examples/server/spec/search_spec.rb
+- examples/service/recipes/disable.rb
+- examples/service/recipes/enable.rb
+- examples/service/recipes/reload.rb
+- examples/service/recipes/restart.rb
+- examples/service/recipes/start.rb
+- examples/service/recipes/stop.rb
+- examples/service/spec/disable_spec.rb
+- examples/service/spec/enable_spec.rb
+- examples/service/spec/reload_spec.rb
+- examples/service/spec/restart_spec.rb
+- examples/service/spec/start_spec.rb
+- examples/service/spec/stop_spec.rb
+- examples/smartos_package/recipes/install.rb
+- examples/smartos_package/recipes/remove.rb
+- examples/smartos_package/recipes/upgrade.rb
+- examples/smartos_package/spec/install_spec.rb
+- examples/smartos_package/spec/remove_spec.rb
+- examples/smartos_package/spec/upgrade_spec.rb
+- examples/solaris_package/recipes/install.rb
+- examples/solaris_package/recipes/remove.rb
+- examples/solaris_package/spec/install_spec.rb
+- examples/solaris_package/spec/remove_spec.rb
+- examples/state_attrs/providers/lwrp.rb
+- examples/state_attrs/recipes/default.rb
+- examples/state_attrs/resources/lwrp.rb
+- examples/state_attrs/spec/default_spec.rb
+- examples/step_into/providers/lwrp.rb
+- examples/step_into/recipes/default.rb
+- examples/step_into/resources/lwrp.rb
+- examples/step_into/spec/default_spec.rb
+- examples/stub_command/recipes/default.rb
+- examples/stub_command/recipes/other_recipe.rb
+- examples/stub_command/spec/default_spec.rb
+- examples/stub_data_bag/recipes/default.rb
+- examples/stub_data_bag/spec/default_spec.rb
+- examples/stub_data_bag_item/recipes/default.rb
+- examples/stub_data_bag_item/spec/default_spec.rb
+- examples/stub_node/recipes/default.rb
+- examples/stub_node/spec/default_spec.rb
+- examples/stub_search/recipes/default.rb
+- examples/stub_search/spec/default_spec.rb
+- examples/subscribes/recipes/chained.rb
+- examples/subscribes/recipes/default.rb
+- examples/subscribes/recipes/delayed.rb
+- examples/subscribes/recipes/immediately.rb
+- examples/subscribes/spec/chained_spec.rb
+- examples/subscribes/spec/default_spec.rb
+- examples/subscribes/spec/delayed_spec.rb
+- examples/subscribes/spec/immediately_spec.rb
+- examples/subversion/recipes/checkout.rb
+- examples/subversion/recipes/export.rb
+- examples/subversion/recipes/force_export.rb
+- examples/subversion/recipes/sync.rb
+- examples/subversion/spec/checkout_spec.rb
+- examples/subversion/spec/export_spec.rb
+- examples/subversion/spec/force_export_spec.rb
+- examples/subversion/spec/sync_spec.rb
+- examples/template/recipes/create.rb
+- examples/template/recipes/create_if_missing.rb
+- examples/template/recipes/delete.rb
+- examples/template/recipes/touch.rb
+- examples/template/spec/create_if_missing_spec.rb
+- examples/template/spec/create_spec.rb
+- examples/template/spec/delete_spec.rb
+- examples/template/spec/touch_spec.rb
+- examples/use_inline_resources/libraries/matchers.rb
+- examples/use_inline_resources/providers/lwrp.rb
+- examples/use_inline_resources/recipes/default.rb
+- examples/use_inline_resources/resources/lwrp.rb
+- examples/use_inline_resources/spec/default_spec.rb
+- examples/user/recipes/create.rb
+- examples/user/recipes/lock.rb
+- examples/user/recipes/manage.rb
+- examples/user/recipes/modify.rb
+- examples/user/recipes/remove.rb
+- examples/user/recipes/unlock.rb
+- examples/user/spec/create_spec.rb
+- examples/user/spec/lock_spec.rb
+- examples/user/spec/manage_spec.rb
+- examples/user/spec/modify_spec.rb
+- examples/user/spec/remove_spec.rb
+- examples/user/spec/unlock_spec.rb
+- examples/windows_service/recipes/configure_startup.rb
+- examples/windows_service/recipes/disable.rb
+- examples/windows_service/recipes/enable.rb
+- examples/windows_service/recipes/reload.rb
+- examples/windows_service/recipes/restart.rb
+- examples/windows_service/recipes/start.rb
+- examples/windows_service/recipes/stop.rb
+- examples/windows_service/spec/configure_startup_spec.rb
+- examples/windows_service/spec/disable_spec.rb
+- examples/windows_service/spec/enable_spec.rb
+- examples/windows_service/spec/reload_spec.rb
+- examples/windows_service/spec/restart_spec.rb
+- examples/windows_service/spec/start_spec.rb
+- examples/windows_service/spec/stop_spec.rb
+- examples/yum_package/recipes/install.rb
+- examples/yum_package/recipes/purge.rb
+- examples/yum_package/recipes/remove.rb
+- examples/yum_package/recipes/upgrade.rb
+- examples/yum_package/spec/install_spec.rb
+- examples/yum_package/spec/purge_spec.rb
+- examples/yum_package/spec/remove_spec.rb
+- examples/yum_package/spec/upgrade_spec.rb
+- features/apt_package.feature
+- features/attributes.feature
+- features/batch.feature
+- features/cached.feature
+- features/chef_gem.feature
+- features/compile_time.feature
+- features/cookbook_file.feature
+- features/cron.feature
+- features/custom_matcher.feature
+- features/deploy.feature
+- features/directory.feature
+- features/do_nothing.feature
+- features/dpkg_package.feature
+- features/easy_install_package.feature
+- features/env.feature
+- features/erl_call.feature
+- features/execute.feature
+- features/expect_exception.feature
+- features/file.feature
+- features/freebsd_package.feature
+- features/gem_package.feature
+- features/git.feature
+- features/group.feature
+- features/guards.feature
+- features/heavy_provider_light_resource.feature
+- features/http_request.feature
+- features/ifconfig.feature
+- features/include_recipe.feature
+- features/ips_package.feature
+- features/link.feature
+- features/log.feature
+- features/macports_package.feature
+- features/mdadm.feature
+- features/mount.feature
+- features/multiple_actions.feature
+- features/multiple_run_action.feature
+- features/notifications.feature
+- features/ohai.feature
+- features/package.feature
+- features/pacman_package.feature
+- features/portage_package.feature
+- features/powershell_script.feature
+- features/reboot.feature
+- features/registry_key.feature
+- features/remote_directory.feature
+- features/remote_file.feature
+- features/render_file.feature
+- features/roles.feature
+- features/route.feature
+- features/rpm_package.feature
+- features/ruby_block.feature
+- features/script.feature
+- features/server.feature
+- features/service.feature
+- features/smartos_package.feature
+- features/solaris_package.feature
+- features/state_attrs.feature
+- features/step_definitions/background_steps.rb
+- features/step_into.feature
+- features/stub_command.feature
+- features/stub_data_bag.feature
+- features/stub_data_bag_item.feature
+- features/stub_node.feature
+- features/stub_search.feature
+- features/subscribes.feature
+- features/subversion.feature
+- features/support/env.rb
+- features/support/executor.rb
+- features/template.feature
+- features/use_inline_resources.feature
+- features/user.feature
+- features/windows_service.feature
+- features/yum_package.feature
+- gemfiles/chefspec.gemfile
+- lib/chefspec.rb
+- lib/chefspec/api.rb
+- lib/chefspec/api/apt_package.rb
+- lib/chefspec/api/batch.rb
+- lib/chefspec/api/chef_gem.rb
+- lib/chefspec/api/cookbook_file.rb
+- lib/chefspec/api/cron.rb
+- lib/chefspec/api/deploy.rb
+- lib/chefspec/api/directory.rb
+- lib/chefspec/api/do_nothing.rb
+- lib/chefspec/api/dpkg_package.rb
+- lib/chefspec/api/easy_install_package.rb
+- lib/chefspec/api/env.rb
+- lib/chefspec/api/erl_call.rb
+- lib/chefspec/api/execute.rb
+- lib/chefspec/api/file.rb
+- lib/chefspec/api/freebsd_package.rb
+- lib/chefspec/api/gem_package.rb
+- lib/chefspec/api/git.rb
+- lib/chefspec/api/group.rb
+- lib/chefspec/api/http_request.rb
+- lib/chefspec/api/ifconfig.rb
+- lib/chefspec/api/include_recipe.rb
+- lib/chefspec/api/ips_package.rb
+- lib/chefspec/api/link.rb
+- lib/chefspec/api/log.rb
+- lib/chefspec/api/macports_package.rb
+- lib/chefspec/api/mdadm.rb
+- lib/chefspec/api/mount.rb
+- lib/chefspec/api/notifications.rb
+- lib/chefspec/api/ohai.rb
+- lib/chefspec/api/package.rb
+- lib/chefspec/api/pacman_package.rb
+- lib/chefspec/api/portage_package.rb
+- lib/chefspec/api/powershell_script.rb
+- lib/chefspec/api/reboot.rb
+- lib/chefspec/api/registry_key.rb
+- lib/chefspec/api/remote_directory.rb
+- lib/chefspec/api/remote_file.rb
+- lib/chefspec/api/render_file.rb
+- lib/chefspec/api/route.rb
+- lib/chefspec/api/rpm_package.rb
+- lib/chefspec/api/ruby_block.rb
+- lib/chefspec/api/script.rb
+- lib/chefspec/api/service.rb
+- lib/chefspec/api/smartos_package.rb
+- lib/chefspec/api/solaris_package.rb
+- lib/chefspec/api/state_attrs.rb
+- lib/chefspec/api/subscriptions.rb
+- lib/chefspec/api/subversion.rb
+- lib/chefspec/api/template.rb
+- lib/chefspec/api/user.rb
+- lib/chefspec/api/windows_service.rb
+- lib/chefspec/api/yum_package.rb
+- lib/chefspec/berkshelf.rb
+- lib/chefspec/cacher.rb
+- lib/chefspec/chef_backwards_compat.rb
+- lib/chefspec/coverage.rb
+- lib/chefspec/coverage/filters.rb
+- lib/chefspec/deprecations.rb
+- lib/chefspec/errors.rb
+- lib/chefspec/expect_exception.rb
+- lib/chefspec/extensions/chef/client.rb
+- lib/chefspec/extensions/chef/conditional.rb
+- lib/chefspec/extensions/chef/cookbook_uploader.rb
+- lib/chefspec/extensions/chef/data_query.rb
+- lib/chefspec/extensions/chef/lwrp_base.rb
+- lib/chefspec/extensions/chef/resource.rb
+- lib/chefspec/extensions/chef/resource/freebsd_package.rb
+- lib/chefspec/extensions/chef/securable.rb
+- lib/chefspec/formatter.rb
+- lib/chefspec/librarian.rb
+- lib/chefspec/macros.rb
+- lib/chefspec/matchers.rb
+- lib/chefspec/matchers/do_nothing_matcher.rb
+- lib/chefspec/matchers/include_recipe_matcher.rb
+- lib/chefspec/matchers/link_to_matcher.rb
+- lib/chefspec/matchers/notifications_matcher.rb
+- lib/chefspec/matchers/render_file_matcher.rb
+- lib/chefspec/matchers/resource_matcher.rb
+- lib/chefspec/matchers/state_attrs_matcher.rb
+- lib/chefspec/matchers/subscribes_matcher.rb
+- lib/chefspec/mixins/normalize.rb
+- lib/chefspec/renderer.rb
+- lib/chefspec/rspec.rb
+- lib/chefspec/server.rb
+- lib/chefspec/server_methods.rb
+- lib/chefspec/server_runner.rb
+- lib/chefspec/solo_runner.rb
+- lib/chefspec/stubs/command_registry.rb
+- lib/chefspec/stubs/command_stub.rb
+- lib/chefspec/stubs/data_bag_item_registry.rb
+- lib/chefspec/stubs/data_bag_item_stub.rb
+- lib/chefspec/stubs/data_bag_registry.rb
+- lib/chefspec/stubs/data_bag_stub.rb
+- lib/chefspec/stubs/registry.rb
+- lib/chefspec/stubs/search_registry.rb
+- lib/chefspec/stubs/search_stub.rb
+- lib/chefspec/stubs/stub.rb
+- lib/chefspec/util.rb
+- lib/chefspec/version.rb
+- spec/spec_helper.rb
+- spec/support/hash.rb
+- spec/unit/api_spec.rb
+- spec/unit/cacher_spec.rb
+- spec/unit/coverage/filters_spec.rb
+- spec/unit/deprecations_spec.rb
+- spec/unit/errors_spec.rb
+- spec/unit/expect_exception_spec.rb
+- spec/unit/extensions/lwrp_base_spec.rb
+- spec/unit/macros_spec.rb
+- spec/unit/matchers/do_nothing_matcher.rb
+- spec/unit/matchers/include_recipe_matcher_spec.rb
+- spec/unit/matchers/link_to_matcher_spec.rb
+- spec/unit/matchers/notifications_matcher_spec.rb
+- spec/unit/matchers/render_file_matcher_spec.rb
+- spec/unit/matchers/resource_matcher_spec.rb
+- spec/unit/matchers/state_attrs_matcher_spec.rb
+- spec/unit/matchers/subscribes_matcher_spec.rb
+- spec/unit/renderer_spec.rb
+- spec/unit/solo_runner_spec.rb
+- spec/unit/stubs/command_registry_spec.rb
+- spec/unit/stubs/command_stub_spec.rb
+- spec/unit/stubs/data_bag_item_registry_spec.rb
+- spec/unit/stubs/data_bag_item_stub_spec.rb
+- spec/unit/stubs/data_bag_registry_spec.rb
+- spec/unit/stubs/data_bag_stub_spec.rb
+- spec/unit/stubs/registry_spec.rb
+- spec/unit/stubs/search_registry_spec.rb
+- spec/unit/stubs/search_stub_spec.rb
+- spec/unit/stubs/stub_spec.rb
+- templates/coverage/human.erb
+- templates/errors/cookbook_path_not_found.erb
+- templates/errors/gem_load_error.erb
+- templates/errors/no_conversion_error.erb
+- templates/errors/not_stubbed.erb
+homepage: https://sethvargo.github.io/chefspec/
+licenses:
+- MIT
+metadata: {}
+post_install_message: 
+rdoc_options: []
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - ">="
+    - !ruby/object:Gem::Version
+      version: '1.9'
+required_rubygems_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - ">="
+    - !ruby/object:Gem::Version
+      version: '0'
+requirements: []
+rubyforge_project: 
+rubygems_version: 2.2.3
+signing_key: 
+specification_version: 4
+summary: Write RSpec examples and generate coverage reports for Chef recipes!
+test_files:
+- features/apt_package.feature
+- features/attributes.feature
+- features/batch.feature
+- features/cached.feature
+- features/chef_gem.feature
+- features/compile_time.feature
+- features/cookbook_file.feature
+- features/cron.feature
+- features/custom_matcher.feature
+- features/deploy.feature
+- features/directory.feature
+- features/do_nothing.feature
+- features/dpkg_package.feature
+- features/easy_install_package.feature
+- features/env.feature
+- features/erl_call.feature
+- features/execute.feature
+- features/expect_exception.feature
+- features/file.feature
+- features/freebsd_package.feature
+- features/gem_package.feature
+- features/git.feature
+- features/group.feature
+- features/guards.feature
+- features/heavy_provider_light_resource.feature
+- features/http_request.feature
+- features/ifconfig.feature
+- features/include_recipe.feature
+- features/ips_package.feature
+- features/link.feature
+- features/log.feature
+- features/macports_package.feature
+- features/mdadm.feature
+- features/mount.feature
+- features/multiple_actions.feature
+- features/multiple_run_action.feature
+- features/notifications.feature
+- features/ohai.feature
+- features/package.feature
+- features/pacman_package.feature
+- features/portage_package.feature
+- features/powershell_script.feature
+- features/reboot.feature
+- features/registry_key.feature
+- features/remote_directory.feature
+- features/remote_file.feature
+- features/render_file.feature
+- features/roles.feature
+- features/route.feature
+- features/rpm_package.feature
+- features/ruby_block.feature
+- features/script.feature
+- features/server.feature
+- features/service.feature
+- features/smartos_package.feature
+- features/solaris_package.feature
+- features/state_attrs.feature
+- features/step_definitions/background_steps.rb
+- features/step_into.feature
+- features/stub_command.feature
+- features/stub_data_bag.feature
+- features/stub_data_bag_item.feature
+- features/stub_node.feature
+- features/stub_search.feature
+- features/subscribes.feature
+- features/subversion.feature
+- features/support/env.rb
+- features/support/executor.rb
+- features/template.feature
+- features/use_inline_resources.feature
+- features/user.feature
+- features/windows_service.feature
+- features/yum_package.feature
+- spec/spec_helper.rb
+- spec/support/hash.rb
+- spec/unit/api_spec.rb
+- spec/unit/cacher_spec.rb
+- spec/unit/coverage/filters_spec.rb
+- spec/unit/deprecations_spec.rb
+- spec/unit/errors_spec.rb
+- spec/unit/expect_exception_spec.rb
+- spec/unit/extensions/lwrp_base_spec.rb
+- spec/unit/macros_spec.rb
+- spec/unit/matchers/do_nothing_matcher.rb
+- spec/unit/matchers/include_recipe_matcher_spec.rb
+- spec/unit/matchers/link_to_matcher_spec.rb
+- spec/unit/matchers/notifications_matcher_spec.rb
+- spec/unit/matchers/render_file_matcher_spec.rb
+- spec/unit/matchers/resource_matcher_spec.rb
+- spec/unit/matchers/state_attrs_matcher_spec.rb
+- spec/unit/matchers/subscribes_matcher_spec.rb
+- spec/unit/renderer_spec.rb
+- spec/unit/solo_runner_spec.rb
+- spec/unit/stubs/command_registry_spec.rb
+- spec/unit/stubs/command_stub_spec.rb
+- spec/unit/stubs/data_bag_item_registry_spec.rb
+- spec/unit/stubs/data_bag_item_stub_spec.rb
+- spec/unit/stubs/data_bag_registry_spec.rb
+- spec/unit/stubs/data_bag_stub_spec.rb
+- spec/unit/stubs/registry_spec.rb
+- spec/unit/stubs/search_registry_spec.rb
+- spec/unit/stubs/search_stub_spec.rb
+- spec/unit/stubs/stub_spec.rb
+has_rdoc: 
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..26b155f
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,8 @@
+require 'chefspec'
+require 'support/hash'
+
+RSpec.configure do |config|
+  config.expect_with(:rspec) { |c| c.syntax = :expect }
+  config.filter_run(focus: true)
+  config.run_all_when_everything_filtered = true
+end
diff --git a/spec/support/hash.rb b/spec/support/hash.rb
new file mode 100644
index 0000000..582e2ae
--- /dev/null
+++ b/spec/support/hash.rb
@@ -0,0 +1,35 @@
+#
+# An extension of a Hash that allows mash-style like lookups.
+#
+# @private
+#
+class Hash
+  # Like, seriously Windows?
+  undef_method(:timeout)
+
+  #
+  # Monkey-patch to allow mash-style look ups for tests
+  #
+  def method_missing(m, *args, &block)
+    if has_key?(m.to_sym)
+      self[m.to_sym]
+    elsif has_key?(m.to_s)
+      self[m.to_s]
+    else
+      super
+    end
+  end
+
+  #
+  # Monkey-patch to stdlib Hash to correspond to Mash-style lookup
+  #
+  # @see Hash#respond_to?
+  #
+  def respond_to?(m, include_private = false)
+    if has_key?(m.to_sym) || has_key?(m.to_s)
+      true
+    else
+      super
+    end
+  end
+end
diff --git a/spec/unit/api_spec.rb b/spec/unit/api_spec.rb
new file mode 100644
index 0000000..b14920a
--- /dev/null
+++ b/spec/unit/api_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe ChefSpec::API do
+  before do
+    module ChefSpec::API
+      module CustomSubmodule
+        def custom_method; end
+      end
+    end
+  end
+
+  subject { Class.new { include ChefSpec::API }.new }
+
+  it 'includes all submodules in the including class' do
+    expect(subject).to be_a(ChefSpec::API::CustomSubmodule)
+    expect(subject).to respond_to(:custom_method)
+  end
+end
diff --git a/spec/unit/cacher_spec.rb b/spec/unit/cacher_spec.rb
new file mode 100644
index 0000000..a0e8ae2
--- /dev/null
+++ b/spec/unit/cacher_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+require 'chefspec/cacher'
+
+describe ChefSpec::Cacher do
+  let(:klass) do
+    Class.new(RSpec::Core::ExampleGroup) do
+      extend ChefSpec::Cacher
+
+      def self.metadata
+        { parent_example_group: { location: 'spec' } }
+      end
+    end
+  end
+
+  let(:cache) { described_class.class_variable_get(:@@cache) }
+
+  before(:each) { described_class.class_variable_set(:@@cache, {}) }
+
+  describe 'cached' do
+    it 'lazily defines the results for the cache' do
+      klass.cached(:chef_run)
+      expect(klass).to be_method_defined(:chef_run)
+    end
+
+    it 'adds the item to the cache when called' do
+      runner = double(:runner)
+      klass.cached(:chef_run) { runner }
+      klass.new.chef_run
+
+      expect(cache[Thread.current.object_id]).to have_key('spec.chef_run')
+      expect(cache[Thread.current.object_id]['spec.chef_run']).to eq(runner)
+    end
+
+    context 'when multithreaded environment' do
+      it 'is thread safe' do
+        (1..2).each do |n|
+          Thread.new do
+            klass.cached(:chef_run) { n }
+            expect(klass.new.chef_run).to eq(n)
+          end.join
+        end
+      end
+    end
+  end
+
+  describe 'cached!' do
+    it 'loads the value at runtime' do
+      expect(klass).to receive(:cached).with(:chef_run).once
+      expect(klass).to receive(:before).once
+
+      klass.cached!(:chef_run) { }
+    end
+  end
+end
diff --git a/spec/unit/coverage/filters_spec.rb b/spec/unit/coverage/filters_spec.rb
new file mode 100644
index 0000000..a049fb4
--- /dev/null
+++ b/spec/unit/coverage/filters_spec.rb
@@ -0,0 +1,60 @@
+require 'spec_helper'
+
+# Note: These specs don't use Berkshelf code directly as this project doesn't
+# have a direct dependency on Berkshelf and loading it would impact the
+# perfance of these specs. While not ideal, the test doubles provide enough of
+# a standin for Berkshelf to exercise the `#matches?` behavior.
+describe ChefSpec::Coverage::BerkshelfFilter do
+  let(:dependencies) do
+    [double('Berkshelf::Dependency', metadata?: true, name: "cookbookery")]
+  end
+  let(:berksfile) { double('Berkshelf::Berksfile', dependencies: dependencies) }
+  let(:resource) { Chef::Resource.new('theone') }
+  subject { described_class.new(berksfile) }
+
+  describe '#matches?' do
+    it 'returns truthy if resource source_line is nil' do
+      expect(subject.matches?(resource)).to be_truthy
+    end
+
+    context 'when resource#source_line is under target cookbook' do
+      it 'normal unix path returns truthy' do
+        resource.source_line =
+          '/path/to/cookbooks/nope/recipes/default.rb:22'
+        expect(subject.matches?(resource)).to be_truthy
+      end
+
+      it 'normal windows path returns truthy' do
+        resource.source_line =
+          'C:\\path\\to\\cookbooks\\nope\\recipes\\default.rb:22'
+        expect(subject.matches?(resource)).to be_truthy
+      end
+
+      it 'mixed windows path returns truthy' do
+        resource.source_line =
+          'C:\\path\\to\\cookbooks/nope/recipes/default.rb:22'
+        expect(subject.matches?(resource)).to be_truthy
+      end
+    end
+
+    context 'when resource#source_line is not under target cookbook' do
+      it 'normal unix path returns falsey' do
+        resource.source_line =
+          '/path/to/cookbooks/cookbookery/recipes/default.rb:22'
+        expect(subject.matches?(resource)).to be_falsey
+      end
+
+      it 'normal windows path returns falsey' do
+        resource.source_line =
+          'C:\\path\\to\\cookbooks\\cookbookery\\recipes\\default.rb:22'
+        expect(subject.matches?(resource)).to be_falsey
+      end
+
+      it 'mixed windows path returns falsey' do
+        resource.source_line =
+          'C:\\path\\to\\cookbooks/cookbookery/recipes/default.rb:22'
+        expect(subject.matches?(resource)).to be_falsey
+      end
+    end
+  end
+end
diff --git a/spec/unit/deprecations_spec.rb b/spec/unit/deprecations_spec.rb
new file mode 100644
index 0000000..a765d84
--- /dev/null
+++ b/spec/unit/deprecations_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe ChefSpec::Runner do
+  before do
+    allow_any_instance_of(ChefSpec::SoloRunner)
+      .to receive(:dry_run?)
+      .and_return(true)
+    allow(ChefSpec::Runner).to receive(:deprecated)
+  end
+
+  describe '#define_runner_method' do
+    before do
+      allow(ChefSpec).to receive(:define_matcher)
+    end
+
+    it 'prints a deprecation' do
+      expect(ChefSpec::Runner).to receive(:deprecated)
+        .with("`ChefSpec::Runner.define_runner_method' is deprecated."\
+          " It is being used in the my_custom_resource resource matcher." \
+          " Please use `ChefSpec.define_matcher' instead.")
+      ChefSpec::Runner.define_runner_method(:my_custom_resource)
+    end
+
+    it 'calls ChefSpec#define_matcher' do
+      expect(ChefSpec).to receive(:define_matcher).with(:my_custom_resource).once
+      ChefSpec::Runner.define_runner_method(:my_custom_resource)
+    end
+
+  end
+
+  describe '#new' do
+    before do
+      allow(ChefSpec::SoloRunner).to receive(:new)
+    end
+
+    it 'prints a deprecation' do
+      expect(ChefSpec::Runner).to receive(:deprecated)
+        .with("`ChefSpec::Runner' is deprecated. Please use" \
+        " `ChefSpec::SoloRunner' or `ChefSpec::ServerRunner' instead.")
+      ChefSpec::Runner.new
+    end
+
+    it 'calls SoloRunner#new with no args' do
+      expect(ChefSpec::SoloRunner).to receive(:new).with(no_args()).once
+      ChefSpec::Runner.new
+    end
+
+    it 'calls SoloRunner#new with args' do
+      args = [ 'args' ]
+      expect(ChefSpec::SoloRunner).to receive(:new).with(*args).once
+      ChefSpec::Runner.new(*args)
+    end
+
+  end
+end
+
+describe ChefSpec::Server do
+  before do
+    allow(ChefSpec::Server).to receive(:deprecated)
+  end
+
+  it 'prints a deprecation for any method called' do
+    expect(ChefSpec::Server).to receive(:deprecated)
+      .with("`ChefSpec::Server.any_method' is deprecated. There is no longer" \
+        " a global Chef Server instance. Please use a ChefSpec::ServerRunner" \
+        " instead. More documentation can be found in the ChefSpec README."
+      )
+    expect{ChefSpec::Server.any_method}.to raise_error
+  end
+
+  it 'raises non-conversion error for any method called' do
+    expect{ChefSpec::Server.any_method}
+      .to raise_error(ChefSpec::Error::NoConversionError)
+  end
+
+end
diff --git a/spec/unit/errors_spec.rb b/spec/unit/errors_spec.rb
new file mode 100644
index 0000000..03bfcec
--- /dev/null
+++ b/spec/unit/errors_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+
+module ChefSpec::Error
+  describe CommandNotStubbed do
+    let(:instance) { described_class.new(args: ['cat']) }
+
+    it 'raises an exception with the correct message' do
+      instance
+      expect { raise instance }.to raise_error { |error|
+        expect(error).to be_a(described_class)
+        expect(error.message).to eq <<-EOH.gsub(/^ {10}/, '')
+          Executing a real command is disabled. Unregistered command:
+
+              command("cat")
+
+          You can stub this command with:
+
+              stub_command("cat").and_return(...)
+        EOH
+      }
+    end
+  end
+
+  describe CookbookPathNotFound do
+    let(:instance) { described_class.new }
+
+    it 'raises an exception with the correct message' do
+      expect { raise instance }.to raise_error { |error|
+        expect(error).to be_a(described_class)
+        expect(error.message).to eq <<-EOH.gsub(/^ {10}/, '')
+          I could not find or infer a cookbook_path from your current working directory.
+          Please make sure you put your specs (tests) under a directory named 'spec' or
+          manually set the cookbook path in the RSpec configuration.
+        EOH
+      }
+    end
+  end
+
+  describe GemLoadError do
+    let(:instance) { described_class.new(gem: 'bacon', name: 'bacon') }
+
+    it 'raises an exception with the correct message' do
+      expect { raise instance }.to raise_error { |error|
+        expect(error).to be_a(described_class)
+        expect(error.message).to eq <<-EOH.gsub(/^ {10}/, '')
+          I could not load the 'bacon' gem! You must have the gem installed
+          on your local system before you can use the bacon plugin.
+          You can install bacon by running:
+
+              gem install bacon
+
+          or add bacon to your Gemfile and run the `bundle` command to install.
+        EOH
+      }
+    end
+  end
+end
diff --git a/spec/unit/expect_exception_spec.rb b/spec/unit/expect_exception_spec.rb
new file mode 100644
index 0000000..a3ed1d9
--- /dev/null
+++ b/spec/unit/expect_exception_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe ChefSpec::ExpectException do
+  context 'when there have been no `raise_error` matchers' do
+    subject { described_class.new(Exception) }
+
+    it 'does not match' do
+      allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(nil)
+      expect(subject.expected?).to be_falsy
+    end
+  end
+
+  context 'when the last error does not match the expected type' do
+    subject { described_class.new(RuntimeError) }
+
+    it 'does not match' do
+      last_error = double('last error', last_error_for_chefspec: ArgumentError)
+      allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(last_error)
+      expect(subject.expected?).to be_falsy
+    end
+  end
+
+  context 'when the last error matches the expected type' do
+    subject { described_class.new(RuntimeError) }
+
+    it 'does not match' do
+      last_error = double('last error', last_error_for_chefspec: RuntimeError)
+      allow(RSpec::Matchers::BuiltIn::RaiseError).to receive(:last_run).and_return(last_error)
+      expect(subject.expected?).to be_truthy
+    end
+  end
+end
diff --git a/spec/unit/extensions/lwrp_base_spec.rb b/spec/unit/extensions/lwrp_base_spec.rb
new file mode 100644
index 0000000..3337674
--- /dev/null
+++ b/spec/unit/extensions/lwrp_base_spec.rb
@@ -0,0 +1,96 @@
+require 'spec_helper'
+
+# Chef 12 fixed resource inheritance issues
+if Chef::VERSION.to_f < 12.0
+  module ChefSpec
+    module Extensions
+      describe :LWRPBase do
+        describe '#remove_existing_lwrp' do
+          before do
+            Chef::Provider::MysqlDatabase = nil
+            Chef::Resource::MysqlDatabase = Class.new(Chef::Resource)
+          end
+
+          after do
+            [Chef::Provider, Chef::Resource].each do |mod|
+              next unless mod.const_defined?(:MysqlDatabase, false)
+              mod.send(:remove_const, :MysqlDatabase)
+            end
+          end
+
+          context Chef::Provider do
+            before do
+              Chef::Provider::LWRPBase.remove_existing_lwrp('MysqlDatabase')
+            end
+
+            it 'removes the provider if it already exists' do
+              expect(Chef::Provider.constants).to_not include(:MysqlDatabase)
+            end
+
+            it 'does not remove resource'  do
+              expect(Chef::Resource.constants).to include(:MysqlDatabase)
+            end
+
+            it 'does not throw an error if the resource does not already exist' do
+              expect {
+                Chef::Provider::LWRPBase.remove_existing_lwrp 'Human'
+              }.to_not raise_error
+            end
+          end
+
+          context Chef::Resource do
+            let!(:resource_class) { Chef::Resource::MysqlDatabase }
+
+            before do
+              Chef::Resource::LWRPBase.remove_existing_lwrp('MysqlDatabase')
+            end
+
+            it 'removes the resource if it already exists' do
+              expect(Chef::Resource.constants).to_not include(:MysqlDatabase)
+            end
+
+            if Chef::Resource.respond_to?(:resource_classes)
+              it 'removes the resource from Chef::Resource.resource_classes' do
+                expect(Chef::Resource.resource_classes).to_not include(resource_class)
+              end
+            end
+
+            it 'does not remove the provider'  do
+              Chef::Resource::LWRPBase.remove_existing_lwrp('MysqlDatabase')
+              expect(Chef::Provider.constants).to include(:MysqlDatabase)
+            end
+
+            it 'does not throw an error if the resource does not already exist' do
+              expect {
+                Chef::Resource::LWRPBase.remove_existing_lwrp 'Human'
+              }.to_not raise_error
+            end
+          end
+        end
+      end
+
+      describe '#build_from_file' do
+        let(:args){ %w{mycookbook thisfile context} }
+
+        shared_context "wrapping" do
+          it 'wraps the existing chef build_from_file method' do
+            klass = mod::LWRPBase
+            allow(klass).to receive(:build_from_file_without_removal)
+            expect(klass).to receive(:build_from_file_without_removal).with(*args)
+            klass.build_from_file(*args)
+          end
+        end
+
+        context Chef::Provider do
+          let(:mod){ Chef::Provider }
+          include_context "wrapping"
+        end
+
+        context Chef::Resource do
+          let(:mod){ Chef::Resource }
+          include_context "wrapping"
+        end
+      end
+    end
+  end
+end
diff --git a/spec/unit/macros_spec.rb b/spec/unit/macros_spec.rb
new file mode 100644
index 0000000..b05292b
--- /dev/null
+++ b/spec/unit/macros_spec.rb
@@ -0,0 +1,119 @@
+require 'spec_helper'
+
+describe ChefSpec::Macros do
+  describe '#stub_command' do
+    let(:command_stub) { double('command') }
+
+    it 'adds the command to the command registry' do
+      allow(ChefSpec::Stubs::CommandStub).to receive(:new).and_return(command_stub)
+      described_class.stub_command('echo "hello"')
+
+      expect(ChefSpec::Stubs::CommandRegistry.stubs).to include(command_stub)
+    end
+  end
+
+  describe '#stub_search' do
+    let(:search_stub) { double('search') }
+
+    it 'adds the query to the search registry' do
+      allow(ChefSpec::Stubs::SearchStub).to receive(:new).and_return(search_stub)
+      described_class.stub_search(:node, '*:*')
+
+      expect(ChefSpec::Stubs::SearchRegistry.stubs).to include(search_stub)
+    end
+  end
+
+  describe '#stub_data_bag' do
+    let(:data_bag_stub) { double('data_bag') }
+
+    it 'adds the query to the data_bag registry' do
+      allow(ChefSpec::Stubs::DataBagStub).to receive(:new).and_return(data_bag_stub)
+      described_class.stub_data_bag(:users)
+
+      expect(ChefSpec::Stubs::DataBagRegistry.stubs).to include(data_bag_stub)
+    end
+  end
+
+  describe '#stub_data_bag_item' do
+    let(:data_bag_item_stub) { double('data_bag_item') }
+
+    it 'adds the query to the data_bag_item registry' do
+      allow(ChefSpec::Stubs::DataBagItemStub).to receive(:new).and_return(data_bag_item_stub)
+      described_class.stub_data_bag_item(:users, 'id')
+
+      expect(ChefSpec::Stubs::DataBagItemRegistry.stubs).to include(data_bag_item_stub)
+    end
+  end
+
+  describe '#stub_node' do
+    it 'returns a Chef::Node' do
+      expect(described_class.stub_node).to be_a(Chef::Node)
+    end
+
+    it 'defaults the node name to `node.example`' do
+      node = described_class.stub_node
+      expect(node.name).to eq('node.example')
+    end
+
+    it 'sets the node name when given' do
+      node = described_class.stub_node('example.com')
+      expect(node.name).to eq('example.com')
+    end
+
+    it 'sets the automatic attributes' do
+      node = described_class.stub_node
+      expect(node.automatic).to eq(Fauxhai.mock.data)
+    end
+
+    it 'sets the automatic attributes with ohai overrides' do
+      node = described_class.stub_node('node.example', ohai: { ipaddress: '1.2.3.4' })
+      expect(node['ipaddress']).to eq('1.2.3.4')
+    end
+
+    it 'sets the automatic attributes for a specific platform and version' do
+      node = described_class.stub_node('node.example', platform: 'ubuntu', version: '12.04')
+      expect(node.automatic).to eq(Fauxhai.mock(platform: 'ubuntu', version: '12.04').data)
+    end
+
+    it 'sets the automatic attributes from a JSON data path' do
+      allow(File).to receive(:exists?).with('/path/to/json').and_return(true)
+      allow(File).to receive(:read).with('/path/to/json').and_return('{ "ipaddress": "1.2.3.4" }')
+      node = described_class.stub_node('node.example', path: '/path/to/json')
+      expect(node['ipaddress']).to eq('1.2.3.4')
+    end
+
+    it 'yields a block' do
+      expect { |block| described_class.stub_node(&block) }.to yield_with_args(Chef::Node)
+    end
+  end
+end
+
+describe 'nginx::source' do
+  describe '#described_cookbook' do
+    describe 'nginx::source' do
+      it 'returns the name of the cookbook' do
+        expect(described_cookbook).to eq('nginx')
+      end
+
+      context 'in a nested context' do
+        it 'still returns the name of the cookbook' do
+          expect(described_cookbook).to eq('nginx')
+        end
+      end
+    end
+  end
+
+  describe '#described_recipe' do
+    describe 'nginx::source' do
+      it 'returns the cookbook::recipe' do
+        expect(described_recipe).to eq('nginx::source')
+      end
+
+      context 'in a nested context' do
+        it 'still retrns the cookbook::recipe' do
+          expect(described_recipe).to eq('nginx::source')
+        end
+      end
+    end
+  end
+end
diff --git a/spec/unit/matchers/do_nothing_matcher.rb b/spec/unit/matchers/do_nothing_matcher.rb
new file mode 100644
index 0000000..4e67781
--- /dev/null
+++ b/spec/unit/matchers/do_nothing_matcher.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe ChefSpec::Matchers::DoNothingMatcher do
+  pending
+end
diff --git a/spec/unit/matchers/include_recipe_matcher_spec.rb b/spec/unit/matchers/include_recipe_matcher_spec.rb
new file mode 100644
index 0000000..f9d50c6
--- /dev/null
+++ b/spec/unit/matchers/include_recipe_matcher_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe ChefSpec::Matchers::IncludeRecipeMatcher do
+  let(:chef_run) { double('chef run', run_context: { loaded_recipes: %w(one two three) }) }
+  subject { described_class.new('one::default') }
+
+  describe '#failure_message' do
+    it 'has the right value' do
+      subject.matches?(chef_run)
+      expect(subject.failure_message)
+        .to eq(%q(expected ["one::default", "two::default", "three::default"] to include "one::default"))
+    end
+  end
+
+  describe '#failure_message_when_negated' do
+    it 'has the right value' do
+      subject.matches?(chef_run)
+      expect(subject.failure_message_when_negated)
+        .to eq(%q(expected "one::default" to not be included))
+    end
+  end
+
+  describe '#description' do
+    it 'has the right value' do
+      subject.matches?(chef_run)
+      expect(subject.description).to eq(%q(include recipe "one::default"))
+    end
+  end
+
+  it 'matches when the recipe is included' do
+    expect(subject.matches?(chef_run)).to be_truthy
+  end
+
+  it 'does not match when the recipe is not included' do
+    failure = described_class.new('nope')
+    expect(failure.matches?(chef_run)).to be_falsy
+  end
+end
diff --git a/spec/unit/matchers/link_to_matcher_spec.rb b/spec/unit/matchers/link_to_matcher_spec.rb
new file mode 100644
index 0000000..27f5b40
--- /dev/null
+++ b/spec/unit/matchers/link_to_matcher_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe ChefSpec::Matchers::LinkToMatcher do
+  let(:from) { '/var/www' }
+  let(:to)   { '/var/html' }
+  let(:link) do
+    Chef::Resource::Link.new(from).tap do |link|
+      link.to(to)
+      link.perform_action(:create)
+    end
+  end
+  subject { described_class.new(to) }
+
+  describe '#failure_message' do
+    it 'has the right value' do
+      subject.matches?(link)
+      expect(subject.failure_message)
+        .to eq(%Q(expected "link[#{from}]" to link to "#{to}" but was "#{to}"))
+    end
+  end
+
+  describe '#failure_message_when_negated' do
+    it 'has the right value' do
+      subject.matches?(link)
+      expect(subject.failure_message_when_negated)
+        .to eq(%Q(expected "link[#{from}]" to not link to "#{to}"))
+    end
+  end
+
+  describe '#description' do
+    it 'has the right value' do
+      subject.matches?(link)
+      expect(subject.description).to eq(%Q(link to "#{to}"))
+    end
+  end
+
+  context 'when the link is correct' do
+    it 'matches' do
+      expect(subject.matches?(link)).to be_truthy
+    end
+
+    it 'adds the link to the coverage report' do
+      expect(ChefSpec::Coverage).to receive(:cover!).with(link)
+      subject.matches?(link)
+    end
+  end
+
+  context 'when the link is not correct' do
+    subject { described_class.new('/nope/bad/path/bro') }
+
+    it 'does not match' do
+      expect(subject.matches?(link)).to be_falsy
+    end
+  end
+end
diff --git a/spec/unit/matchers/notifications_matcher_spec.rb b/spec/unit/matchers/notifications_matcher_spec.rb
new file mode 100644
index 0000000..95a2a33
--- /dev/null
+++ b/spec/unit/matchers/notifications_matcher_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe ChefSpec::Matchers::NotificationsMatcher do
+  subject { described_class.new('execute[install]') }
+  let(:package) do
+    double('package',
+      name: 'package',
+      to_s: 'package[foo]',
+      is_a?: true,
+      performed_action?: true,
+      immediate_notifications: [],
+      delayed_notifications: [],
+    )
+  end
+
+  describe '#failure_message' do
+    it 'has the right value' do
+      subject.matches?(package)
+      expect(subject.failure_message)
+        .to include %|expected "package[foo]" to notify "execute[install]", but did not.|
+    end
+  end
+
+  describe '#failure_message_when_negated' do
+    it 'has the right value' do
+      subject.matches?(package)
+      expect(subject.failure_message_when_negated)
+        .to eq %|expected "package[foo]" to not notify "execute[install]", but it did.|
+    end
+  end
+
+  describe '#description' do
+    it 'has the right value' do
+      subject.matches?(package)
+      expect(subject.description)
+        .to eq %|notify "execute[install]"|
+    end
+  end
+end
diff --git a/spec/unit/matchers/render_file_matcher_spec.rb b/spec/unit/matchers/render_file_matcher_spec.rb
new file mode 100644
index 0000000..464a49b
--- /dev/null
+++ b/spec/unit/matchers/render_file_matcher_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe ChefSpec::Matchers::RenderFileMatcher do
+  let(:path) { '/tmp/thing' }
+  let(:file) { double('file', to: path, to_s: "file[#{path}]", performed_action?: true) }
+  let(:chef_run) { double('chef run', find_resource: file) }
+  subject { described_class.new(path) }
+
+  describe '#failure_message' do
+    it 'has the right value' do
+      subject.matches?(chef_run)
+      expect(subject.failure_message)
+        .to eq(%Q(expected Chef run to render "#{path}"))
+    end
+  end
+
+  describe '#failure_message_when_negated' do
+    it 'has the right value' do
+      subject.matches?(chef_run)
+      expect(subject.failure_message_when_negated)
+        .to eq(%Q(expected file "#{path}" to not be in Chef run))
+    end
+  end
+
+  describe '#description' do
+    it 'has the right value' do
+      subject.matches?(chef_run)
+      expect(subject.description).to eq(%Q(render file "#{path}"))
+    end
+  end
+
+  context 'when the file is correct' do
+    it 'matches' do
+      expect(subject.matches?(chef_run)).to be_truthy
+    end
+
+    it 'adds the resource to the coverage report' do
+      expect(ChefSpec::Coverage).to receive(:cover!).with(file)
+      subject.matches?(chef_run)
+    end
+  end
+
+  context 'when the file is not correct' do
+    it 'does not match' do
+      allow(chef_run).to receive(:find_resource).and_return(nil)
+      failure = described_class.new('nope')
+      expect(failure.matches?(chef_run)).to be_falsy
+    end
+  end
+end
diff --git a/spec/unit/matchers/resource_matcher_spec.rb b/spec/unit/matchers/resource_matcher_spec.rb
new file mode 100644
index 0000000..8c16d16
--- /dev/null
+++ b/spec/unit/matchers/resource_matcher_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe ChefSpec::Matchers::ResourceMatcher do
+  pending
+end
diff --git a/spec/unit/matchers/state_attrs_matcher_spec.rb b/spec/unit/matchers/state_attrs_matcher_spec.rb
new file mode 100644
index 0000000..5442981
--- /dev/null
+++ b/spec/unit/matchers/state_attrs_matcher_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe ChefSpec::Matchers::StateAttrsMatcher do
+  subject { described_class.new([:a, :b]) }
+
+  context 'when the resource does not exist' do
+    let(:resource) { nil }
+    before { subject.matches?(resource) }
+
+    it 'does not match' do
+      expect(subject).to_not be_matches(resource)
+    end
+
+    it 'has the correct description' do
+      expect(subject.description).to eq('have state attributes [:a, :b]')
+    end
+
+    it 'has the correct failure message for should' do
+      expect(subject.failure_message).to include <<-EOH.gsub(/^ {8}/, '')
+        expected _something_ to have state attributes, but the _something_ you gave me was nil!
+        Ensure the resource exists before making assertions:
+
+          expect(resource).to be
+      EOH
+    end
+
+    it 'has the correct failure message for should not' do
+      expect(subject.failure_message_when_negated).to include <<-EOH.gsub(/^ {8}/, '')
+        expected _something_ to not have state attributes, but the _something_ you gave me was nil!
+        Ensure the resource exists before making assertions:
+
+          expect(resource).to be
+      EOH
+    end
+  end
+
+  context 'when the resource exists' do
+    let(:klass) { double('class', state_attrs: [:a, :b]) }
+    let(:resource) { double('resource', class: klass) }
+    before { subject.matches?(resource) }
+
+    it 'has the correct description' do
+      expect(subject.description).to eq('have state attributes [:a, :b]')
+    end
+
+    it 'has the correct failure message for should' do
+      expect(subject.failure_message).to eq('expected [:a, :b] to equal [:a, :b]')
+    end
+
+    it 'has the correct failure message for should not' do
+      expect(subject.failure_message_when_negated).to eq('expected [:a, :b] to not equal [:a, :b]')
+    end
+
+    it 'matches when the state attributes are correct' do
+      expect(subject).to be_matches(resource)
+    end
+
+    it 'does not match when the state attributes are incorrect' do
+      allow(klass).to receive(:state_attrs).and_return([:c, :d])
+      expect(subject).to_not be_matches(resource)
+    end
+
+    it 'does not match when partial state attribute are incorrect' do
+      allow(klass).to receive(:state_attrs).and_return([:b, :c])
+      expect(subject).to_not be_matches(resource)
+    end
+  end
+end
diff --git a/spec/unit/matchers/subscribes_matcher_spec.rb b/spec/unit/matchers/subscribes_matcher_spec.rb
new file mode 100644
index 0000000..b0319b0
--- /dev/null
+++ b/spec/unit/matchers/subscribes_matcher_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe ChefSpec::Matchers::SubscribesMatcher do
+  subject { described_class.new('execute[install]') }
+  let(:runner) { double('runner', find_resource: nil) }
+  let(:run_context) { double('run_context', node: node) }
+  let(:node) { double('node', runner: runner) }
+  let(:package) do
+    double('package',
+      name: 'package',
+      to_s: 'package[foo]',
+      run_context: run_context,
+    )
+  end
+
+  context 'when no resource is found' do
+    describe '#failure_message' do
+      it 'has the right value' do
+        subject.matches?(package)
+        expect(subject.failure_message)
+          .to include %|expected _something_ to notify "package[foo]", but the _something_ you gave me was nil! If you are running a test like:|
+      end
+    end
+  end
+
+  context 'when the resource exists' do
+    let(:execute) do
+      double('execute',
+        name: 'execute',
+        to_s: 'execute[install]',
+        immediate_notifications: [],
+        delayed_notifications: [],
+      )
+    end
+
+    before do
+      allow(runner).to receive(:find_resource).and_return(execute)
+    end
+
+    describe '#failure_message' do
+      it 'has the right value' do
+        subject.matches?(package)
+        expect(subject.failure_message)
+          .to include %|expected "execute[install]" to notify "package[foo]", but did not.|
+      end
+    end
+
+    describe '#failure_message_when_negated' do
+      it 'has the right value' do
+        subject.matches?(package)
+        expect(subject.failure_message_when_negated)
+          .to eq %|expected "execute[install]" to not notify "package[foo]", but it did.|
+      end
+    end
+  end
+
+  describe '#description' do
+    it 'has the right value' do
+      subject.matches?(package)
+      expect(subject.description)
+        .to eq %|notify "package[foo]"|
+    end
+  end
+end
diff --git a/spec/unit/renderer_spec.rb b/spec/unit/renderer_spec.rb
new file mode 100644
index 0000000..6430c5a
--- /dev/null
+++ b/spec/unit/renderer_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe ChefSpec::Renderer do
+  describe '.initialize' do
+    it 'accepts two arguments and assigns their instance variables' do
+      instance = described_class.new('runner', 'resource')
+      expect(instance.chef_run).to eq('runner')
+      expect(instance.resource).to eq('resource')
+    end
+  end
+
+  let(:chef_run) { double('chef_run', {node: 'node'}) }
+  let(:resource) { double('resource', {cookbook: 'cookbook', source: 'source', variables: {}}) }
+  subject { described_class.new(chef_run, resource) }
+
+  describe '#content' do
+    before do
+      allow(subject).to receive(:content_from_cookbook_file).and_return('cookbook_file content')
+      allow(subject).to receive(:content_from_file).and_return('file content')
+      allow(subject).to receive(:content_from_template).and_return('template content')
+    end
+
+    context 'when the resource is a cookbook_file' do
+      it 'renders the cookbook_file content' do
+        allow(resource).to receive(:resource_name).and_return('cookbook_file')
+        expect(subject.content).to eq('cookbook_file content')
+      end
+    end
+
+    context 'when the resource is a file' do
+      it 'renders the file content' do
+        allow(resource).to receive(:resource_name).and_return('file')
+        expect(subject.content).to eq('file content')
+      end
+    end
+
+    context 'when the resource is a template' do
+      it 'renders the template content' do
+        allow(resource).to receive(:resource_name).and_return('template')
+        expect(subject.content).to eq('template content')
+      end
+    end
+
+    context 'when the resource is not a file type' do
+      it 'returns nil' do
+        allow(resource).to receive(:resource_name).and_return('service')
+        expect(subject.content).to be_nil
+      end
+    end
+  end
+
+  describe 'content_from_template' do
+    it 'renders the template by extending modules for rendering paritals within the template' do
+      cookbook_collection = {}
+      cookbook_collection['cookbook'] = double('', {preferred_filename_on_disk_location: "/template/location"} )
+      allow(subject).to receive(:cookbook_collection).with('node').and_return(cookbook_collection)
+      allow(subject).to receive(:template_finder)
+      
+      allow(resource).to receive(:helper_modules).and_return([Module.new])
+      allow(resource).to receive(:resource_name).and_return('template')
+
+      chef_template_context = double('context', {render_template: 'rendered template content',update: nil})
+      allow(Chef::Mixin::Template::TemplateContext).to receive(:new).and_return(chef_template_context)
+      
+      expect(chef_template_context).to receive(:_extend_modules).with(resource.helper_modules)
+      expect(subject.content).to eq('rendered template content')
+    end
+  end
+end
diff --git a/spec/unit/solo_runner_spec.rb b/spec/unit/solo_runner_spec.rb
new file mode 100644
index 0000000..a3290a5
--- /dev/null
+++ b/spec/unit/solo_runner_spec.rb
@@ -0,0 +1,170 @@
+require 'spec_helper'
+
+describe ChefSpec::SoloRunner do
+  before do
+    allow_any_instance_of(ChefSpec::SoloRunner)
+      .to receive(:dry_run?)
+      .and_return(true)
+  end
+
+  describe '#initialize' do
+    let(:windows_caller_stack) do
+      [
+        "C:/cookbooks/Temp/spec/test_spec.rb:11:in `block (2 levels) in <top (required)>'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:114:in `instance_eval'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:114:in `block in run'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:254:in `with_around_each_hooks'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:111:in `run'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:390:in `block in run_examples'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:386:in `map'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:386:in `run_examples'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:371:in `run'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/command_line.rb:28:in `block (2 levels) in run'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/command_line.rb:28:in `map'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/command_line.rb:28:in `block in run'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/reporter.rb:58:in `report'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/command_line.rb:25:in `run'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/runner.rb:80:in `run'",
+        "C:/Ruby193/lib/ruby/gems/1.9.1/gems/rspec-core-2.14.8/lib/rspec/core/runner.rb:17:in `block in autorun'",
+      ]
+    end
+
+    it 'defaults the log level to :warn' do
+      described_class.new
+      expect(Chef::Log.level).to eq(:warn)
+    end
+
+    it 'sets the log level' do
+      described_class.new(log_level: :error)
+      expect(Chef::Log.level).to eq(:error)
+    end
+
+    it 'defaults the cookbook_path to the calling spec' do
+      described_class.new
+      expect(Chef::Config.cookbook_path).to eq([File.expand_path('../../../..', __FILE__)])
+    end
+
+    it 'defaults the cookbook_path to the calling spec when using windows paths' do
+      runner = described_class.new
+      windows_path = runner.instance_exec(windows_caller_stack) { |callstack|
+        calling_cookbook_path(callstack)
+      }
+      # There's got to be a better way to do this the File.expand_path returns
+      # something like /home/user/repos/chefspec/C:/cookbooks" which is less
+      # than ideal as a robust test
+      expect(windows_path).to end_with("C:/cookbooks")
+    end
+
+    it 'sets the cookbook path' do
+      described_class.new(cookbook_path: '/tmp/bacon')
+      expect(Chef::Config.cookbook_path).to eq(['/tmp/bacon'])
+    end
+
+    it 'sets the file cache path' do
+      described_class.new( file_cache_path: '/tmp/pantoa')
+      expect(Chef::Config.file_cache_path).to eq('/tmp/pantoa')
+    end
+
+    it 'sets the Chef::Config' do
+      expect(Chef::Config.cache_type).to eq('Memory')
+      expect(Chef::Config.force_logger).to be_truthy
+      expect(Chef::Config.no_lazy_load).to be_truthy
+      expect(Chef::Config.solo).to be_truthy
+    end
+
+    it 'yields a block to set node attributes' do
+      expect { |block| described_class.new({}, &block) }.to yield_with_args(Chef::Node)
+    end
+
+    context 'default ohai attributes' do
+      let(:hash) { described_class.new.node.to_hash }
+
+      it 'sets the default attributes' do
+        expect(hash['os']).to eq('chefspec')
+        expect(hash['languages']['ruby']['bin_dir']).to eq('/usr/local/bin')
+        expect(hash['os_version']).to eq('0.6.1')
+        expect(hash['fqdn']).to eq('chefspec.local')
+        expect(hash['domain']).to eq('local')
+        expect(hash['ipaddress']).to eq('127.0.0.1')
+        expect(hash['hostname']).to eq('chefspec')
+        expect(hash['kernel']['machine']).to eq('i386')
+      end
+    end
+
+    context 'fauxhai attributes' do
+      let(:hash) { described_class.new(platform: 'ubuntu', version: '12.04').node.to_hash }
+
+      it 'sets the attributes from fauxhai' do
+        expect(hash['os']).to eq('linux')
+        expect(hash['languages']['ruby']['ruby_bin']).to eq('/usr/local/bin/ruby')
+        expect(hash['os_version']).to eq('3.2.0-26-generic')
+        expect(hash['fqdn']).to eq('fauxhai.local')
+        expect(hash['domain']).to eq('local')
+        expect(hash['ipaddress']).to eq('10.0.0.2')
+        expect(hash['hostname']).to eq('Fauxhai')
+        expect(hash['kernel']['machine']).to eq('x86_64')
+      end
+    end
+
+    context 'RSpec global configuration' do
+      before do
+        allow(RSpec.configuration).to receive(:cookbook_path).and_return('./path')
+        allow(RSpec.configuration).to receive(:environment_path).and_return('./env-path')
+        allow(RSpec.configuration).to receive(:file_cache_path).and_return('./file-cache-path')
+        allow(RSpec.configuration).to receive(:log_level).and_return(:fatal)
+        allow(RSpec.configuration).to receive(:path).and_return('ohai.json')
+        allow(RSpec.configuration).to receive(:platform).and_return('ubuntu')
+        allow(RSpec.configuration).to receive(:version).and_return('12.04')
+      end
+
+      it 'uses the RSpec values' do
+        options = described_class.new.options
+        expect(options[:cookbook_path]).to eq('./path')
+        expect(options[:environment_path]).to eq('./env-path')
+        expect(options[:file_cache_path]).to eq('./file-cache-path')
+        expect(options[:log_level]).to eq(:fatal)
+        expect(options[:path]).to eq('ohai.json')
+        expect(options[:platform]).to eq('ubuntu')
+        expect(options[:version]).to eq('12.04')
+      end
+    end
+  end
+
+  describe '#node' do
+    it 'returns the Chef::Node' do
+      expect(subject.node).to be_a(Chef::Node)
+    end
+
+    it 'defines a #runner method that returns self' do
+      expect(subject.node.methods).to include(:runner)
+      expect(subject.node.runner).to be(subject)
+    end
+
+    it 'allows attributes to be set on the node' do
+      subject.node.set['bacon'] = 'ham'
+      expect(subject.node.bacon).to eq('ham')
+    end
+  end
+
+  describe '#to_s' do
+    it 'overrides the default string representation to something readable' do
+      expect(subject.converge('apache2::default').to_s)
+        .to eq('#<ChefSpec::SoloRunner run_list: [recipe[apache2::default]]>')
+    end
+
+    it 'is ok when a convergence has not yet taken place' do
+      expect(subject.to_s).to eq('#<ChefSpec::SoloRunner run_list: []>')
+    end
+
+    it 'includes the entire run_list' do
+      expect(subject.converge('apache2::default', 'apache2::mod_ssl').to_s)
+        .to eq('#<ChefSpec::SoloRunner run_list: [recipe[apache2::default], recipe[apache2::mod_ssl]]>')
+    end
+
+    it 'has the run_list only for the last convergence' do
+      ['mysql::client', 'mysql::server'].each { |recipe| subject.converge(recipe) }
+      expect(subject.to_s)
+        .to eq('#<ChefSpec::SoloRunner run_list: [recipe[mysql::server]]>')
+    end
+  end
+end
diff --git a/spec/unit/stubs/command_registry_spec.rb b/spec/unit/stubs/command_registry_spec.rb
new file mode 100644
index 0000000..92c3bc8
--- /dev/null
+++ b/spec/unit/stubs/command_registry_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::CommandRegistry do
+  before { described_class.reset! }
+
+  it 'inherits from Registry' do
+    expect(described_class.superclass).to be(ChefSpec::Stubs::Registry)
+  end
+
+  describe '#stub_for' do
+    it 'finds a stub by name' do
+      command = double('command', command: 'test -f /var/www')
+      described_class.register(command)
+      expect(described_class.stub_for('test -f /var/www')).to eq(command)
+    end
+
+    it 'finds a stub by regex' do
+      command = double('command', command: /test -f (.+)/)
+      described_class.register(command)
+      expect(described_class.stub_for('test -f /var/www')).to eq(command)
+    end
+
+    it 'returns nil when no commands are matched' do
+      expect(described_class.stub_for('which a2ensite')).to eq(nil)
+    end
+  end
+end
diff --git a/spec/unit/stubs/command_stub_spec.rb b/spec/unit/stubs/command_stub_spec.rb
new file mode 100644
index 0000000..5474fdb
--- /dev/null
+++ b/spec/unit/stubs/command_stub_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::CommandStub do
+  describe '#initialize' do
+    it 'sets the command and block' do
+      block = Proc.new {}
+      stub  = described_class.new('command', &block)
+
+      expect(stub.command).to eq('command')
+      expect(stub.block).to eq(block)
+    end
+  end
+
+  describe '#and_return' do
+    subject { described_class.new('command').and_return('value') }
+
+    it 'sets the value' do
+      expect(subject.value).to eq('value')
+    end
+
+    it 'returns an instance of the stub' do
+      expect(subject.and_return('value')).to be(subject)
+    end
+  end
+
+  describe '#result' do
+    context 'when a value is given' do
+      subject { described_class.new('command').and_return('value') }
+
+      it 'returns the value' do
+        expect(subject.result).to eq('value')
+      end
+    end
+
+    context 'when a block is given' do
+      subject { described_class.new('command') { 1 == 2 } }
+
+      it 'calls the block' do
+        expect(subject.result).to eq(false)
+      end
+    end
+  end
+
+  describe '#signature' do
+    context 'when a value is given' do
+      subject { described_class.new('command').and_return(false) }
+
+      it 'includes the value' do
+        expect(subject.signature).to eq('stub_command("command").and_return(false)')
+      end
+    end
+
+    context 'when a block is given' do
+      subject { described_class.new('command') { 1 == 2 } }
+
+      it 'includes a comment about the block' do
+        expect(subject.signature).to eq('stub_command("command") { # Ruby code }')
+      end
+    end
+  end
+end
diff --git a/spec/unit/stubs/data_bag_item_registry_spec.rb b/spec/unit/stubs/data_bag_item_registry_spec.rb
new file mode 100644
index 0000000..2497b75
--- /dev/null
+++ b/spec/unit/stubs/data_bag_item_registry_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::DataBagItemRegistry do
+  before { described_class.reset! }
+
+  it 'inherits from Registry' do
+    expect(described_class.superclass).to be(ChefSpec::Stubs::Registry)
+  end
+
+  describe '#stub_for' do
+    it 'finds a stub by name' do
+      search = double('search', bag: 'users', id: 'svargo')
+      described_class.register(search)
+      expect(described_class.stub_for('users', 'svargo')).to eq(search)
+    end
+
+    it 'finds a stub by Regex' do
+      search = double('search', bag: 'users', id: /sv(.+)/)
+      described_class.register(search)
+      expect(described_class.stub_for('users', 'svargo')).to eq(search)
+    end
+
+    it 'matches when the bag is a Symbol' do
+      search = double('search', bag: :users, id: 'svargo')
+      described_class.register(search)
+      expect(described_class.stub_for('users', 'svargo')).to eq(search)
+    end
+
+    it 'matches when the stub is a Symbol' do
+      search = double('search', bag: 'users', id: 'svargo')
+      described_class.register(search)
+      expect(described_class.stub_for(:users, 'svargo')).to eq(search)
+    end
+
+    it 'returns nil when no searches are matched' do
+      expect(described_class.stub_for('users', 'svargo')).to eq(nil)
+    end
+  end
+end
diff --git a/spec/unit/stubs/data_bag_item_stub_spec.rb b/spec/unit/stubs/data_bag_item_stub_spec.rb
new file mode 100644
index 0000000..7965d16
--- /dev/null
+++ b/spec/unit/stubs/data_bag_item_stub_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::DataBagItemStub do
+  it 'inherts from Stub' do
+    expect(described_class.superclass).to be(ChefSpec::Stubs::Stub)
+  end
+
+  describe '#initialize' do
+    it 'sets the bag, id, and block' do
+      block = Proc.new {}
+      stub  = described_class.new('bag', 'id', &block)
+
+      expect(stub.bag).to eq('bag')
+      expect(stub.id).to eq('id')
+      expect(stub.block).to eq(block)
+    end
+  end
+
+  describe '#signature' do
+    context 'when a value is given' do
+      subject { described_class.new('bag', 'id').and_return(false) }
+
+      it 'includes the value' do
+        expect(subject.signature).to eq('stub_data_bag_item("bag", "id").and_return(false)')
+      end
+    end
+
+    context 'when a block is given' do
+      subject { described_class.new('bag', 'id') { 1 == 2 } }
+
+      it 'includes a comment about the block' do
+        expect(subject.signature).to eq('stub_data_bag_item("bag", "id") { # Ruby code }')
+      end
+    end
+  end
+end
diff --git a/spec/unit/stubs/data_bag_registry_spec.rb b/spec/unit/stubs/data_bag_registry_spec.rb
new file mode 100644
index 0000000..5bb7aa3
--- /dev/null
+++ b/spec/unit/stubs/data_bag_registry_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::DataBagRegistry do
+  before { described_class.reset! }
+
+  it 'inherits from Registry' do
+    expect(described_class.superclass).to be(ChefSpec::Stubs::Registry)
+  end
+
+  describe '#stub_for' do
+    it 'finds a stub by name' do
+      search = double('search', bag: 'users')
+      described_class.register(search)
+      expect(described_class.stub_for('users')).to eq(search)
+    end
+
+    it 'finds a stub by Regex' do
+      search = double('search', bag: 'users')
+      described_class.register(search)
+      expect(described_class.stub_for('users')).to eq(search)
+    end
+
+    it 'matches when the bag is Symbol' do
+      search = double('search', bag: :users)
+      described_class.register(search)
+      expect(described_class.stub_for('users')).to eq(search)
+    end
+
+    it 'matches when the stub is Symbol' do
+      search = double('search', bag: 'users')
+      described_class.register(search)
+      expect(described_class.stub_for(:users)).to eq(search)
+    end
+
+    it 'returns nil when no searches are matched' do
+      expect(described_class.stub_for('users')).to eq(nil)
+    end
+  end
+end
diff --git a/spec/unit/stubs/data_bag_stub_spec.rb b/spec/unit/stubs/data_bag_stub_spec.rb
new file mode 100644
index 0000000..8829ad7
--- /dev/null
+++ b/spec/unit/stubs/data_bag_stub_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::DataBagStub do
+  it 'inherts from Stub' do
+    expect(described_class.superclass).to be(ChefSpec::Stubs::Stub)
+  end
+
+  describe '#initialize' do
+    it 'sets the bag and block' do
+      block = Proc.new {}
+      stub  = described_class.new('bag', &block)
+
+      expect(stub.bag).to eq('bag')
+      expect(stub.block).to eq(block)
+    end
+  end
+
+  describe '#signature' do
+    context 'when a value is given' do
+      subject { described_class.new('bag').and_return(false) }
+
+      it 'includes the value' do
+        expect(subject.signature).to eq('stub_data_bag("bag").and_return(false)')
+      end
+    end
+
+    context 'when a block is given' do
+      subject { described_class.new('bag') { 1 == 2 } }
+
+      it 'includes a comment about the block' do
+        expect(subject.signature).to eq('stub_data_bag("bag") { # Ruby code }')
+      end
+    end
+  end
+end
diff --git a/spec/unit/stubs/registry_spec.rb b/spec/unit/stubs/registry_spec.rb
new file mode 100644
index 0000000..f928502
--- /dev/null
+++ b/spec/unit/stubs/registry_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::Registry do
+  before { described_class.reset! }
+
+  describe '#reset!' do
+    it 'cleans the list of stubs' do
+      described_class.stubs = [1, 2, 3]
+      described_class.reset!
+
+      expect(described_class.stubs).to be_empty
+    end
+  end
+
+  describe '#register' do
+    it 'adds the stub to the registry' do
+      described_class.register('bacon')
+      expect(described_class.stubs).to include('bacon')
+    end
+  end
+
+  describe '#stub_for' do
+    it 'finds a stub by name' do
+      expect {
+        described_class.stub_for
+      }.to raise_error(ArgumentError)
+    end
+  end
+end
diff --git a/spec/unit/stubs/search_registry_spec.rb b/spec/unit/stubs/search_registry_spec.rb
new file mode 100644
index 0000000..a44faab
--- /dev/null
+++ b/spec/unit/stubs/search_registry_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::SearchRegistry do
+  before { described_class.reset! }
+
+  it 'inherits from Registry' do
+    expect(described_class.superclass).to be(ChefSpec::Stubs::Registry)
+  end
+
+  describe '#stub_for' do
+    it 'finds a stub by name' do
+      search = double('search', type: 'node', query: '*:*')
+      described_class.register(search)
+      expect(described_class.stub_for('node', '*:*')).to eq(search)
+    end
+
+    it 'finds a stub by Regex' do
+      search = double('search', type: 'node', query: /name:(.+)/)
+      described_class.register(search)
+      expect(described_class.stub_for('node', 'name:example.com')).to eq(search)
+    end
+
+    it 'matches when the type is Symbol' do
+      search = double('search', type: :node, query: '*:*')
+      described_class.register(search)
+      expect(described_class.stub_for('node', '*:*')).to eq(search)
+    end
+
+    it 'matches when the stub is Symbol' do
+      search = double('search', type: 'node', query: '*:*')
+      described_class.register(search)
+      expect(described_class.stub_for(:node, '*:*')).to eq(search)
+    end
+
+    it 'returns nil when no searches are matched' do
+      expect(described_class.stub_for('node', 'name:example.com')).to eq(nil)
+    end
+  end
+end
diff --git a/spec/unit/stubs/search_stub_spec.rb b/spec/unit/stubs/search_stub_spec.rb
new file mode 100644
index 0000000..4b9f95f
--- /dev/null
+++ b/spec/unit/stubs/search_stub_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::SearchStub do
+  it 'inherts from Stub' do
+    expect(described_class.superclass).to be(ChefSpec::Stubs::Stub)
+  end
+
+  describe '#initialize' do
+    it 'sets the type, query, and block' do
+      block = Proc.new {}
+      stub  = described_class.new('type', 'query', &block)
+
+      expect(stub.type).to eq('type')
+      expect(stub.query).to eq('query')
+      expect(stub.block).to eq(block)
+    end
+  end
+
+  describe '#signature' do
+    context 'when a value is given' do
+      subject { described_class.new('type', 'query').and_return(false) }
+
+      it 'includes the value' do
+        expect(subject.signature).to eq('stub_search("type", "query").and_return(false)')
+      end
+    end
+
+    context 'when a block is given' do
+      subject { described_class.new('type', 'query') { 1 == 2 } }
+
+      it 'includes a comment about the block' do
+        expect(subject.signature).to eq('stub_search("type", "query") { # Ruby code }')
+      end
+    end
+  end
+end
diff --git a/spec/unit/stubs/stub_spec.rb b/spec/unit/stubs/stub_spec.rb
new file mode 100644
index 0000000..ac8eb5b
--- /dev/null
+++ b/spec/unit/stubs/stub_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe ChefSpec::Stubs::Stub do
+  describe '#and_return' do
+    subject { described_class.new.and_return('value') }
+
+    it 'sets the value' do
+      expect(subject.value).to eq('value')
+    end
+
+    it 'returns an instance of the stub' do
+      expect(subject.and_return('value')).to be(subject)
+    end
+  end
+
+  describe '#and_raise' do
+    subject { described_class.new.and_raise(ArgumentError) }
+
+    it 'sets the block' do
+      expect(subject.instance_variable_get(:@block)).to be_a(Proc)
+    end
+
+    it 'returns an instance of the stub' do
+      expect(subject.and_raise(ArgumentError)).to be(subject)
+    end
+  end
+
+  describe '#result' do
+    context 'when a value is given' do
+      subject { described_class.new.and_return('value') }
+
+      it 'returns the value' do
+        expect(subject.result).to eq('value')
+      end
+    end
+
+    context 'when a block is given' do
+      subject { described_class.new }
+
+      it 'calls the block' do
+        subject.instance_variable_set(:@block, Proc.new { 1 == 2 })
+        expect(subject.result).to eq(false)
+      end
+    end
+
+    context 'when an exception block is given' do
+      subject { described_class.new.and_raise(ArgumentError) }
+
+      it 'raises the exception' do
+        expect {
+          subject.result
+        }.to raise_error(ArgumentError)
+      end
+    end
+
+    context 'when the value is a Hash' do
+      subject { described_class.new.and_return([ { name: 'a' } ]) }
+
+      it 'recursively mashifies the value' do
+        expect(subject.result.first).to be_a(Mash)
+      end
+    end
+  end
+end
diff --git a/templates/coverage/human.erb b/templates/coverage/human.erb
new file mode 100644
index 0000000..3f4e187
--- /dev/null
+++ b/templates/coverage/human.erb
@@ -0,0 +1,22 @@
+
+<% if @total == 0 %>
+  No Chef resources found, skipping coverage calculation...
+<% else %>
+ChefSpec Coverage report generated...
+
+  Total Resources:   <%= @total %>
+  Touched Resources: <%= @touched %>
+  Touch Coverage:    <%= @coverage %>%
+
+<% if @untouched_resources.empty? %>
+You are awesome and so is your test coverage! Have a fantastic day!
+
+<% else %>
+Untouched Resources:
+
+<% @untouched_resources.each do |resource| %>
+  <%= resource.to_s.ljust(32) %>   <%= resource.source_file %>:<%= resource.source_line %>
+<% end %>
+
+<% end %>
+<% end %>
diff --git a/templates/errors/cookbook_path_not_found.erb b/templates/errors/cookbook_path_not_found.erb
new file mode 100644
index 0000000..043142e
--- /dev/null
+++ b/templates/errors/cookbook_path_not_found.erb
@@ -0,0 +1,3 @@
+I could not find or infer a cookbook_path from your current working directory.
+Please make sure you put your specs (tests) under a directory named 'spec' or
+manually set the cookbook path in the RSpec configuration.
diff --git a/templates/errors/gem_load_error.erb b/templates/errors/gem_load_error.erb
new file mode 100644
index 0000000..903568f
--- /dev/null
+++ b/templates/errors/gem_load_error.erb
@@ -0,0 +1,7 @@
+I could not load the '<%= @name %>' gem! You must have the gem installed
+on your local system before you can use the <%= @gem %> plugin.
+You can install <%= @gem %> by running:
+
+    gem install <%= @gem %>
+
+or add <%= @name %> to your Gemfile and run the `bundle` command to install.
diff --git a/templates/errors/no_conversion_error.erb b/templates/errors/no_conversion_error.erb
new file mode 100644
index 0000000..465f3ca
--- /dev/null
+++ b/templates/errors/no_conversion_error.erb
@@ -0,0 +1 @@
+You called a deprecated method without a backwards compatible replacement.
diff --git a/templates/errors/not_stubbed.erb b/templates/errors/not_stubbed.erb
new file mode 100644
index 0000000..3a480a0
--- /dev/null
+++ b/templates/errors/not_stubbed.erb
@@ -0,0 +1,7 @@
+Executing a real <%= @type %> is disabled. Unregistered <%= @type %>:
+
+    <%= @signature %>
+
+You can stub this <%= @type %> with:
+
+    <%= @stub %>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-chefspec.git



More information about the Pkg-ruby-extras-commits mailing list