I
like Chefspec, and on the
whole find it very helpful when I’m writing cookbooks. At times though,
it can be rather frustrating. A frustration I hit today was in mocking a
search
result. My recipe pulled a node name out of a the data returned
by search
, and fed it into a template. (It was configuring a Redis
slave, and the search looked up the relevant master.)
This was done by
redis_slaveof = results.first.name
Don’t worry, there was logic around this, and it only got executed if the search returned exactly one result. But, when I ran the test, it failed with:
Failure/Error: ChefSpec::Runner.new do |node|
NoMethodError:
undefined method `name' for "10745740492":String
My mock was of the form that you find in the Chefspec docs. Something like:
stub_search('node', "chef_environment:#{c_env} AND redis.type:master " +
"AND redis.service:#{test_service}").and_return([{ name: "#{test_master}" }])
So, it’s clear why the NoMethodError
got thrown: name’s a property,
not a method. What you need to return is an object which has a name
method, which returns the string you want in your mocked search. What
you need is a
mash.
Require the Gem (assuming you’ve installed it of course), and create a
mash with a single fake method, name
, which returns the value of the
variable test_master
, as we tried to do in the stub_search
above:
require 'mash'
search_stub = Mash.new(name: test_master)
Then, adapt stub_search
to return it:
stub_search('node', "chef_environment:#{c_env} AND redis_type:master " +
"AND redis_service:#{test_service}").and_return([search_stub])
Then, calling the name
method of the stubbed search works just fine.
Obviously, you can put as many “methods” into your mash as your tests
require.
If you’re wondering why I’m using a +
to concatenate two strings it’s
because I’m anal about 80 character lines. Rubocop might not like it,
but it will have to lump it, because using a backslash doesn’t work in
this case.