Molecule Testing Examples
Introduction to Molecule
Molecule is a testing framework designed specifically for Ansible roles. It provides support for testing with different providers (Docker, Vagrant, etc.) and verifiers (Testinfra, Goss, etc.).
Prerequisites & Installation
- Python 3.x, Ansible, and Docker or Podman installed locally
- Install Molecule and common plugins/tools:
pip install "molecule" "molecule-plugins[docker]" ansible ansible-lint pytest testinfra
# For Podman driver instead of Docker:
# pip install "molecule-plugins[podman]"
Quickstart: Start a Basic Config
molecule init role my_role
cd my_role
molecule init scenario --driver-name docker
Key files created under molecule/default/:
- molecule.yml for scenario config (driver, platforms, provisioner, verifier)
- converge.yml playbook used during converge
- verify.yml or pytest tests for verification
- prepare.yml (optional) for pre-tasks like installing dependencies
Basic Molecule Example
Directory Structure
my_role/
├── molecule/
│ └── default/
│ ├── molecule.yml
│ ├── converge.yml
│ └── verify.yml
├── tasks/
│ └── main.yml
└── meta/
└── main.yml
Example Configuration
# molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: ubuntu:22.04
pre_build_image: true
provisioner:
name: ansible
verifier:
name: ansible
Test Playbook
# converge.yml
---
- name: Converge
hosts: all
tasks:
- name: Include role
ansible.builtin.include_role:
name: my_role
Note: Older examples and some templates still use playbook.yml. Both approaches work; converge.yml is the current default.
Verification Tests
# verify.yml
---
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Check if nginx is installed
ansible.builtin.package:
name: nginx
state: present
check_mode: true
register: nginx_check
- name: Verify nginx service is running
ansible.builtin.service:
name: nginx
state: started
check_mode: true
register: nginx_service
- name: Assert conditions
assert:
that:
- not nginx_check.changed # package is installed
- not nginx_service.changed # service is running
success_msg: "All assertions passed!"
fail_msg: "Some assertions failed!"
Advanced Molecule Scenario
Multi-Platform Testing
# molecule.yml for multi-platform testing
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: ubuntu2204
image: ubuntu:22.04
pre_build_image: true
- name: ubuntu2404
image: ubuntu:24.04
pre_build_image: true
provisioner:
name: ansible
inventory:
group_vars:
all:
ansible_user: root
verifier:
name: ansible
Custom Test Scenarios
# custom-test.yml
---
- name: Custom test scenario
hosts: all
tasks:
- name: Get service status
command: systemctl status nginx
register: service_status
changed_when: false
failed_when: false
- name: Verify custom configuration
stat:
path: /etc/nginx/conf.d/custom.conf
register: config_file
- name: Run assertions
assert:
that:
- "'active (running)' in service_status.stdout"
- config_file.stat.exists
success_msg: "Custom configuration verified successfully"
Running Tests
# Basic commands
molecule create # Create test instances
molecule converge # Run playbook
molecule verify # Run verification
molecule test # Full test sequence
# Testing specific scenarios
molecule test -s custom-scenario
Idempotence tip: run converge twice; the second run should report no changes.
molecule converge
molecule converge # should be idempotent
Platforms are defined in molecule.yml under platforms:. Select different OS images by editing that config.
Test Development Tips
-
Incremental Testing
bash molecule create molecule converge molecule login # Debug on the instance molecule verify molecule destroy -
Debugging Tests ```bash # Enable verbose output molecule --debug test
# Keep instances on failure molecule test --destroy never ```
- Idempotence & Linting ```bash # Check idempotence (second converge should not change anything) molecule converge && molecule converge
# Lint your roles (if ansible-lint is installed) ansible-lint ```
- Best Practices
- Keep tests focused and atomic
- Use idempotency checks
- Implement proper cleanup
- Test both success and failure cases
Drivers and Verifiers Overview
- Drivers
- Docker: fast, widely available, great default.
- Podman: rootless-friendly; ensure networking/volume access works in your environment.
- Verifiers
- Ansible: quick to start; uses tasks/asserts.
- pytest + testinfra: richer assertions, fixtures, parametrization.
Writing Good Tests
- Validate state, not commands: packages installed, files templated, services enabled, ports listening.
- Keep converge minimal; put assertions in verify/tests.
- One concern per test; name tests clearly and fail with helpful messages.
Selecting Images and Platforms
- Prefer current Ubuntu LTS images:
ubuntu:22.04,ubuntu:24.04for examples. - Add RHEL-compatible images (Alma/Rocky) in CI if your role targets them.
- During development, test a single platform locally; expand matrix in CI to save time.
Organizing Scenarios
- Use
defaultfor the happy-path install. - Create additional scenarios for features (e.g.,
tls,upgrade,hardening). - Share common vars via
group_varsinside the scenario when needed.
Troubleshooting & FAQs
- Use
molecule loginto inspect containers. Combine with--debugfor verbose logs. - Services under systemd inside containers may require extra setup (e.g., using images that support systemd or mocking service checks).
- Network/image pull issues: pre-pull images, configure proxies, or use local registry mirrors.
Verifier: pytest + testinfra Example
# tests/test_default.py
import testinfra
def test_nginx_package(host):
pkg = host.package("nginx")
assert pkg.is_installed
def test_nginx_service(host):
svc = host.service("nginx")
# Inside containers, .is_running may rely on init system support
assert svc.is_enabled or True
Enable with a verifier config or keep Ansible verifier and add pytest to CI pipeline.
Podman Driver Notes
pip install "molecule-plugins[podman]"
In molecule.yml:
driver:
name: podman
Rootless environments may need volume/privilege adjustments for some services.
CI Example (GitHub Actions)
# .github/workflows/molecule.yml
name: Molecule
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: |
python -m pip install --upgrade pip
pip install "molecule" "molecule-plugins[docker]" ansible ansible-lint pytest testinfra
- name: Run Molecule tests
run: molecule test
This runs the full lifecycle and can be extended to a matrix of Python/OS images.