{"id":13521368,"url":"https://github.com/chefspec/chefspec","last_synced_at":"2025-12-16T14:59:09.572Z","repository":{"id":418960,"uuid":"2132207","full_name":"chefspec/chefspec","owner":"chefspec","description":"Write RSpec examples and generate coverage reports for Chef recipes!","archived":false,"fork":false,"pushed_at":"2024-07-16T02:55:23.000Z","size":2438,"stargazers_count":823,"open_issues_count":38,"forks_count":335,"subscribers_count":32,"default_branch":"main","last_synced_at":"2025-12-03T06:47:03.545Z","etag":null,"topics":["chef","chefspec","rspec","testing"],"latest_commit_sha":null,"homepage":"https://chefspec.github.io/chefspec/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chefspec.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2011-07-31T12:22:20.000Z","updated_at":"2025-09-12T22:26:48.000Z","dependencies_parsed_at":"2024-09-20T05:40:47.721Z","dependency_job_id":null,"html_url":"https://github.com/chefspec/chefspec","commit_stats":{"total_commits":1030,"total_committers":150,"mean_commits":6.866666666666666,"dds":0.6757281553398058,"last_synced_commit":"df9ca04f989d7cc98f66293fd944147217501513"},"previous_names":[],"tags_count":78,"template":false,"template_full_name":null,"purl":"pkg:github/chefspec/chefspec","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chefspec%2Fchefspec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chefspec%2Fchefspec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chefspec%2Fchefspec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chefspec%2Fchefspec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chefspec","download_url":"https://codeload.github.com/chefspec/chefspec/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chefspec%2Fchefspec/sbom","scorecard":{"id":275511,"data":{"date":"2025-08-11","repo":{"name":"github.com/chefspec/chefspec","commit":"df9ca04f989d7cc98f66293fd944147217501513"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":3,"reason":"Found 5/16 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/chefspec/chefspec/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/chefspec/chefspec/ci.yml/main?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 23 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T14:17:59.589Z","repository_id":418960,"created_at":"2025-08-17T14:17:59.589Z","updated_at":"2025-08-17T14:17:59.589Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27766746,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-16T02:00:10.477Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chef","chefspec","rspec","testing"],"created_at":"2024-08-01T06:00:33.456Z","updated_at":"2025-12-16T14:59:09.556Z","avatar_url":"https://github.com/chefspec.png","language":"Ruby","readme":"# ChefSpec\n\n[![Gem Version](https://badge.fury.io/rb/chefspec.svg)](https://badge.fury.io/rb/chefspec)\n[![CI](https://github.com/chefspec/chefspec/actions/workflows/ci.yml/badge.svg)](https://github.com/chefspec/chefspec/actions/workflows/ci.yml)\n\nChefSpec 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.\n\nChefSpec runs your cookbooks locally while skipping making actual changes. This has two primary benefits:\n\n- It's really fast!\n- Your tests can vary node attributes, operating systems, and other system data to assert behavior under varying conditions.\n\n## Important Notes\n\n- **ChefSpec requires Ruby 2.5 or later and Chef 15 or later!**\n- **This documentation corresponds to the main branch, which may be unreleased. Please check the README of the latest git tag or the gem's source for your version's documentation!**\n\n**ChefSpec aims to maintain compatibility with at least the two most recent minor versions of Chef.** If you are running an older version of Chef it may work, or you will need to run an older version of ChefSpec.\n\nAs a general rule, if it is tested in the Travis CI matrix, it is a supported version.\n\n## Quick Start\n\n## When To Use ChefSpec?\n\nAs mentioned before, ChefSpec is built for speed. In order to run your tests as\nquickly as possible (and to allow running tests on your workstation), ChefSpec\nruns your recipe code with all the resource actions disabled. This means that\nChefSpec excels at testing complex logic in a cookbook, but can't actually tell\nyou if a cookbook is doing the right thing. Integration testing is provided by\nthe [Test Kitchen](https://kitchen.ci/) project, and for most simple cookbooks\nwithout much logic in them we recommend you start off with integration tests and\nonly return to ChefSpec and unit tests as your code gets more complicated.\n\nThere are two common \"units\" of code in Chef cookbooks, custom resources and\nrecipes. If you find yourself with a lot of recipes that are so complex they\nrequire unit tests, consider if they can be refactored as custom resources.\n\n### Testing a Custom Resource\n\nIf you have have a cookbook with a custom resource `resources/greet.rb` like:\n\n```ruby\nresource_name :mycookbook_greet\n\nproperty :greeting, String, default: 'Hello'\n\naction :run do\n  log \"#{new_resource.greeting} world\"\nend\n```\n\nYou can test that resource by creating a spec file `spec/greet_spec.rb`:\n\n```ruby\n# Load ChefSpec and put our test into ChefSpec mode.\nrequire 'chefspec'\n\n# Describing our custom resource.\ndescribe 'mycookbook_greet' do\n  # Normally ChefSpec skips running resources, but for this test we want to\n  # actually run this one custom resource.\n  step_into :mycookbook_greet\n  # Nothing in this test is platform-specific, so use the latest Ubuntu for\n  # simulated data.\n  platform 'ubuntu'\n\n  # Create an example group for testing the resource defaults.\n  context 'with the default greeting' do\n    # Set the subject of this example group to a snippet of recipe code calling\n    # our custom resource.\n    recipe do\n      mycookbook_greet 'test'\n    end\n\n    # Confirm that the resources created by our custom resource's action are\n    # correct. ChefSpec matchers all take the form `action_type(name)`.\n    it { is_expected.to write_log('Hello world') }\n  end\n\n  # Create a second example group to test a different block of recipe code.\n  context 'with a custom greeting' do\n    # This time our test recipe code sets a property on the custom resource.\n    recipe do\n      mycookbook_greet 'test' do\n        greeting 'Bonjour'\n      end\n    end\n\n    # Use the same kind of matcher as before to confirm the action worked.\n    it { is_expected.to write_log('Bonjour world') }\n  end\nend\n```\n\nAnd then run your test using `chef exec rspec`.\n\n### Testing a Recipe\n\nAs a general rule of thumb, only very complex recipes benefit from ChefSpec unit\ntests. If you find yourself writing a lot of recipe unit tests, consider converting\nthe recipes to custom resources instead. For the sake of example we'll use a\nsimple recipe, `recipes/farewell.rb`:\n\n```ruby\nlog \"#{node[\"mycookbook\"][\"farewell\"]} world\"\n```\n\nYou can test that recipe by creating a spec file `spec/farewell_spec.rb`:\n\n```ruby\n# Load ChefSpec and put our test into ChefSpec mode.\nrequire 'chefspec'\n\n# Describing our recipe. The group name should be the recipe string as you would\n# use it with include_recipe.\ndescribe 'mycookbook::farewell' do\n  # Nothing in this test is platform-specific, so use the latest Ubuntu for\n  # simulated data.\n  platform 'ubuntu'\n\n  # Create an example group for testing the recipe defaults.\n  context 'with default attributes' do\n    # Since there was no `recipe do .. end` block here, the default subject is\n    # recipe we named in the `describe`. ChefSpec matchers all take the form\n    # `action_type(name)`.\n    it { is_expected.to write_log('Goodbye world') }\n  end\n\n  # Create a second example group to test with attributes.\n  context 'with a custom farewell' do\n    # Set an override attribute for this group.\n    override_attributes['mycookbook']['farewell'] = 'Adios'\n\n    # Use the same kind of matcher as before to confirm the recipe worked.\n    it { is_expected.to write_log('Adios world') }\n  end\nend\n```\n\n## Cookbook Dependencies\n\nIf your cookbook depends on other cookbooks, you must ensure ChefSpec knows how\nto fetch those dependencies. If you use a monorepo-style layout with all your\ncookbooks in a single `cookbooks/` folder, you don't need to do anything.\n\nIf you are using Berkshelf, `require 'chefspec/berkshelf'` in your spec file (or `spec_helper.rb`):\n\n```ruby\nrequire 'chefspec'\nrequire 'chefspec/berkshelf'\n```\n\nIf you are using a Policyfile, `require 'chefspec/policyfile'` in you spec file (or `spec_helper.rb`):\n\n```ruby\nrequire 'chefspec'\nrequire 'chefspec/policyfile'\n```\n\nYour `Policyfile.rb` should look something like this:\n\n```ruby\n# The policy name is ignored but you need to specify one.\nname 'my_cookbook'\n# Pull dependent cookbooks from https://supermarket.chef.io/\ndefault_source :supermarket\n# The run list is also ignored by ChefSpec but you need to specify one.\nrun_list 'my_cookbook::default'\n# The name here must match the name in metadata.rb.\ncookbook 'my_cookbook', path: '.'\n```\n\n## Writing Tests\n\nChefSpec is an RSpec library, so if you're already familiar with RSpec you can\nuse all the normal spec-y goodness to which you are accustomed. The usual structure\nof an RSpec test file is a file named like `spec/something_spec.rb` containing:\n\n```ruby\nrequire 'chefspec'\n\ndescribe 'resource name or recipe' do\n  # Some configuration for everything inside this `describe`.\n  platform 'redhat', '7'\n  default_attributes['value'] = 1\n\n  context 'when some condition' do\n    # Some configuration that only applies to this `context`.\n    default_attributes['value'] = 2\n\n    # `matcher` is some matcher function which we'll cover below.\n    it { expect(value).to matcher }\n    # There is a special value you can expect things on called `subject`, which\n    # is the main thing being tested.\n    it { expect(subject).to matcher }\n    # And if prefer it for readability, `expect(subject)` can be written as `is_expected`.\n    it { is_expected.to matcher }\n  end\n\n  context 'when some other condition' do\n    # Repeat as needed.\n  end\nend\n```\n\n### ChefSpec Matchers\n\nThe primary matcher used with ChefSpec are resource matchers:\n\n```ruby\nit { expect(chef_run).to ACTION_RESOURCE('NAME') }\n# Or equivalently.\nit { is_expected.to ACTION_RESOURCE('NAME') }\n```\n\nThis checks that a resource like `RESOURCE 'NAME'` would have run the specified\naction if the cookbook was executing normally. You can also test for specific\nproperty values:\n\n```ruby\nit { is_expected.to create_user('asmithee').with(uid: 512, gid: 45) }\n# You can also use other RSpec matchers to create a \"compound matcher\". Check\n# RSpec documentation for a full reference on the built-in matchers.\nit { is_expected.to install_package('myapp').with(version: starts_with(\"3.\")) }\n```\n\n#### `render_file`\n\nFor the common case of testing that a file is rendered to disk via either a\n`template`, `file`, or `cookbook_file` resource, you can use a `render_file`\nmatcher:\n\n```ruby\nit { is_expected.to render_file('/etc/myapp.conf') }\n# You can check for specific content in the file.\nit { is_expected.to render_file('/etc/myapp.conf').with_content(\"debug = false\\n\") }\n# Or with a regexp.\nit { is_expected.to render_file('/etc/myapp.conf').with_content(/user = \\d+/) }\n# Or with a compound matcher.\nit { is_expected.to render_file('/etc/myapp.conf').with_content(start_with('# This file managed by Chef')) }\n# Or with a Ruby block of arbitrary assertions.\nit do\n  is_expected.to render_file('/etc/myapp.conf').with_content { |content|\n    # Arbitrary RSpec code goes here.\n  }\nend\n```\n\n#### Notifications\n\nAs actions do not normally run in ChefSpec, testing for notifications is a special\ncase. Unlike the resource matchers which evaluate against the ChefSpec runner,\nthe notification matchers evaluate against a resource object:\n\n```ruby\n# To match `notifies :run, 'execute[unpack]', :immediately\nit { expect(chef_run.remote_file('/download')).to notify('execute[unpack]') }\n# To check for a specific notification action.\nit { expect(chef_run.remote_file('/download')).to notify('execute[unpack]').to(:run) }\n# And to check for a specific timing.\nit { expect(chef_run.remote_file('/download')).to notify('execute[unpack]').to(:run).immediately }\n```\n\nAnd similarly for subscriptions:\n\n```ruby\nit { expect(chef_run.execute('unpack')).to subscribe_to('remote_file[/download]').on(:create) }\n```\n\n### Test Subject\n\nRSpec expectations always need a value to run against, with the main value being\ntested for a given example group (`describe` or `context` block) is called the\n`subject`. In ChefSpec this is almost always `ChefSpec::Runner` that has converge\nsome recipe code.\n\nThere are two ways to set which recipe code should be run for the test. More commonly\nfor testing custom resources, you use the `recipe` helper method in the test to\nprovide an in-line block of recipe code:\n\n```ruby\ndescribe 'something' do\n  recipe do\n    my_custom_resource 'something' do\n      debug true\n    end\n  end\nend\n```\n\nBy using an in-line block of recipe code, you can try many variations to test\ndifferent configurations of your custom resource.\n\nIf no `recipe` block is present, ChefSpec will use the name of the top-level\n`describe` block as a recipe name to run. So for the case of testing a recipe\nin your cookbook, use the `cookbookname::recipename` string as the label:\n\n```ruby\ndescribe 'mycookbook'\n# Or.\ndescribe 'mycookbook::myrecipe'\n```\n\n### Test Settings\n\nMost ChefSpec configuration is set in your example groups (`describe` or `context`\nblocks) using helper methods. These all follow the RSpec convention of inheriting\nfrom a parent group to the groups inside it. So a setting in your top-level `describe`\nwill automatically be set in any `context` unless overridden:\n\n```ruby\ndescribe 'something' do\n  platform 'ubuntu'\n\n  # Platform is Ubuntu for any tests here.\n  it { is_expected.to ... }\n\n  context 'when something' do\n    # Platform is still Ubuntu for any tests here.\n  end\n\n  context 'when something else' do\n    platform 'fedora'\n    # But platform here will be Fedora.\n  end\nend\n```\n\n#### Platform Data\n\nTo support simulating Chef runs on the same OS as you use your cookbooks on, ChefSpec\nloads pre-fabricated Ohai data from [Fauxhai](https://github.com/chefspec/fauxhai/).\nTo configure which OS' data is set for your test, use the `platform` helper method:\n\n```ruby\ndescribe 'something' do\n  platform 'ubuntu', '18.04'\n  # ...\nend\n```\n\nYou can specify a partial version number to get the latest version of that OS\nmatching the provided prefix, or leave the version off entirely to get the latest\nversion overall:\n\n```ruby\n# Will use the latest RedHat 7.x.\nplatform 'redhat', '7'\n# Will use the latest version of Windows.\nplatform 'windows'\n```\n\n**WARNING:** If you leave off the version or use a partial version prefix, the\nbehavior of your tests may change between versions of Chef Workstation as new data is\navailable in Fauxhai. Only use this feature if you're certain that your tests\ndo not (or should not) depend on the specifics of OS version.\n\n#### Node Attributes\n\nNode attributes are set using the `default_attributes`, `normal_attributes`,\n`override_attributes`, and `automatic_attributes` helper methods. These inherit\nfrom a parent group to its children using a deep merge, like in other places in\nChef:\n\n```ruby\ndescribe 'something' do\n  default_attributes['myapp']['name'] = 'one'\n  default_attributes['myapp']['email'] = 'myapp@example.com'\n\n  context 'when something' do\n    default_attributes['myapp']['name'] = 'two'\n  end\nend\n```\n\nAny values set using `automatic_attributes` take priority over Fauxhai data.\n\n#### Step Into\n\nNormally ChefSpec skips all resource (and provider) actions. When testing the\nimplementation of a custom resource, we need to tell ChefSpec to run actions\non our specific custom resource so it can be tested:\n\n```ruby\ndescribe 'something' do\n  step_into :my_custom_resource\nend\n```\n\n\n#### Other ChefSpec Configuration\n\nYou can specify any other ChefSpec configuration options using the `chefspec_options`\nhelper:\n\n```ruby\ndescribe 'something' do\n  chefspec_options[:log_level] = :debug\nend\n```\n\n### Stubbing\n\nIn order to keep unit tests fast and independent of the target system, we have to\nmake sure that any interaction with the system (either the target node or the Chef\nServer, both parts of the system just in opposite directions) is replaced with a\nfake, local version. For some thing, like ensuring that resource actions are\nreplaced with a no-op, the stubbing is automatic. For others, we need to tell ChefSpec\nhow to handle things.\n\n#### Guards\n\nThe most common case of interacting with the system is a guard clause on a resource:\n\n```ruby\nnot_if 'some command'\n# Or.\nonly_if 'some command'\n```\n\nIn order for ChefSpec to know how to evaluate the resource, we need to tell it\nhow the command would have returned for this test if it was running on the actual\nmachine:\n\n```ruby\ndescribe 'something' do\n  recipe do\n    execute '/opt/myapp/install.sh' do\n      # Check if myapp is installed and runnable.\n      not_if 'myapp --version'\n    end\n  end\n\n  before do\n    # Tell ChefSpec the command would have succeeded.\n    stub_command('myapp --version').and_return(true)\n    # Tell ChefSpec the command would have failed.\n    stub_command('myapp --version').and_return(false)\n    # You can also use a regexp to stub multiple commands at once.\n    stub_command(/^myapp/).and_return(false)\n  end\nend\n```\n\nIf using the Ruby code block form of a guard (e.g. `not_if { something }`), see\nthe [Ruby stubbing section](#ruby-code) below.\n\n#### Search\n\nWhen testing code that uses the `search()` API in Chef, we have to stub out the\nresults that would normally come from the Chef Server:\n\n```ruby\ndescribe 'something' do\n  recipe do\n    web_servers = search(:node, 'roles:web').map { |n| n['hostname'] }\n  end\n\n  before do\n    stub_search(:node, 'roles:web').and_return([{hostname: 'one'}, {hostname: two}])\n  end\nend\n```\n\n#### Searches in libraries\n\nWhen testing code in a library that uses `Chef::Search::Query.new.search()`, we have\nto stub out the results that would normally come from the Chef Server:\n\n```ruby\ndescribe 'something' do\n  recipe do\n    results = Chef::Query::Search.new.search(:node, \"tags:mytag AND chef_environment:my_env\"))\n  end\n\n  before do\n    query = double\n    allow(query).to receive(:search) do |_, arg2|\n    case arg2.downcase\n    when /tags\\:mytag AND chef_environment\\:my_env/\n        [\n            [\n                stub_node(\"server01\", ohai: { hostname: \"server01\", ipaddress: '169.0.0.1' }, platform: 'windows', version: '2016'),\n                stub_node(\"server02\", ohai: { hostname: \"server02\", ipaddress: '169.0.0.2' }, platform: 'windows', version: '2016'),\n            ],\n            0,\n            2,\n        ]\n    else\n        [\n            [],\n            0,\n            0\n        ]\n    end\n    allow(Chef::Search::Query).to receive(:new).and_return(query)\n  end\nend\n```\n\n#### Data Bags\n\nSimilar to the Search API, the `data_bag()` and `data_bag_item()` APIs normally\nfetch data from Chef Server so we need to stub their results:\n\n```ruby\ndescribe 'something' do\n  recipe do\n    # Side note: don't write recipe code like this. This should be `search(:users, '*:*')`.\n    users = data_bag('users').map do |user|\n      data_bag_item('users', user['id'])\n    end\n  end\n\n  before do\n    stub_data_bag('users').and_return(['asmithee'])\n    stub_data_bag_item('users', 'asmithee').and_return({uid: 1234})\n  end\nend\n```\n\n#### Resource and Provider Methods\n\nWhen testing custom resources, it is often useful to stub methods on the resource\nor provider instance. These can be set up using the `stubs_for_resource` and\n`stubs_for_provider` helpers:\n\n```ruby\ndescribe 'something' do\n  recipe do\n    my_custom_resource 'something'\n  end\n\n  # Set up stubs for just the one resource.\n  stubs_for_resource('my_custom_resource[something]') do |res|\n    # Can use any RSpec Mocks code here, see below.\n    allow(res).to receive(:something)\n  end\n  # Stubs for any instance of my_custom_resource.\n  stubs_for_resource('my_custom_resource') do |res|\n    # ...\n  end\n  # Stubs for any resource.\n  stubs_for_resource do |res|\n    # ...\n  end\n\n  # Stubs for the provider for just the one resource.\n  stubs_for_provider('my_custom_resource[something]') do |res|\n    # Can use any RSpec Mocks code here, see below.\n    allow(res).to receive(:something)\n  end\n  # And similar to the above for any provider of a type or any overall.\nend\n```\n\nBy default, stubs for the resource will also be set up on the `current_resource` and `after_resource` objects that are\ncreated via `load_current_value`.  This can be disabled by using `stubs_for_resource('my_custom_resource[something]',\ncurrent_value: false)`.  You can also manually set stubs for only the `current_resource` and `after_resource` objects using\n`stubs_for_current_value`.\n\n#### Ruby Code\n\nFor more complex Ruby code, in recipes, libraries, or custom resources, you have\nthe full power of RSpec and RSpec Mocks available to you.\n\nOne issue that comes up often is stubbing filesystem checks such as `File.exist?`.  Since those are global class methods\nby stubbing them they will be stubbed throughout the entire chef-client codebase that chefspec relies upon.  There are\nmany calls to `File.exist?` in any chefspec test that are not immediately visible to the user.  In order to\nmake the client behave correctly the pattern that should be followed is to allow all the chef-client calls to operate\nnormally using `and_call_original` and then to stub the exact path the test needs:\n\n```ruby\nbefore do\n  allow(File).to receive(:exist?).and_call_original\n  allow(File).to receive(:exist?).with('/test/path').and_return(true)\nend\n```\n\nAll the ruby methods off of the File, Dir and FileUtils classes along with any other global class methods that the\nclient might use, should follow a similar pattern for stubbing.\n\nCheck out the [RSpec Mocks documentation](https://relishapp.com/rspec/rspec-mocks/docs)\nfor more information about setting up Ruby method stubs.\n\n## Development\n\n1. Fork the repository from GitHub.\n2. Clone your fork to your local machine:\n\n  ```\n  $ git clone git@github.com:USER/chefspec.git\n  ```\n\n3. Create a git branch\n\n  ```\n  $ git checkout -b my_bug_fix\n  ```\n\n4. **Write tests**\n\n5. Make your changes/patches/fixes, committing appropriately\n\n6. Run the tests: `bundle exec rake`\n\n7. Push your changes to GitHub\n\n8. Open a Pull Request\n\nChefSpec is on [Travis CI][travis] which tests against multiple Chef and Ruby versions.\n\nIf you are contributing, please see the [Contributing Guidelines](https://github.com/chefspec/chefspec/blob/main/CONTRIBUTING.md) for more information.\n\n## License\n\nMIT - see the accompanying [LICENSE](https://github.com/chefspec/chefspec/blob/main/LICENSE) file for details.\n","funding_links":[],"categories":["Ruby","Tools"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchefspec%2Fchefspec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchefspec%2Fchefspec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchefspec%2Fchefspec/lists"}