Using Molecule to Test Ansible Roles

Keir Whitlock
4 min readDec 23, 2019

--

Gain Confidence in your Ansible Roles by Writing Tests

Prerequisites

If you wish to use Molecule for local development, and use a Docker container to test your roles on; you will of course need docker installed locally. I am using docker-for-mac; which works really well.

Setting up your environment

I like to use python’s virtualenv to separate concerns and keep each working environment I may be using, isolated.

$ virtualenv --no-site-packages venv
$ source venv/bin/activate

Once activated; install molecule

$ pip install molecule

This should also install Ansible; and various other dependancies.

You might need the Docker driver for Molecule also, so install that too.

$ pip install molecule[docker]

Use Molecule to create a new role

$ molecule init role -d docker -r ansible.role.example

This will create a generic role structure from ansible-galaxy as well as an additional molecule directory, from which all the tests and setup for molecule will be kept.

Although I have specified the docker driver here using -d docker it is in fact the default driver, if non were specified.

If you look inside the molecule directory you will see it has given you a Dockerfile.j2 from which molecule will use to spin up a new docker container to test your task from.

As molecule uses ansible-galaxy to create the templated role, there are metadata checks in place. Within meta/main.yml there are 5 sections that need to be filled in

  1. author
  2. description
  3. company
  4. license
  5. platform

Otherwise the molecule lint sequence will fail. Alternatively you can rename the yml file to something else for now.

$ mv meta/main.yml meta/main

Running a Molecule test

Before writing anything, run a molecule test and see what the output is.

$ cd ansible.role.example
$ molecule test
--> Validating schema /Users/keirwhitlock/development/ansible.role.example/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
└── default
├── lint
├── dependency
├── cleanup
├── destroy
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
├── cleanup
└── destroy
--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in /Users/keirwhitlock/development/ansible.role.example/...
Lint completed successfully.

I’ve cut this short to save screen space; but you should of seen molecule spin up a container, run its test(s) and then delete the container again.

Now that you have molecule up and running and a base role created, you can no go ahead and start adding your tasks as you normally would have, and you can run molecule test at anytime to get molecule to spin up a test box and try out your roles tasks.

If you are continuously iterating your development of your role, you might not want the docker container to constantly be created and destroyed each time you run molecule test . If this is the case you can use molecule converge instead, which will leave your container running. Once you are done you can issue the molecule destroy command to delete it.

NB: The molecule.yml is used to setup the environment for molecule to use, and the default image for docker to spin up is centos:7 ; so you will need to edit this if this is incorrect for you.

Add some tasks to your role

Just to illustrate how you might use molecule for your development. I’m going to add a basic task to my ansible.role.example role.

So within tasks/main.yml I’m going to add a task to install httpd .

---
# tasks file for ansible.role.example
- name: install apache package
package:
name: httpd
state: present

Run a converge to test

$ molecule converge

and you will see that molecule is using our role on the docker test box, and installing httpd as intended.

--> Action: 'converge'PLAY [Converge] ****************************************************************TASK [Gathering Facts] *********************************************************
ok: [instance]
TASK [ansible.role.example : install apache package] ***************************
changed: [instance]
PLAY RECAP *********************************************************************
instance : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

But how can we be extra certain the package is installed correctly?

Writing Tests

Molecule out the box uses testinfra to allow users to write python tests to check the test systems they are applying their Ansible roles to, for correctness.

Within your default scenario, within the molecule directory there is a python script where you can add your tests.

molecule/default/tests/test_default.py

Inside you will see that there is already a example test there for you, which is checking for the existence of the /etc/hosts file. This is the 1 test you may have noticed in the molecule test output earlier.

Testinfra comes out the box with various modules that can be used for testing your Ansible with. As we have just installed the httpd package, lets test that this exists. Testinfra has a package class for this very thing.

Adding a simple test for this is simply

def test_httpd_installed(host):
assert host.package("httpd").is_installed

Now lets run the verify command to run our tests and hopefully things should pass.

Good! Now we have 2 tests that are passing. Just for illustration I’ll show you a snippet of the output when the tests do not pass.

Here I have changed the test to look for apache instead of httpd and as a result our test has failed.

Conclusion

So as you can see from my quick example; you can use Molecule to help you in the process of developing an Ansible role. Spinning up a new container to test your role’s tasks against, and even the ability writing additional tests, (which can be as simple or as complex as you feel you need), in order to assure yourself that your role is configuring your systems as you expect.

Next time, I’m going to take the process one step further, and have Gitlab-CI detect updates to the role repositories and conduct a test run for us.

--

--