{"id":13647899,"url":"https://github.com/nickjj/rolespec","last_synced_at":"2025-06-30T20:39:39.484Z","repository":{"id":20953333,"uuid":"24241919","full_name":"nickjj/rolespec","owner":"nickjj","description":"A test library for testing Ansible roles","archived":false,"fork":false,"pushed_at":"2017-11-23T03:19:06.000Z","size":95,"stargazers_count":232,"open_issues_count":12,"forks_count":17,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-06-22T01:47:52.380Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"willf/bloom","license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nickjj.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.rst","contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-19T19:04:16.000Z","updated_at":"2025-04-14T19:18:29.000Z","dependencies_parsed_at":"2022-07-23T15:32:12.745Z","dependency_job_id":null,"html_url":"https://github.com/nickjj/rolespec","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/nickjj/rolespec","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Frolespec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Frolespec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Frolespec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Frolespec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nickjj","download_url":"https://codeload.github.com/nickjj/rolespec/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Frolespec/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262847713,"owners_count":23374061,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T01:03:49.624Z","updated_at":"2025-06-30T20:39:39.453Z","avatar_url":"https://github.com/nickjj.png","language":"Shell","funding_links":[],"categories":["Shell","Climbing"],"sub_categories":["Chess :chess_pawn:"],"readme":"RoleSpec\n========\n\n|Build status|\n\nA shell based test library for Ansible that works both locally and over Travis-CI.\n\n.. contents::\n   :local:\n\nTypical Travis setup vs using RoleSpec\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTravis on its own\n`````````````````\n\n- Tons of duplication\n- Locked into using Travis\n- Running bash in YAML syntax\n- Manual dependency management\n- Tons of \"Damn you Travis, please work\" commits\n\nRoleSpec\n````````\n- Still uses Travis, so you can keep the badges and CI environment\n- Runs on any Debian-based OS in case you want to use it locally\n- Customized assertions optimized for Ansible\n- ~75% less code written\n- Automatic dependency resolution\n- Automatic debug outputs\n- Custom linter\n- Your test cases end up being fully working examples/documentation\n- Multiple test modes to optimize iteration speed\n\nComparing real examples\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThe snippets below come from testing a Rails deployment role.\n\nThe old typical Travis way\n``````````````````````````\n\n.. code:: YAML\n\n  ---\n\n  language: \"python\"\n  python: \"2.7\"\n\n  env:\n    - SITE=\"tests/main.yml -i 'localhost,'\"\n\n  install:\n    - \"pip install ansible\"\n    - \"printf '[defaults]\\\\nroles_path = ../' \u003e ansible.cfg\"\n\n  before_script:\n    - \u003e\n      sudo ansible-galaxy install debops.secret debops.etc_services \\\n      debops.postgresql debops.nginx debops.monit\n\n      git config --global user.email 'foo@bar.com' \\\n      \u0026\u0026 git config --global user.name 'Foo Bar'\n\n      cd tests/testapp \u0026\u0026 git init \u0026\u0026 cd -\n\n  script:\n    - \"ansible-playbook $SITE --syntax-check\"\n    - \"ansible-playbook $SITE --connection=local -vvvv\"\n    - \u003e\n      ansible-playbook $SITE --connection=local\n      | grep -q \"changed=0.*failed=0\"\n      \u0026\u0026 (echo \"Idempotence test: PASS\" \u0026\u0026 exit 0)\n      || (echo \"Idempotence test: FAIL\" \u0026\u0026 exit 1)\n    - sleep 5\n    - \u003e\n      sudo cat /srv/users/testapp/.ssh/id_rsa\n      | grep -q \"ssh\"\n      \u0026\u0026 (echo \"Private key: PASS\" \u0026\u0026 exit 0)\n      || (echo \"Private key: FAIL\" \u0026\u0026 exit 1)\n    - \u003e\n      sudo groups testuser\n      | grep -q \"audio\"\n      \u0026\u0026 (echo \"Group: PASS\" \u0026\u0026 exit 0)\n      || (echo \"Group: FAIL\" \u0026\u0026 exit 1)\n    - \u003e\n      sudo stat -c \"%a %n\" /srv/users/testapp\n      | grep -q \"751\"\n      \u0026\u0026 (echo \"Secure home: PASS\" \u0026\u0026 exit 0)\n      || (echo \"Secure home: FAIL\" \u0026\u0026 exit 1)\n    - \u003e\n      sudo cat /etc/logrotate.d/testapp\n      | grep -q \"{.*}\"\n      \u0026\u0026 (echo \"Rotated logs: PASS\" \u0026\u0026 exit 0)\n      || (echo \"Rotated logs: FAIL\" \u0026\u0026 exit 1)\n    - \u003e\n      curl -k -s -o /dev/null -w \"%{http_code}\" https://localhost\n      | grep -q \"200\"\n      \u0026\u0026 (echo \"SSL 200 - Testapp: PASS\" \u0026\u0026 exit 0)\n      || (echo \"SSL 200 - Testapp: FAIL\" \u0026\u0026 exit 1)\n    - \u003e\n      curl -k -s -o /dev/null -w \"%{http_code}\" https://localhost/sidekiq\n      | grep -q \"200\"\n      \u0026\u0026 (echo \"SSL 200 - Sidekiq: PASS\" \u0026\u0026 exit 0)\n      || (echo \"SSL 200 - Sidekiq: FAIL\" \u0026\u0026 exit 1)\n    - \u003e\n      sudo monit status\n      | grep -q \"testapp\"\n      \u0026\u0026 (echo \"Monitoring Testapp: PASS\" \u0026\u0026 exit 0)\n      || (echo \"Monitoring Testapp: FAIL\" \u0026\u0026 exit 1)\n    - \u003e\n      sudo monit status\n      | grep -q \"sidekiq\"\n      \u0026\u0026 (echo \"Monitoring Sidekiq: PASS\" \u0026\u0026 exit 0)\n    || (echo \"Monitoring Sidekiq: FAIL\" \u0026\u0026 exit 1)\n\n\nThe same test case using RoleSpec\n`````````````````````````````````\n\n.. code:: Bash\n\n  #!/bin/bash\n\n  . \"${ROLESPEC_LIB}/main\"\n\n  install_ansible \"v1.7.1\"\n\n  cd \"${ROLESPEC_TEST}/test_files/testapp\" \u0026\u0026 git init \u0026\u0026 cd -\n\n  assert_playbook_runs\n  assert_playbook_idempotent\n  assert_playbook_idempotent_long\n\n  assert_permission \"/srv/users/testapp\" \"751\"\n  assert_user_in_group \"testuser\" \"audio\"\n\n  assert_in_file \"/srv/users/testapp/.ssh/id_rsa\" \"ssh\"\n  assert_in_file \"/etc/logrotate.d/testapp\" \"{.*}\"\n\n  assert_url \"https://${ROLESPEC_FQDN}\"\n  assert_url \"https://${ROLESPEC_FQDN}/sidekiq\"\n\n  assert_monitoring \"testapp\"\n  assert_monitoring \"sidekiq\"\n\nInstallation\n~~~~~~~~~~~~\n\nIf you're using it on Travis then you don't need to download anything.\n\nUse this ``.travis.yml`` as a guide, it would go in each of your role's repositories:\n\n.. code:: YAML\n\n  ---\n\n  # Ensure Python 2.7.x is being used\n  language: 'python'\n  python: '2.7'\n\n  # Use system installed packages inside of the Virtual environment\n  virtualenv:\n    system_site_packages: True\n\n  # Skip running these which boosts the Travis boot time\n  before_install: True\n  install: True\n\n  script:\n    # Clone the RoleSpec repo, feel free to use --branch xxx to use something\n    # other than the master branch (latest stable)\n    - 'git clone --depth 1 https://github.com/nickjj/rolespec'\n\n    # The location of YOUR test suite\n    - 'cd rolespec ; bin/rolespec -r https://github.com/you/some-test-suite'\n\nYou can also use RoleSpec locally, perhaps in a container or virtual machine.\n\n.. code:: Bash\n\n  git clone https://github.com/nickjj/rolespec\n  cd rolespec ; sudo make install\n\nGetting setup locally\n`````````````````````\n\nYou'll probably want to run tests locally in a container or VM so you can\niterate on them quicker. Then once you're ready you could push it out to Travis.\nWe will go over on how to do this shortly.\n\nWays to organize your tests\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**Dedicated test suite**\n\nIt would consist of 1 repository that contains isolated test cases for each\nrole you have. This is how we do it for DebOps. Check out the\n`DebOps test suite \u003chttps://github.com/debops/test-suite\u003e`_ for a working example.\n\nThis allows you to not pollute your role's commit history with things like\n\"Travis is a jerk face, attempt 42 finally worked!\". It also makes it\nconvenient for adding new tests.\n\n**A tests/ directory in each role**\n\nNot supported right now but it could be in the future. I'm looking for feedback\nto see if the demand is there. Let me know by opening an issue or by contacting\nme, `#debops \u003chttp://webchat.freenode.net/?channels=debops\u003e`_ on Freenode\nor `@nickjanetakis \u003chttps://twitter.com/nickjanetakis\u003e`_.\n\nWrite your first test case\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nLet's create a new test in a container/VM. I'm going to assume by now you have\ninstalled RoleSpec.\n\nFirst off we'll want to **init a new working directory**. This is where all of\nyour roles and tests will be stored. It can be located anywhere you want. Run this:\n\n.. code:: Bash\n\n  rolespec -i ~/foo\n\nFrom this point on I'm going to assume you're in your working directory. All\npaths will be relative to that.\n\nFor this example let's make pretend we have the following setup:\n\n- Your role name is **foo**\n- Your Ansible Galaxy name is **someperson**\n- Your role is on GitHub at **github.com/someperson/ansible-foo**\n- Your tests are on GitHub at **github.com/someperson/test-suite**\n- Your test is located in the **ansible-foo** directory in the **test-suite repo**\n\n**NOTE:** Galaxy and GitHub are not necessary for any of this, it is\njust an example.\n\nLet's create a role locally and make it do the least amount possible just so\nwe can test it.\n\n.. code:: Bash\n\n  mkdir -p roles/someperson.foo/tasks \u0026\u0026 touch roles/someperson.foo/tasks/main.yml\n\nBasic test scaffold\n```````````````````\n\n``rolespec -n tests/ansible-foo`` to create a new test case for this role.\n\nInvestigate the hosts file\n``````````````````````````\n\nRoleSpec provides you with many variables and will also do find/replaces on\nyour test to replace placeholders at runtime. The ``hosts`` file is one spot\nwhere you will use a placeholder.\n\nYou will notice it contains nothing except ``placeholder_fqdn``. You can put\nit in 1 or more groups if you want. All instances of that string will get\nswapped to the real fully qualified domain name of the host.\n\nCreate a playbook\n`````````````````\n\n**You don't have to make one** because RoleSpec will generate one at runtime\nfor you. It will consist of running the play against the FQDN of the host\n(all groups essentially) and set the role you're testing.\n\nIf you want more control over the generated playbook then you can supply a\ncustom playbook of your own, it must be located at ``tests/ansible-foo/playbooks/test.yml``.\n\nInvestigate the test\n````````````````````\n\nOpen up ``tests/ansible-foo/test`` and read through it. It's commented and\nexplains everything.\n\n\nLint it\n```````\n\nYou can optionally run ``rolespec -l`` to run a linter against all of your\ntests. It will report back missing files, warn you if you're missing key things\nin your test script/yaml files and perform a syntax check.\n\n- RED results will cause your test to not run\n- YELLOW results are warnings that you should fix but are pretty ok to ignore\n- No results is great, that means everything is syntactically valid and well formed\n\nTry running it now, you may see some feedback.\n\nRun it\n``````\n\n.. code:: Bash\n\n  rolespec -r foo\n\nIt should run successfully and you'll be greeted with passing tests at the end.\nHere's a cool tip too, if you run ``bash -x rolespec -r foo`` instead you will\nbe provided with an in depth debug output as it runs.\n\nTest modes\n``````````\n\n**By default** RoleSpec will run the full setup/teardown stack. That includes\ntasks like installing system packages, installing Ansible, running the\nplaybook and the assertions. This is good to run when you want to do a full test.\n\nSometimes you just want to quickly iterate on a playbook and you don't care\nabout resetting all of the system packages, etc.. You can run RoleSpec\nin **playbook mode** like so:\n\n.. code:: Bash\n\n  rolespec -r foo -p\n\nLast up is **turbo mode** which skips everything except running your assertions.\nThis allows you to work against a static state of the system. Perfect for when\nyou want to write a bunch of assertions against a known setup. You can\nrun that like so:\n\n.. code:: Bash\n\n  rolespec -r foo -t\n\nWrapping things up\n``````````````````\n\nIf you ever get lost then run ``rolespec -h`` to bring up the help menu. Also\ndon't forget that each test is basically a standalone guide on how to use your\nrole. Feel free to use ``inventory/group_vars`` or ``meta/main.yml`` in your\ntest if you need to.\n\nExample test cases\n~~~~~~~~~~~~~~~~~~\n\nYou can view over 50 working examples in the\n`DebOps test suite \u003chttps://github.com/debops/test-suite\u003e`_.\n\nTest API\n~~~~~~~~~~~~\n\nSystem actions\n``````````````\n\nDo not use quotes when calling any system functions, they must be passed as\narguments.\n\n- ``install \u003cspace separated list of apt packages\u003e``\n- ``purge \u003cspace separated list of apt packages\u003e``\n- ``start \u003cservice name\u003e``\n- ``stop \u003cservice name\u003e``\n\nAnsible actions and assertions\n``````````````````````````````\n\n- ``install_ansible [branch=devel]``\n    - Installs a specific version of Ansible\n\nYou may optionally pass ``ansible-playbook`` arguments to any of the functions\nbelow.\n\n- ``assert_playbook_syntax``\n    - Performs just a syntax check\n- ``assert_playbook_runs``\n    - Performs a syntax check **and** runs the playbook once\n- ``assert_playbook_check_runs``\n    - Performs a syntax check **and** runs ansible in check mode **and** runs the playbook once\n- ``assert_playbook_idempotent``\n    - Re-runs the playbook checking for 0 changes\n- ``assert_playbook_idempotent_long``\n    - Re-runs the playbook checking for 0 changes with periodic output\n\nBasic assertions\n````````````````\n\nAdd an `!` as an optional last argument to any of the functions below to negate\nthem.\n\n- ``assert_in \u003ccommand output or string\u003e \u003csearch pattern\u003e``\n- ``assert_in_file \u003cpath\u003e \u003csearch pattern\u003e``\n- ``assert_path \u003cpath\u003e``\n- ``assert_permission \u003cpath\u003e \u003coctal permission\u003e``\n- ``assert_group \u003cspace separated list of groups\u003e``\n- ``assert_user_in_group \u003cuser\u003e \u003cgroup\u003e``\n- ``assert_running \u003cprocess name\u003e``\n- ``assert_monitoring \u003cprocess name\u003e``\n- ``assert_iptables_allow \u003cport or service name\u003e``\n- ``assert_url \u003cfull url\u003e [status code=200]``\n- ``assert_tcp \u003chostname\u003e \u003cport\u003e [return code=0]``\n- ``assert_exit_code \u003ccommand\u003e \u003cexpected exit code\u003e``\n\nAvailable variables\n```````````````````\n\n- ``ROLESPEC_ANSIBLE_INSTALL``\n    - The path where Ansible has been installed to\n- ``ROLESPEC_ANSIBLE_SOURCE``\n    - The address where Ansible has been cloned from\n- ``ROLESPEC_ANSIBLE_ROLES``\n    - The ``roles_path`` which gets set in ``ansible.cfg``\n- ``ROLESPEC_ANSIBLE_CONFIG``\n    - The path where ``ansible.cfg`` exists\n- ``ROLESPEC_LIB``\n    - The path where RoleSpec's libs exist\n- ``ROLESPEC_VERSION``\n    - The version of RoleSpec\n- ``ROLESPEC_RELEASE_NAME``\n    - The release name of the host's OS\n- ``ROLESPEC_FQDN``\n    - The fully qualified domain name of the host\n- ``ROLESPEC_TRAVIS``\n    - Is the host running on Travis-CI?\n- ``ROLESPEC_TRAVIS_ROLES_PATH``\n    - The path where roles are downloaded from Travis-CI\n- ``ROLESPEC_TURBO_MODE``\n    - Has turbo mode been enabled?\n- ``ROLESPEC_DEVELOPMENT_MODE``\n    - Has development mode been enabled?\n- ``ROLESPEC_ROLES``\n    - The path where roles are downloaded from Ansible Galaxy\n- ``ROLESPEC_ROLE``\n    - The name of the role as it exists on the file system\n- ``ROLESPEC_ROLE_NAME``\n    - The name of the role without any galaxy or repository prefix\n- ``ROLESPEC_TEST``\n    - The path of the test directory for the current role being tested\n- ``ROLESPEC_HOSTS``\n    - The path of its hosts file\n- ``ROLESPEC_META``\n    - The path of its meta file\n- ``ROLESPEC_PLAYBOOK``\n    - The path of its playbook file\n- ``ROLESPEC_SCRIPT``\n    - The path of its test file\n- ``ROLESPEC_POSTGRESQL_LIBS``\n    - A list of packages to purge before installing PostgreSQL\n- ``ROLESPEC_MYSQL_LIBS``\n    - A list of packages to purge before installing MySQL\n\nTest code style\n~~~~~~~~~~~~~~~\n\nUp to you but so far I'm digging this, each section gets separated by 2 lines:\n\n1. Header comments\n2. Source the RoleSpec lib\n3. Stop service / purge apt packages\n4. Install apt packages\n5. Any type of setup code that needs to happen before the playbook is ran\n6. ``install_ansible [version]``\n7. ``assert_playbook_runs`` and optionally ``assert_playbook_idempotent``\n8. All of your tests, separated by 0 or 1 lines\n9. Any cleanup code that needs to happen, such as stopping a server\n\nDo you want to contribute?\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSounds great, check out the\n`contributing guide \u003chttps://github.com/nickjj/rolespec/blob/master/CONTRIBUTING.rst\u003e`_\nfor the details.\n\nAuthor\n~~~~~~\n\n**Nick Janetakis**\n\n- Email: nick.janetakis@gmail.com\n- Twitter: `@nickjanetakis \u003chttps://twitter.com/nickjanetakis\u003e`_\n- GitHub: `nickjj \u003chttps://github.com/nickjj\u003e`_\n\n.. |Build status| image:: http://img.shields.io/travis/nickjj/rolespec.svg?style=flat\n   :target: https://travis-ci.org/nickjj/rolespec\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnickjj%2Frolespec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnickjj%2Frolespec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnickjj%2Frolespec/lists"}