On March 23rd, I presented a live webinar on Writing Great Unit Tests. Watch the recording below to hear me explain how to write unit tests and how they apply to the domain of cookbook development. You’ll see me demonstrate how to build cookbooks by using a test-driven approach. At the end of the presentation, you’ll know when to apply unit tests, how to use the Chef DK tools effectively, and where you can go to improve your skills even more. At the end of this post, I’ve included some Q&A from the presentation.
Is there a good way to test resource notify and subscribe in ChefSpec?
Yes. Look at the examples in the ChefSpec documentation for:
Does Pry being a generic tool have any limitations in working with chef recipes?
None. When you write Chef you are really writing in the Domain Specific Language (DSL) built on top of Ruby. Pry is built for Ruby. What you will not have are perhaps some shortcuts that might find handy to use while debugging Chef code. If you found some then you could always codify them and turn them into a Pry plugin that you could release to the community.
What was the spec parameter to switch to documentation mode?
Here is all the documentation on RSpec’s various flags and commands:
How to you use ChefSpec to test custom resources or LWRPs?
ChefSpec provides support for testing custom resources that you can implement.
When you write a Custom Resource it is responsible for you to provide with your cookbook the RSpec matchers necessary for the resource that you defined. That was the problem I faced when I started to work with the Ark cookbook. None of my recipes used the custom resource that I provided so I needed to build the custom matchers and then create a cookbook solely to test the use of the custom resource.
Will tests like installing a package or downloading a file – do the action? If yes, then the state is changed and the next test will pass because the package is already installed. If no, then how can we be sure that it will actually be installed?
No. All ChefSpec examples and expectations are in-memory. No actual state of the system changes. This is important to remember. Because it means that are isolated from external dependencies allowing you to execute tests and get fast feedback. That also means that you are isolated from external dependencies which means you really do not know if it work on the platform. That is why both unit testing and integration testing should be used together to verify the recipes you create.
After you’ve added the breakpoint, how can you “continue” running? Can you step through from there?
To continue debugging you can use the ‘exit’ command. This will continue through the execution until it reaches the next break point or end of execution. ‘exit!’ was the command that will halt the execution immediately and return you back to the shell.
Is there a chef command to create the scaffolding/dir structure for the spec/unit/ directory?
The ‘chef’ tool that is packaged with the Chef Development Kit (Chef DK) allows you to generate recipes. When you generate a recipe it will create the entire directory structure for you to store your unit tests.
I think SoloRunner would be a lot faster here … any reason for choosing ServerRunner vs SoloRunner?
I left the unit tests as they were generated by Chef. I am often working with a Chef Server so I also do not often feel the need to change it over. I have not tested the performance between the two but I imagine that you are probably right. I would love to read a blog post with some results.
When you used rspec command with the file path and ended with the line number (e.g. rspec spec/unit/recipes/install_spec.rb:21) does it run up to line 21 or just line 21?
It only executes the example found on line 21. Line 20 or 22 would have also worked to target that one example.
I’ve only ever done integration tests (serverspec). If I unit test all resources can I reduce my integration tests? I currently do everything as integration tests because that’s all I knew prior to today.
Yes. That is personally what I would choose to do.
I personally use my integration tests to focus on the big things like does the service return the content that matters most to me. That means I often do not express things like: is this package installed; is this service running; etc. I leave those small things to my unit tests.
Is there anyway to create “libraries” of test? Resuse tests? or is that just wrong?
There are ways in which you can define tests and have them be re-usable. There are two ways build right into RSpec called ‘Shared Examples’ and ‘Shared Context’. Take a look at those two topics:
- https://relishapp.com/rspec/rspec-core/v/3-4/docs/example-groups/shared-examples
- https://relishapp.com/rspec/rspec-core/v/3-4/docs/example-groups/shared-context
Sharing examples and context between cookbooks requires you to package up that code make it available to multiple sources. That means you would need to create a gem.
Is there any atom integration for running chef spec from a keyboard shortcut or anything like that? Just to avoid having to switch between the editor and console constantly.
Atom provides a number of different plugins. This is one that I found right away that would do the job you are describing:
I also mentioned in the webinar a Ruby project named Guard and Guard-Rspec. These tools allow you to setup file watches that will automatically execute RSpec upon change to any recipe file or specification file. Take a look at the Ark project where I have that setup. It requires some more Ruby knowledge to work with Bundler and then to run Guard but it is not a completely insurmountable hurdle.