Molecule is the standard testing framework for Ansible roles. It creates ephemeral test environments (using Docker, Vagrant, or cloud providers), runs your role against them, and verifies the results. This guide covers setting up Molecule for automated testing of your Ansible roles.
Why Test Ansible Roles?
- Catch bugs early: Find issues before deploying to production servers
- Ensure idempotency: Verify roles can run multiple times without unwanted changes
- Test across platforms: Verify roles work on Ubuntu, CentOS, Debian, etc.
- CI/CD integration: Automated testing on every code change
- Documentation: Tests serve as examples of how the role should be used
Installation
# Install Molecule with Docker driver
pip install molecule molecule-docker ansible-lint yamllint
# Verify
molecule --version
# Initialize a new role with Molecule
molecule init role my_org.webserver
# Or add Molecule to an existing role
cd roles/webserver
molecule init scenario --driver-name docker
Molecule Configuration
# molecule/default/molecule.yml
---
dependency:
name: galaxy
options:
requirements-file: requirements.yml
driver:
name: docker
platforms:
- name: ubuntu-24
image: ubuntu:24.04
pre_build_image: true
command: /sbin/init
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
tmpfs:
- /run
- /tmp
- name: debian-12
image: debian:12
pre_build_image: true
command: /sbin/init
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
- name: rocky-9
image: rockylinux:9
pre_build_image: true
command: /sbin/init
privileged: true
provisioner:
name: ansible
config_options:
defaults:
gathering: smart
fact_caching: jsonfile
fact_caching_connection: /tmp/facts_cache
inventory:
group_vars:
all:
nginx_worker_processes: 2
nginx_server_name: test.example.com
verifier:
name: ansible
lint: |
yamllint .
ansible-lint
Test Playbook (Converge)
# molecule/default/converge.yml
---
- name: Converge
hosts: all
become: yes
pre_tasks:
- name: Update apt cache (Debian)
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
roles:
- role: webserver
vars:
nginx_worker_processes: 2
nginx_server_name: test.example.com
nginx_sites:
- name: default
server_name: test.example.com
root: /var/www/html
port: 80
Verification Tests
# molecule/default/verify.yml
---
- name: Verify
hosts: all
become: yes
gather_facts: yes
tasks:
- name: Verify Nginx is installed
package:
name: nginx
state: present
check_mode: yes
register: nginx_installed
failed_when: nginx_installed.changed
- name: Verify Nginx service is running
service:
name: nginx
state: started
enabled: yes
check_mode: yes
register: nginx_service
failed_when: nginx_service.changed
- name: Verify Nginx is listening on port 80
wait_for:
port: 80
timeout: 10
- name: Verify Nginx responds
uri:
url: http://localhost/
status_code: [200, 301, 302]
register: nginx_response
- name: Verify config file exists
stat:
path: /etc/nginx/nginx.conf
register: nginx_conf
failed_when: not nginx_conf.stat.exists
- name: Verify worker processes setting
shell: grep "worker_processes 2" /etc/nginx/nginx.conf
changed_when: false
- name: Verify no default sites remain
stat:
path: /etc/nginx/sites-enabled/default
register: default_site
- name: Print test results
debug:
msg: "All verification tests passed!"
Running Tests
# Run full test sequence
molecule test
# The test sequence runs:
# 1. dependency — Install role dependencies from Galaxy
# 2. lint — Run yamllint and ansible-lint
# 3. cleanup — Clean up any previous test resources
# 4. destroy — Destroy any existing test instances
# 5. syntax — Check playbook syntax
# 6. create — Create test instances (Docker containers)
# 7. prepare — Run preparation playbook
# 8. converge — Run the role
# 9. idempotence — Run the role again (verify no changes)
# 10. verify — Run verification tests
# 11. cleanup — Clean up
# 12. destroy — Destroy test instances
# Run individual steps
molecule create # Create test containers
molecule converge # Run the role
molecule verify # Run verification
molecule login -h ubuntu-24 # SSH into a test container
molecule destroy # Clean up
CI/CD Integration
# .github/workflows/molecule.yml
name: Molecule Test
on:
push:
paths: ['roles/**']
pull_request:
paths: ['roles/**']
jobs:
molecule:
runs-on: ubuntu-latest
strategy:
matrix:
scenario: [default, multi-site]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: pip install molecule molecule-docker ansible-lint yamllint
- name: Run Molecule
run: molecule test -s ${{ matrix.scenario }}
working-directory: roles/webserver
env:
PY_COLORS: '1'
ANSIBLE_FORCE_COLOR: '1'
Multiple Test Scenarios
# Create additional scenarios
molecule init scenario multi-site --driver-name docker
# molecule/multi-site/converge.yml — Test with multiple sites
# molecule/multi-site/verify.yml — Verify multi-site configuration
# Run specific scenario
molecule test -s multi-site
# Run all scenarios
molecule test --all
Best Practices
- Test across multiple platforms: Include Ubuntu, Debian, and RHEL-family in your test matrix
- Always test idempotency: The idempotence step catches roles that make unnecessary changes
- Use verify.yml for positive assertions — check that services run and ports are open
- Run Molecule in CI: Catch issues before merging pull requests
- Create multiple scenarios for different use cases (single site, multi-site, SSL, etc.)
- Use
molecule loginto debug failing tests interactively