Skip to content

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

  1. Incremental Testing bash molecule create molecule converge molecule login # Debug on the instance molecule verify molecule destroy

  2. Debugging Tests ```bash # Enable verbose output molecule --debug test

# Keep instances on failure molecule test --destroy never ```

  1. 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 ```

  1. Best Practices
  2. Keep tests focused and atomic
  3. Use idempotency checks
  4. Implement proper cleanup
  5. 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.04 for 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 default for the happy-path install.
  • Create additional scenarios for features (e.g., tls, upgrade, hardening).
  • Share common vars via group_vars inside the scenario when needed.

Troubleshooting & FAQs

  • Use molecule login to inspect containers. Combine with --debug for 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.