{"id":51388,"date":"2025-08-07T08:31:54","date_gmt":"2025-08-07T08:31:54","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=51388"},"modified":"2025-08-07T08:49:15","modified_gmt":"2025-08-07T08:49:15","slug":"ansible-unit-testing-in-ansible","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/ansible-unit-testing-in-ansible\/","title":{"rendered":"Ansible: Unit Testing in Ansible?"},"content":{"rendered":"\n<ul class=\"wp-block-list\">\n<li>https:\/\/github.com\/devopsschool-demo-labs-projects\/ansible-molecule-testing<\/li>\n\n\n\n<li>https:\/\/github.com\/devopsschool-demo-labs-projects\/ansible-molecule-example<\/li>\n\n\n\n<li>https:\/\/github.com\/devops-school\/molecule-ansible-playbook-testing<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-is-unit-testing-in-ansible\">What is Unit Testing in Ansible?<\/h2>\n\n\n\n<p><strong>Unit testing in Ansible<\/strong> refers to testing the smallest pieces of code within Ansible\u2014primarily custom modules, plugins, or Ansible-related Python code\u2014in isolation from the rest of the system. Unlike <em>playbook<\/em> testing (which is more integration or functional testing), unit tests focus on verifying the logic and expected outcomes of functions or methods without requiring actual servers or external systems.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-does-unit-testing-work-in-ansible\">How Does Unit Testing Work in Ansible?<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Scope:<\/strong> Unit tests target specific functions in Ansible modules\/utilities. They are <em>Python-based<\/em> and are executed independently of target infrastructure.<\/li>\n\n\n\n<li><strong>Method:<\/strong> You write Python test scripts that mock dependencies and check expected behaviors (for example, does the module return the right result for specific input? Does it call the correct APIs?).<\/li>\n\n\n\n<li><strong>Framework:<\/strong> Ansible leverages standard Python tools like <code>pytest<\/code> or <code>unittest<\/code>, and provides the <code>ansible-test<\/code> command-line tool to execute unit tests in a standardized, continuous integration-friendly way.<\/li>\n\n\n\n<li><strong>Location:<\/strong> Unit tests for the Ansible core project or custom collections are typically placed in a <code>tests\/unit\/<\/code> directory and mirror the structure of the code under test.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"why-use-unit-tests\">Why Use Unit Tests?<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Faster feedback<\/strong> than integration tests.<\/li>\n\n\n\n<li>Do not require target servers or real infrastructure.<\/li>\n\n\n\n<li>Help catch logical and regression bugs in module code early.<\/li>\n\n\n\n<li>Force you to design testable (often better) code.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-to-do-unit-testing-in-ansible-step-by-step\">How to Do Unit Testing in Ansible: Step by Step<\/h2>\n\n\n\n<p>Below is a practical and minimal workflow to write and run unit tests for Ansible modules or Python code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Create or Identify the Code to Test<\/h2>\n\n\n\n<p>Suppose you have a custom module, e.g. <code>my_module.py<\/code>, or a utility function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Create a Test Script<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Place it in <code>tests\/unit\/modules\/test_my_module.py<\/code> or a similar path for your project.<\/li>\n\n\n\n<li>Use <code>pytest<\/code> or <code>unittest<\/code> in Python.<\/li>\n<\/ul>\n\n\n\n<p><strong>Example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">python<code>import unittest\nfrom ansible.modules.my_namespace import my_module\n\nclass TestMyModule(unittest.TestCase):\n    def test_add(self):\n        result = my_module.add(2, 3)\n        self.assertEqual(result, 5)\n\n    def test_fail_case(self):\n        with self.assertRaises(ValueError):\n            my_module.add('a', 1)\n<\/code><\/pre>\n\n\n\n<p>Or with a simple function-based style (for small cases):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">python<code>def test_add():\n    from ansible.modules.my_namespace import my_module\n    assert my_module.add(2, 3) == 5\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3. Use Mocking for External Dependencies<\/h2>\n\n\n\n<p>For modules interacting with systems\/network, mock those calls to make tests independent of actual systems.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Run the Unit Tests<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Activate your development environment.<\/li>\n\n\n\n<li>Use the standard Ansible test runner (inside your ansible repo): text<code>source hacking\/env-setup ansible-test units --docker -v<\/code> Or, for a specific file\/module: text<code>ansible-test units --docker -v my_module<\/code><\/li>\n\n\n\n<li>Or, run with pytest directly if set up that way: text<code>pytest tests\/unit\/modules\/test_my_module.py<\/code><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">5. Interpret Results<\/h2>\n\n\n\n<p>Green = tests passed. Red = test or logic failure (with a Python traceback showing what failed and why).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6. Refine and Repeat<\/h2>\n\n\n\n<p>Add more test functions covering different logic branches, error cases, and edge cases.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"summary-table-unit-test-steps-for-ansible-modules\">Summary Table: Unit Test Steps for Ansible Modules<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Step<\/th><th>What to Do<\/th><\/tr><\/thead><tbody><tr><td>1<\/td><td>Write your Ansible module\/Python function<\/td><\/tr><tr><td>2<\/td><td>Write Python unit test scripts (test_*.py)<\/td><\/tr><tr><td>3<\/td><td>Use mocking for any system-level or network functions<\/td><\/tr><tr><td>4<\/td><td>Run tests using <code>ansible-test units<\/code> or pytest<\/td><\/tr><tr><td>5<\/td><td>Fix code\/tests based on failures, repeat cycle<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">References for Further Reading<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[Ansible&#8217;s official unit testing documentation]<\/li>\n\n\n\n<li>[Practical unit test project examples]<\/li>\n<\/ul>\n\n\n\n<p>You do <strong>not<\/strong> use unit tests for playbooks\/roles directly\u2014instead, use playbook integration testing tools like Molecule or full-featured CI workflows.<\/p>\n\n\n\n<p><strong>In short:<\/strong><br>Unit testing in Ansible means writing small Python tests targeting your custom modules\/utilities, running them with <code>ansible-test units<\/code> or <code>pytest<\/code>, and using mocks where needed\u2014resulting in quick, automated checks of your Ansible code&#8217;s core logic before running on actual servers.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><strong>Unit testing in Ansible<\/strong> is all about verifying the smallest, testable parts of your Ansible automation (like roles, modules, and tasks) <strong>in isolation<\/strong> before running them in production. Unlike full integration testing (which runs an entire playbook), unit tests are meant to catch mistakes and enforce code quality in your Ansible content <em>early and often<\/em>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What is Unit Testing in Ansible?<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Definition:<\/strong><br>Unit testing in Ansible is the practice of testing <strong>individual roles, modules, or even specific tasks<\/strong>\u2014without running the whole playbook against real infrastructure.<\/li>\n\n\n\n<li><strong>Goal:<\/strong><br>Quickly catch errors, syntax issues, bad logic, and unintended changes by testing code in isolation.<\/li>\n\n\n\n<li><strong>Tools:<\/strong><br>The most common and recommended tool for unit testing Ansible content is <strong><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/dev_guide\/testing.html\" target=\"_blank\" rel=\"noopener\">Ansible Test<\/a><\/strong> (built into ansible-core and Ansible collections).<br>Other popular frameworks: <strong>Molecule<\/strong> (for role\/collection testing), <strong>pytest<\/strong>, <strong>Testinfra<\/strong>, and <strong>YAML linting<\/strong>.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>How Does Unit Testing Work in Ansible?<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ansible code is split into units like roles, modules, or plugins.<\/li>\n\n\n\n<li>Each unit is tested with small, focused tests\u2014often using Python, YAML, or Ansible\u2019s built-in test tools.<\/li>\n\n\n\n<li><strong>Ansible Test<\/strong> runs these tests automatically.<\/li>\n\n\n\n<li>For <strong>roles<\/strong>, Molecule (plus pytest\/testinfra) is often used for deeper tests, including syntax, linting, and idempotence checks.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step-by-Step: Unit Testing in Ansible<\/strong><\/h2>\n\n\n\n<p>Below are <strong>two approaches<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>A. For Ansible Collections (using <code>ansible-test<\/code>)<\/strong><\/li>\n\n\n\n<li><strong>B. For Roles (using Molecule + Testinfra)<\/strong><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>A. Unit Testing Ansible Collections (with <code>ansible-test<\/code>)<\/strong><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 1: Install Requirements<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ensure you have Ansible and <code>ansible-test<\/code> (included with ansible-core and ansible-base).<\/li>\n<\/ul>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pip install ansible-core\n<\/code><\/span><\/pre>\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 2: Structure Your Collection<\/strong><\/h4>\n\n\n\n<p>If you have a collection (the modern way to develop reusable Ansible code), tests live in <code>tests\/unit\/<\/code>.<\/p>\n\n\n\n<p>Example structure:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">my_collection\/\n  plugins\/\n    modules\/\n      mymodule.py\n  tests\/\n    unit\/\n      plugins\/\n        modules\/\n          test_mymodule.py\n<\/code><\/span><\/pre>\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 3: Write a Unit Test<\/strong><\/h4>\n\n\n\n<p>A test for a custom module might look like this (<code>test_mymodule.py<\/code>):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">from ansible.modules.my_collection.mymodule import main\n\ndef test_some_logic():\n    <span class=\"hljs-comment\"># Simulate the module's logic and assert output<\/span>\n    pass\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 4: Run Ansible Test<\/strong><\/h4>\n\n\n\n<p>From your collection root:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">ansible-test units\n<\/code><\/span><\/pre>\n\n\n<p>This will discover and run all unit tests in <code>tests\/unit\/<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>B. Unit Testing Roles (with Molecule + Testinfra)<\/strong><\/h3>\n\n\n\n<p>Molecule can run basic role checks, linting, and testinfra-based verification (often in containers or VMs).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 1: Install Molecule and Testinfra<\/strong><\/h4>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">pip install <span class=\"hljs-string\">\"molecule&#91;docker]\"<\/span> testinfra ansible-lint\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 2: Initialize Molecule in Your Role<\/strong><\/h4>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">cd myrole\nmolecule init scenario -r myrole -d docker\n<\/code><\/span><\/pre>\n\n\n<p>This creates a <code>molecule\/<\/code> folder with default config and tests.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 3: Write Unit Tests in <code>molecule\/default\/tests\/test_default.py<\/code><\/strong><\/h4>\n\n\n\n<p>Example (checks if nginx is installed):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">def test_nginx_installed(host):\n    nginx = host.package(<span class=\"hljs-string\">\"nginx\"<\/span>)\n    assert nginx.is_installed\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\"><strong>Step 4: Run Molecule Test<\/strong><\/h4>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">molecule test\n<\/code><\/span><\/pre>\n\n\n<p>This will:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Lint your role<\/li>\n\n\n\n<li>Create a container\/VM<\/li>\n\n\n\n<li>Apply your role<\/li>\n\n\n\n<li>Run unit tests<\/li>\n\n\n\n<li>Destroy the test environment<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>C. Linting and Syntax Checking (Recommended for ALL Ansible Code)<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>ansible-lint:<\/strong> Lint all playbooks\/roles for best practices: <code>ansible-lint playbook.yml<\/code><\/li>\n\n\n\n<li><strong>ansible-playbook &#8211;syntax-check:<\/strong> Catch syntax errors quickly. <code>ansible-playbook --syntax-check playbook.yml<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Summary Table<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Tool<\/th><th>Purpose<\/th><th>What It Tests<\/th><\/tr><\/thead><tbody><tr><td><code>ansible-test<\/code><\/td><td>Unit test modules\/plugins<\/td><td>Ansible collection plugins\/modules<\/td><\/tr><tr><td><code>molecule<\/code><\/td><td>Role testing (all-in-one)<\/td><td>Roles: syntax, lint, behavior, idempotence<\/td><\/tr><tr><td><code>testinfra<\/code><\/td><td>Python host tests<\/td><td>Verifies end state inside VMs\/CTs<\/td><\/tr><tr><td><code>ansible-lint<\/code><\/td><td>Style and best practices<\/td><td>YAML and role\/playbook structure<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Best Practices<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Start with <code>ansible-lint<\/code> and <code>--syntax-check<\/code> for ALL playbooks\/roles.<\/li>\n\n\n\n<li>For custom modules\/plugins\/collections, <strong>use <code>ansible-test units<\/code><\/strong>.<\/li>\n\n\n\n<li>For roles: <strong>use Molecule + Testinfra<\/strong> for behavior\/unit tests.<\/li>\n\n\n\n<li>Integrate tests into your CI\/CD pipeline for every code change.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>References<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/dev_guide\/testing.html\" target=\"_blank\" rel=\"noopener\">Ansible Testing Strategies<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/molecule.readthedocs.io\/en\/latest\/\" target=\"_blank\" rel=\"noopener\">Molecule Docs<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/testinfra.readthedocs.io\/en\/latest\/\" target=\"_blank\" rel=\"noopener\">Testinfra Docs<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/ansible-lint.readthedocs.io\/en\/latest\/\" target=\"_blank\" rel=\"noopener\">ansible-lint Docs<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>What is Unit Testing in Ansible? Unit testing in Ansible refers to testing the smallest pieces of code within Ansible\u2014primarily custom modules, plugins, or Ansible-related Python code\u2014in isolation from the&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[2],"tags":[],"class_list":["post-51388","post","type-post","status-publish","format-standard","hentry","category-uncategorised"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/51388","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=51388"}],"version-history":[{"count":2,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/51388\/revisions"}],"predecessor-version":[{"id":51390,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/51388\/revisions\/51390"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=51388"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=51388"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=51388"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}