Hi. I'm trying to use a custom resource in my unit test. The custom resource i'm trying to use lives in a different cookbook and my working recipe uses that resource in its code.
resource_name 'other_cookbook_resource'
In my unit test, every time I try to reference that resource in my code it errors out with
NoMethodError:
undefined method `other_cookbook_resource' for cookbook: my_cookbook, recipe: _test
:Chef::Recipe
Heres the code in my unit test
recipe do
other_cookbook_resource 'test' do
name 'chris'
house 'yes'
only_if { 'data.valid?' }
end
end
before do
allow(data).to receive(:valid?).and_return(true)
end
I've tried following the examples on the Github at GitHub - chefspec/chefspec: Write RSpec examples and generate coverage reports for Chef recipes! but it ONLY gives examples on unit testing a custom resource when that resource is in the same cookbook.
My recipe depends on resources from a separate cookbook so i'm trying to mimic this in code.
Assistance is greatly appreciated.
Take a look at the unit tests in apache2 these work brilliantly for resource driven cookbooks https://github.com/sous-chefs/apache2
Hi Jason, thanks for assisting. I took a look at that cookbook, and it doesnt depend on any other cookbooks. Therefore, its not utilizing any resources that are in another cookbook.
any other suggestions?
Hi,
Are you using chefspec/berkshelf
or chefspec/policyfile
to resolve your dependencies? https://github.com/chefspec/chefspec#cookbook-dependencies
After this you can usually just test the resource like you would any built in resource. There is no need to step_into
https://github.com/chefspec/chefspec#step-into the resource or create a recipe
block which is more for testing the internals of a resource.
Hi @ccrebolder , i'm not.
I think I may have not understood the readme based on your reply. You mentioned I dont need the blocks
recipe do
# Insert code
end
I can do
before do
allow(data).to receive(:valid?).and_return(true)
end
expect(chef_run).to add_other_cookbook_resource.with(
name: 'chris',
house: 'yes'
)
and that works fine but I just didnt feel that was a good unit test.
I was trying to write my unit test first, so if it worked ok, I could just do a copy and paste of the resourc e code if it passed inside the unit test. That doesnt seem to be the case here and I would still have to type out the full resource into my recipe.
#Invalid Code
expect(chef_run).to add_other_cookbook_resource.with(
name: node['cookbook]['name']
end
#Valid code
recipe do
# I could copy and paste the following into my real recipe after the test passed saving me alot of time.
other_cookbook_resource do
name node['cookbook']['name']
house node['cookbook']['house']
only_if { 'data.valid?' }
end
end
I hope that makes sense or you were able to get an idea of what i was trying to do.
(Test the resource and if it worked, then copy and paste directly into my recipe. )
Christopher
Okay, I think I understand what you're saying. Unfortunately, the true time-saving method is to write the recipe code once, in the actual recipe, and then include it in the unit test. This is what we do when we write a block like describe 'mycookbook::myrecipe'
- chefspec loads our recipe code for us and we can use it in the runners it provides.
Maintaining two copies of your recipe code is quite labour intensive and is bound to lead to trouble if you or a colleague update the actual recipe code without updating the test code. Your tests will pass, but you are no longer testing the same thing.
It's true that the example is not a great unit test, and I suppose if a unit test looks brittle and useless it probably is. The power of chefspec at least would be from including many different contexts (using the before blocks) for a set of non-trivial branching conditions. This is especially useful if you have a lot of logic around different platforms and platform versions.
Apologies if I still haven't understood!
ps: you can use node attributes in your test with chef_run.node['some']['attribute']
although I don't think this will help you in this situation.