Different Types of Puppet Testing Modules
Why should you test your Puppet modules?
At first glance, writing tests for your Puppet modules appears to be no more than simply duplicating your manifests in a different language and, for basic “package/file/service” modules, it is.
However, when you start leveling up your modules to include dynamic content from templates, support multiple operating systems or take different actions when passed parameters, these tests become invaluable when adding new functionality to your modules, protecting against regressions when refactoring or upgrading to a new Puppet release.
Puppet is written in Ruby and makes use of rspec testing for unit tests. Newcomers to rspec usually find a short learning curve in understand what rspec testing actually does. We write rspec tests to ensure that certain things were coded in our source code. In the context of puppet rspec testing, we are making sure that the class is defined and that certain resources exist. The first rspec test you will write is a check that your module’s class was created.
Remember that we are only testing that resources are coded in our modules, not that the modules actually do what was coded. For example, a good rspec test is to ensure that we have a file resource that copies a file to your /etc directory for configuration settings. We write an rspec test to ensure that the file resource is coded correctly not that our module will actually copy the file to your /etc dir. To better clarify, puppet rspec tests only look through your puppet code. The module is not actually run through a puppet agent at this point to test that it is doing what you want. That level of testing occurs at the puppet acceptance testing cycle.
Puppet unit testing
Puppet rspec testing can test a great many things. It can be as simple or as complicated as you like. In fact, it is usually best to integrate puppet rspec tests in an iterative process. As your experience grows with the puppet rspec test, you can even use it for Test Driven Development (TDD) which is a way of programming in which you write the tests for your module first, then code your module to pass the tests. This kind of coding has seen great successes in the Python world.
With puppet unit testing, you can ensure that the module by itself will work to the best of your ability. However, in most puppet environments, you will not be using a single module rather you will have many modules with some having dependencies on other modules. Puppet unit testing is a good way to make sure that module by itself will work with dependencies, but we need a better way to test the modules as a whole. This is where puppet acceptance testing comes in.
Puppet acceptance testing
Test-driven development can bring a number of benefits. An approach where you write tests first makes you think about what you need to develop. Subsequent refactoring generally leads to designs that emerge in the code with high levels of test coverage.
Recently PuppetLabs introduced a new tool for acceptance testing called Beaker. A Puppet module is applied to a system under test (SUT) which in turn is acceptance tested. These tests would normally complement a unit test-driven approach to writing modules with rspec-puppet.
Acceptance testing for Puppet is done using Beaker. Documentation for Beaker is a little patchy but getting rapidly better.
Beaker can use loads of different cloud and virtualization infrastructures to create SUTs (System Under Test).
We will be using a modified version of the git workflow for updating our module and deploying the same module to different puppet environments. We will create a feature branch on our module where we work on a feature/bug fix. Then we push our changes back to git which will fire off automatic testing through Jenkins. If all the tests pass, then we will have a ‘gatekeeper’ step in our pipeline that will allow us to approve the promotion of our module to production. Before that happens, we will deploy the version of our module that exists in the feature branch to our non-production puppet environment where we can perform user acceptance testing. When we are ready to promote to production, we will merge our feature branch to the master branch and deploy that to our production puppet environment.