A while ago I was drafting an official Ansible Role for Icinga 2. From my previous experience with writing Puppet modules and helping shape Chef cookbooks I already knew approximately how the Ansible role should look like and what functionality should be included. At some point I came across the question how testing should be done during development?
From all the options available, two stuck out:
KitchenCI with an Ansible provisioner
I used TestKitchen in the past to test Chef cookbooks, so this was an advantage. However, after some research I decided to go with Molecule, a tool designed especially for Ansible roles and playbooks. It seems to me to be the better option to use a tool that explicitly aims to test Ansible stuff, rather then using TestKitchen which usually is used for Chef cookbooks but has an extension to support Ansible provisioning.
Molecule support multiple virtualization providers, including Vagrant, Docker, EC2, GCE, Azure, LXC, LXD, and OpenStack. Additionally, various test frameworks are supported to run syntax checks, linting, unit- and integration tests.

Getting started with Molecule

Molecule is written in Python and the only supported installation method is Pip. I won’t go through all requirements, since there’s a great installation documentation available. Besides the requirements, you basically just install molecule via Pip

pip install molecule

After molecule is successfully installed, the next step is to initialise a new Ansible role:

molecule init role -d docker -r ansible-myrole

This will not only create files and directories needed for testing, but the whole Ansible role tree, including all directories and files to get started with a new role. I choose to use Docker as a virtualisation driver. With the init command you can also set the verifier to be used for integration tests. The default is testinfra and I stick with that. Other options are goss and inspec.
Molecule uses Ansible to provision the containers for testing. It creates automatically playbooks to prepare, create and delete those containers. One special playbook is created to actually run your role. The main configuration file, molecule.yml includes some general options, such as what linter to use and on which platform to test. By default cents:7 is the only platform used to test the role. Platforms can be added to run the tests on multiple operating systems and versions.
I mentioned before that I use testinfra to write the integration tests. With the init command Molecule creates a default scenario, which we can use for the first steps. For example, checking if a package is installed and a the service is running the code would look like this:

import os
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
def test_icinga2_is_installed(host):
    i2_package = host.package("icinga2")
    assert i2_package.is_installed
def test_icinga2_running_and_enabled(host):
    i2_service = host.service("icinga2")
    assert i2_service.is_running
    assert i2_service.is_enabled

Running tests

There are multiple ways to run your tests, but there’s one command that does everything automatically. It runs listing tests, provisions containers, runs your playbook, runs integration tests, shows failures and destroys the containers at the end.

molecule test

With other commands you can do all of these steps one by one. Of course there’s much more possible with molecule, such as creating different scenarios and using multiple instances to do more complex testing. The Molecule documentation is well written and has some examples on what you can do more.

Blerim Sheqa
Blerim Sheqa
Product Manager

Blerim ist seit 2013 bei NETWAYS und seitdem schon viel in der Firma rum gekommen. Neben dem Support und diversen internen Projekten hat er auch im Team Infrastruktur tatkräftig mitgewirkt. Hin und wieder lässt er sich auch den ein oder anderen Consulting Termin nicht entgehen. Mittlerweile kümmert sich Blerim hauptsächlich im Icinga Umfeld um die technischen Partner und deren Integrationen in Verbindung mit Icinga 2.