0

How to write the ChefSpec Unit tests for ruby_block? What if the local variables are declared in the recipe? How will it be handled?

Here is the code of a recipe:

package 'autofs' do
  action :install
end

src = '/etc/ssh/sshd_config'

unless ::File.readlines(src).grep(/^PasswordAuthentication yes/).any?
  Chef::Log.warn "Need to add/change PasswordAuthentication to yes in sshd config."
  ruby_block 'change_sshd_config' do
    block do
      srcfile = Chef::Util::FileEdit.new(src)
      srcfile.search_file_replace(/^PasswordAuthentication no/, "PasswordAuthentication yes")
      srcfile.insert_line_if_no_match(/^PasswordAuthentication/, "PasswordAuthentication yes")
      srcfile.write_file
    end
  end
end

unless ::File.readlines(src).grep('/^Banner /etc/issue.ssh/').any?
  Chef::Log.warn "Need to change Banner setting in sshd config."
  ruby_block 'change_sshd_banner_config' do
    block do
      srcfile = Chef::Util::FileEdit.new(src)
      srcfile.search_file_replace(/^#Banner none/, "Banner /etc/issue.ssh")
      srcfile.insert_line_if_no_match(/^Banner/, "Banner /etc/issue.ssh")
      srcfile.write_file
    end
  end
end

As I am new to ChefSpec, I am able to write the code for the basic resources. I have written the Unit Test as below:

require 'chefspec'

describe 'package::install' do

  let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '16.04').converge(described_recipe) }

  it 'install a package autofs' do
    expect(chef_run).to install_package('autofs')
  end

  it 'creates a ruby_block with an change_sshd_config' do
    expect(chef_run).to run_ruby_block('change_sshd_config')
  end

  it 'creates a ruby_block with an change_sshd_banner_config' do
    expect(chef_run).to run_ruby_block('change_sshd_banner_config')
  end

end

Does the above implementation is corrct? I am not able to figure out how it can be written for complex resources like ruby block, etc. And how the local variables declared in recipe should be taken care. Thanks in advance..

Jayu
  • 189
  • 1
  • 1
  • 8
  • Well, that is pretty much you can do. ChefSpec does not run the ruby_block, it just checks, if the ruby_block is in the resource collection with the right action. To test the outcome of ruby block you have to write integration tests, for example with test-kitchen and inspec. – Draco Ater May 12 '17 at 06:19

2 Answers2

1
 let(:conf) { double('conf') }
    before do
      allow(File).to receive(:readlines).with('/etc/ssh/sshd_config').and_return(["something"])
      allow(File).to receive(:readlines).with('/etc/ssh/sshd_config').and_return(["something"])
end

 it 'creates a ruby_block with an change_sshd_config' do
    expect(chef_run).to run_ruby_block('change_sshd_config')
    expect(Chef::Util::FileEdit).to receive(:new).with('/etc/ssh/sshd_config').and_return(conf)
    confile = Chef::Util::FileEdit.new('/etc/ssh/sshd_config')
  end

Do the same thing for other ruby block as well! Mostly it will work.

Or

You can test using inspec to test the state of your system. It seems like you may want to test the state of your system for the way sshd is configured after Chef has applied config. You could accomplish that with inspec with something like the following snippet:

describe file('/etc/ssh/sshd_config') do
  its('content') { should match /whatever/ }
end

I hope this helps!

CodeRunner
  • 21
  • 3
0

Unit tests should be testing that specific inputs produce the expected outputs. By expecting Chef to have run_ruby_block, you're essentially saying "Does Chef work the way I expect it to" -- not, "Does my ruby_block resource work the way I expect it to".

You should use ChefSpec to validate that the side effects of the ruby_block are what you expect them to be. That is, that the file(s) are modified. The ChefSpec documentation on RenderFileMatchers is probably what you're looking for.

vase
  • 339
  • 1
  • 6
  • ChefSpec is a unit test framework, it does not converge the node and does not run the ruby_block, it can only check if the resource is in the final resource collection. – Draco Ater May 12 '17 at 06:17
  • Good call. Generally, this is why I try to avoid ChefSpec all together in favor of something like [Serverspec](http://serverspec.org/). It should be possible, at least, to mock the file and still test the side effects of a `ruby_block` though, no? – vase May 12 '17 at 11:42
  • 1
    The OP has to mock the `::File.readlines(...).grep(...).any?` calls and then check, if the `ruby_blocks` are in the resource collection. – Draco Ater May 12 '17 at 12:13