Testing include_recipes with Chef and ChefSpec

While writing cookbooks, both personally and professionally, I practice a heavy use of TDD to ensure that the recipes are doing what I expect them to. As part of this, I will want to test both standard resources, as well as include_recipes:

Ensuring dependent recipes don't get run

When you're performing a runner.converge with ChefSpec, it is performing a converge by going through each of the recipes and running them in-memory. Because it actually runs the recipe, it means that if a given recipe requires any attributes to be set, then you will also need to put your attributes into the calling recipe. As I'm sure you can guess, having recipes including each other will then start to have quite a large set of attributes and configuration required in order to test what looks like a single recipe, but is instead the full chain of recipes required. This breaks the idea of "unit testing", as it doesn't give us a single unit to test against.

Therefore, the best way to get around this, is to simply not let the other recipes be run. Taking advantage of RSpec Mocks, we effectively let include_recipe be called, but it doesn't do anything:

Defensive include_recipes

However, if we have this running, it won't flag up include_recipe being called on any other recipes that we've not predicted in our tests. Yes, this should be more obvious when practicing TDD, but it ???. This would mean that recipes could be silently executing in the background, slowing down tests, which may not be as noticeable in the case that they don't require any extra attributes set.

To do this, we can utilise RSpec Mocks again, but this time, we can raise if there's a non-whitelisted recipe called.