Introduction
Molecule is the standard testing framework for Ansible roles. It creates ephemeral instances (Docker, Podman, Vagrant, cloud), runs your role, verifies the result, and tears everything down. Essential for CI/CD and ensuring roles work before production deployment.
Install Molecule
pip install molecule molecule-docker
# Or with Podman
pip install molecule molecule-podman
# Verify
molecule --version
Initialize a Role with Molecule
# Create new role with Molecule scaffold
molecule init role myorg.myapp
# Or add Molecule to existing role
cd roles/myapp
molecule init scenario -d docker
Directory structure:
roles/myapp/
├── defaults/main.yml
├── handlers/main.yml
├── meta/main.yml
├── molecule/
│ └── default/
│ ├── molecule.yml # Configuration
│ ├── converge.yml # Playbook to test
│ ├── verify.yml # Verification tests
│ └── prepare.yml # Pre-test setup (optional)
├── tasks/main.yml
└── templates/
molecule.yml Configuration
# molecule/default/molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: ubuntu-test
image: ubuntu:22.04
pre_build_image: true
command: /bin/bash
tmpfs:
- /run
- /tmp
- name: rocky-test
image: rockylinux:9
pre_build_image: true
command: /bin/bash
tmpfs:
- /run
- /tmp
provisioner:
name: ansible
config_options:
defaults:
gathering: smart
inventory:
group_vars:
all:
app_port: 8080
app_env: test
verifier:
name: ansible
converge.yml — The Test Playbook
# molecule/default/converge.yml
---
- name: Converge
hosts: all
become: true
tasks:
- name: Include myapp role
ansible.builtin.include_role:
name: myapp
verify.yml — Verification Tests
# molecule/default/verify.yml
---
- name: Verify
hosts: all
become: true
tasks:
- name: Check package is installed
ansible.builtin.package:
name: nginx
state: present
check_mode: true
register: pkg
failed_when: pkg.changed
- name: Check service is running
ansible.builtin.service:
name: nginx
state: started
check_mode: true
register: svc
failed_when: svc.changed
- name: Check config file exists
ansible.builtin.stat:
path: /etc/nginx/nginx.conf
register: conf
failed_when: not conf.stat.exists
- name: Check port is listening
ansible.builtin.wait_for:
port: 80
timeout: 5
- name: Check config content
ansible.builtin.command: grep "worker_processes" /etc/nginx/nginx.conf
changed_when: false
Run Tests
# Full test sequence (create → converge → idempotence → verify → destroy)
molecule test
# Step by step
molecule create # Create instances
molecule converge # Run playbook
molecule idempotence # Run again (should have 0 changes)
molecule verify # Run verification
molecule destroy # Clean up
# Lint only
molecule lint
# Login to test instance
molecule login --host ubuntu-test
# List instances
molecule list
Test Sequence
The default molecule test runs:
- dependency — Install Galaxy requirements
- lint — Lint playbooks (yamllint, ansible-lint)
- cleanup — Pre-test cleanup
- destroy — Remove old instances
- syntax — Check syntax
- create — Create test instances
- prepare — Run prepare.yml (pre-test setup)
- converge — Run converge.yml (your role)
- idempotence — Run converge again (zero changes expected)
- verify — Run verify.yml (assertions)
- cleanup — Post-test cleanup
- destroy — Remove instances
prepare.yml — Pre-Test Setup
# molecule/default/prepare.yml
---
- name: Prepare
hosts: all
become: true
tasks:
- name: Install Python (for minimal images)
ansible.builtin.raw: apt-get update && apt-get install -y python3
changed_when: false
when: ansible_os_family == 'Debian'
- name: Install prerequisites
ansible.builtin.package:
name:
- sudo
- systemd
state: present
Multiple Scenarios
molecule/
├── default/ # Default scenario (Docker)
│ ├── molecule.yml
│ ├── converge.yml
│ └── verify.yml
├── podman/ # Podman scenario
│ ├── molecule.yml
│ ├── converge.yml
│ └── verify.yml
└── vagrant/ # Vagrant scenario (full VM)
├── molecule.yml
├── converge.yml
└── verify.yml
# Run specific scenario
molecule test -s podman
molecule test -s vagrant
CI/CD Integration
GitHub Actions
# .github/workflows/molecule.yml
name: Molecule Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: pip install molecule molecule-docker ansible-lint
- name: Run Molecule
run: molecule test
GitLab CI
# .gitlab-ci.yml
molecule:
image: python:3.11
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
before_script:
- pip install molecule molecule-docker
script:
- molecule test
Testing with Ansible Verify
# verify.yml — Comprehensive verification
---
- name: Verify
hosts: all
become: true
tasks:
# Check packages
- name: Verify packages installed
ansible.builtin.package:
name: "{{ item }}"
state: present
check_mode: true
register: pkg_check
failed_when: pkg_check.changed
loop:
- nginx
- redis-server
# Check files
- name: Verify config files
ansible.builtin.stat:
path: "{{ item }}"
register: file_check
failed_when: not file_check.stat.exists
loop:
- /etc/nginx/nginx.conf
- /etc/nginx/sites-enabled/myapp
# Check services
- name: Verify services running
ansible.builtin.command: systemctl is-active {{ item }}
changed_when: false
loop:
- nginx
- redis-server
# Check connectivity
- name: Verify HTTP response
ansible.builtin.uri:
url: http://localhost:80
status_code: 200
# Check file content
- name: Verify config content
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
line: "worker_processes auto;"
check_mode: true
register: content_check
failed_when: content_check.changed
Troubleshooting
Docker Permission Denied
# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
systemd Not Available in Container
Use command: /sbin/init and privileged mode:
platforms:
- name: test
image: ubuntu:22.04
command: /sbin/init
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
cgroupns_mode: host
Related Articles
Conclusion
Molecule is the standard for testing Ansible roles. It creates disposable instances, runs your role, checks idempotency (zero changes on second run), and runs verification tests. Use Docker for fast local testing and integrate with GitHub Actions or GitLab CI for automated testing on every commit. The key workflow: molecule test runs the full sequence — create, converge, idempotence check, verify, destroy. Write thorough verify.yml tests that check packages, services, files, and connectivity.