Ansible Testing & Linting (Molecule)
Untested Ansible roles are liabilities. A role that has never been run against a clean system, verified to be idempotent, or checked against a target OS other than the one you developed on will eventually fail in production at the worst possible time. Molecule is the standard testing framework for Ansible roles. Ansible Lint is the standard code quality checker for playbooks and roles. Together, they bring software engineering discipline to infrastructure automation. This lesson gives you the skills to test confidently and ship automation you trust.
What Molecule Does
Molecule automates the full testing lifecycle for an Ansible role:
- Create: Spin up test instances (Docker containers, VMs, or cloud instances)
- Prepare: Run any prerequisite configuration on the instances
- Converge: Apply your role to the instances
- Idempotency: Run converge again and verify zero changes are reported
- Verify: Run assertions to confirm the role achieved the desired state
- Destroy: Tear down all test instances
This entire sequence runs automatically with molecule test. Failed tests report exactly which step failed and why.
Installing Molecule
pip3 install molecule molecule-docker ansible-lint
The molecule-docker plugin uses Docker containers as test instances — the fastest and most resource-efficient option for local development. Molecule also supports Vagrant, AWS, GCE, Azure, and others.
Initialising Molecule in a Role
cd roles/nginx molecule init scenario --driver-name docker
This creates the molecule/default/ directory structure:
molecule/
default/
molecule.yml # Scenario configuration
converge.yml # The test playbook (applies your role)
verify.yml # Assertions to run after converge
prepare.yml # (Optional) pre-role setupConfiguring molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: ubuntu-22.04
image: geerlingguy/docker-ubuntu2204-ansible
pre_build_image: true
- name: ubuntu-20.04
image: geerlingguy/docker-ubuntu2004-ansible
pre_build_image: true
- name: centos-8
image: geerlingguy/docker-centos8-ansible
pre_build_image: true
provisioner:
name: ansible
playbooks:
converge: converge.yml
verify: verify.yml
verifier:
name: ansibleThe geerlingguy/docker-*-ansible Docker images are pre-built containers with Python and systemd support — ideal for Ansible testing. Testing across multiple platforms simultaneously ensures cross-OS compatibility.
Writing converge.yml
---
- name: Converge
hosts: all
become: true
roles:
- role: nginx
vars:
nginx_http_port: 80
nginx_vhosts:
- server_name: testapp.local
document_root: /var/www/testappWriting verify.yml with Assertions
---
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Ensure Nginx package is installed
ansible.builtin.package:
name: nginx
state: present
check_mode: true
register: pkg_check
failed_when: pkg_check.changed
- name: Ensure Nginx service is running
ansible.builtin.service:
name: nginx
state: started
enabled: true
check_mode: true
register: svc_check
failed_when: svc_check.changed
- name: Ensure Nginx responds on port 80
ansible.builtin.uri:
url: http://localhost:80
status_code: 200
register: response
- name: Verify HTTP response
ansible.builtin.assert:
that:
- response.status == 200
fail_msg: "Nginx is not responding with HTTP 200"
success_msg: "Nginx is responding correctly"
- name: Verify virtual host config exists
ansible.builtin.stat:
path: /etc/nginx/sites-available/testapp.local.conf
register: vhost_file
failed_when: not vhost_file.stat.existsRunning the Full Test Suite
# Full test lifecycle (create, converge, idempotency, verify, destroy) molecule test # Just apply the role (faster iteration during development) molecule converge # Run only the verify assertions molecule verify # Drop into the test container for debugging molecule login --host ubuntu-22.04 # Destroy test instances molecule destroy
Ansible Lint: Code Quality Enforcement
Ansible Lint checks playbooks and roles against a comprehensive set of best practice rules:
# Install pip3 install ansible-lint # Lint the current directory ansible-lint # Lint a specific file ansible-lint site.yml # Lint with a specific profile ansible-lint --profile production
Ansible Lint profiles range from min (minimal rules) to production (strict professional standards). Common lint rules it enforces:
- All tasks must have a
name - Use the FQCN (Fully Qualified Collection Name) for modules:
ansible.builtin.aptnot justapt - Variables should be named with lowercase and underscores
- No deprecated module syntax
- Line length limits
- YAML formatting standards
Configuring ansible-lint with .ansible-lint
# .ansible-lint profile: production exclude_paths: - .cache/ - molecule/ skip_list: - yaml[line-length] # Skip line length rule for complex tasks warn_list: - experimental
Integrating Molecule into CI/CD
# GitHub Actions workflow fragment
- name: Run Molecule tests
run: |
pip install molecule molecule-docker ansible-lint
cd roles/nginx
molecule test
env:
PY_COLORS: '1'
ANSIBLE_FORCE_COLOR: '1'Try This: Write a Complete Molecule Test Suite for Your Nginx Role
Initialise Molecule in your roles/nginx directory. Write a verify.yml with at least five assertions: package installed, service running, service enabled on boot, config file exists with correct permissions, and HTTP response returns 200. Run molecule test and confirm all assertions pass. Then introduce a deliberate bug in your role (remove the service start task) and run molecule test again to see the failure. Fix the bug and re-run. This debugging cycle builds the test-driven reflex that distinguishes professional Ansible work.
Summary
Molecule automates the full role testing lifecycle: create, converge, idempotency check, verify, destroy. Testing across multiple platform containers simultaneously ensures cross-OS compatibility. The verify.yml playbook uses Ansible itself to assert that the role achieved the desired state. Ansible Lint enforces code quality and best practices against configurable rule sets. Integrating both into CI/CD pipelines makes quality enforcement automatic and non-negotiable.
