Last Updated: February 25, 2016
· mlafeldt

Testing code inside ruby_block with ChefSpec

I've recently written a wrapper cookbook that installs Ruby using rbenv and ruby_build.

The cookbook's default recipe looks like this:

include_recipe 'ruby_build'
include_recipe 'rbenv::system'

# Fix PATH so that rbenv will be available immediately.
ruby_block "Add rbenv to PATH" do
  block do
    rbenv_root = node['rbenv']['root_path']
    ENV['PATH'] = "#{rbenv_root}/shims:#{rbenv_root}/bin:#{ENV['PATH']}"

Writing ChefSpec examples for this turned out to be tricky, since the code inside the ruby_block is only evaluated during convergence and not at compile time. I could have written a simple RSpec matcher to check that the resource ruby_block[Add rbenv to PATH] is executed at all, but I wanted to verify the actual code as well (ChefSpec 0.9.0 doesn't support ruby_block by default).

As luck would have it, this tip by Andrew Crump helped me to come up with the following spec:

describe 'The recipe ruby::default' do
  let (:chef_run) do
    chef_run =
    chef_run.node.automatic_attrs['platform'] = 'ubuntu'
    chef_run.converge 'ruby::default'

  # ...

  it 'should add rbenv to PATH' do
    # run actual ruby_block resource to check PATH
    chef_run.resources.find { |r| == 'Add rbenv to PATH' }.old_run_action(:create)
    rbenv_root = chef_run.node['rbenv']['root_path']
    ENV['PATH'].should include "#{rbenv_root}/shims"
    ENV['PATH'].should include "#{rbenv_root}/bin"

As you can see, the only way to test code inside ruby_block is to actually converge the resource. I know that this is not always an option, but here it is.

4 Responses
Add your response


Hi @mlafeldt, thanks for sharing! Great tip!

FYI ChefSpec 3.4.0+ now seems to require this slight change:

chef_run.find_resources(:ruby_block).find { |r| == 'Add rbenv to PATH' }.old_run_action(:create)
over 1 year ago ·

@steve-jansen Good to know. Thank you!

over 1 year ago ·

Looks like the newest hotness would be:
ruby chef_run.ruby_block('Add rbenv to PATH').old_run_action(:create)

over 1 year ago ·

Thanks for initiating this conversation.

When working with ruby_blocks or really any ruby I try to extract that ruby into a method and then test that method. Here's an example that I created after someone pointed to your great solution.

over 1 year ago ·