从Ansible角色的单元测试到Travis CI

我写了续篇文章,本文中的设置步骤已经自动化。
这是使用git-flow的Ansible角色进行测试驱动开发的流程。

我将解释如何编写易于进行单元测试的Ansible角色,并将其上传到Travis CI。本文中提到的方法是我最近想到的,它可以处理多个测试用例,并且非常方便地在角色单独开发。

注意:
Ansible的安装请参考另一篇文章。

$ pip install ansible.

创建示例卷

注意
可从以下网址获取本文的示例样本
https://github.com/tumf/ansible-unit-test-sample

例如,可以使用ansible-galaxy命令创建sample角色的模板。

$ ansible-galaxy init sample                      

将会生成如下类型的文件。

├── README.md
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
└── vars
    └── main.yml

考研的准备

在这里创建一个名为tests的目录,并准备用于单元测试的playbook等。将playbook命名为test.yml,并按以下方式编写。

---
- hosts: 127.0.0.1
  connection: local
  tags:
    - case-1
  vars:
    ansible_unit_test: True
  roles:
    - role: ../..

首先,先看前两行…

- hosts: 127.0.0.1
  connection: local

这个部分是为了在本地测试而设定的约定。以下是两个关键点。

1. 在Playbook中添加一个名为case-1的标签。

  tags:
    - case-1

這將是測試案例的名稱。(案例1僅為示例,請給予易於理解的名稱)

设定ansible_unit_test的变量。

  vars:
    ansible_unit_test: True

這是為了在測試時跳過想要跳過的任務而準備的,只需在when條件中加入ansible_unit_test以防止執行。

接下来,如果创建的角色依赖于其他角色,请在tests/requirements.yml中按照以下方式编写…

- src: tumf.systemd-service

使用 ansible-galaxy 命令进行安装。

$ ansible-galaxy install -r tests/requirements.yml -p tests/roles

易于测试的规则书写方式

为了方便测试程序,我们将对输出路径进行设置和跳过任务的指定。

装饰输出路径

创建一个任务来将角色的任务安装到tests/cases/测试用例名下。创建一个名为prefix_dir的变量,并将角色中的文件路径全部加上修饰。

首先,将以下内容写在默认的角色上…

prefix_dir: ""

在模板的dest等输出路径的前面加上prefix_dir。

- template: src="default.j2" dest="{{ prefix_dir }}/etc/default/sample.j2"
  notify: reload systemd

跳过任务的指定

为了跳过那些只能在实际环境中执行的命令或任务,我添加了”when: is not ansible_unit_test” 来阻止它们在测试时执行。我曾想在运行ansible-playbook时加上参数“-C”,但在不同的执行环境中,有时会导致模块无法加载(因为我在OSX上对生产Linux进行测试)。

我們將在下方加上 `when: not ansible_unit_test`。

- service: name="sample" state=started enabled=yes
  when: not ansible_unit_test

默认情况下,我们将ansible_unit_test设置为False,以备实际部署使用时。

ansible_unit_test: False

考虑到你的需求,我提供下面的翻译:
测试执行脚本

接着,我们将准备一个名为tests/run的测试运行脚本。

#!/bin/bash

usage_exit() {
        echo "Usage: $0 [-w] name" 1>&2
        exit 1
}

check="-C"
while getopts wh option
do
    case $option in
        w)
            check=""
            ;;
        h)
            usage_exit
            ;;
    esac
done

shift $((OPTIND - 1))

mkdir -p tests/cases
cases=$(ls tests/cases)
if [ ! -z $1 ];then
    cases=$1
fi
errors=0

for case in $cases
do
    out=$(ansible-playbook ./tests/test.yml -i 127.0.0.1, -t $case -D $check -e prefix_dir="cases/${case}")
    result=$?
    if echo $out|tail -n 1 |grep -E "changed=0\s+unreachable=0\s+failed=0" >/dev/null
    then
        echo -n "."
    else
        echo $case
        echo
        echo "$out"
        errors=$(( errors+1 ))
    fi
done

if [ $errors -eq 0 ]
then
    echo " ok"
else
    echo "${errors} error(s)"
fi
exit $errors

进行考试

考试可按照以下方式进行执行。

$ ./tests/run case-1            
case-1


PLAY [127.0.0.1] 
GATHERING FACTS *************************************************************** 

ok: [127.0.0.1]

TASK: [../.. | template src="default.j2" dest="{{ prefix_dir }}/etc/default/sample"] *** 
--- before: cases/case-1/etc/default/sample
+++ after: /Users/tumf/tmp/sample/templates/default.j2
@@ -0,0 +1 @@
+default test

changed: [127.0.0.1]

TASK: [../.. | service name="sample" state=started enabled=yes] *************** 
skipping: [127.0.0.1]

NOTIFIED: [../.. | reload systemd] ******************************************** 
skipping: [127.0.0.1]

PLAY RECAP ******************************************************************** 
127.0.0.1                  : ok=2    changed=1    unreachable=0    failed=0   
1 error(s)

case-1是在tests/test.yml文件中通过标签指定的测试用例。在这个阶段,请不要关心最后的错误。请查看上面的输出以确认是否正确生成。只有在正确生成之前,我们才会对播放手册进行修正。

创建测试用例。

将由测试用例case-1生成的文件tests/cases/case-1/etc/default/sample作为正确的案例注册到测试用例中。按照以下方式执行。

$ ./tests/run -w case-1
(略)

如果能够正确执行(应该可以执行),tests/cases/case-1/etc/default/sample会被实际写入。下次将按以下方式执行测试。

$ ./tests/run  case-1   
. ok

注意:如果省略案例名称,则会扫描tests/cases文件夹并执行所有案例。通常情况下,这更方便。
$ ./tests/run
. 通过

增加测试用例

在需要的情况下,我们会增加测试用例。
以下是一个简单的例子,只是改变了变量”var”。

---
- hosts: 127.0.0.1
  connection: local
  tags:
    - case-1
  vars:
    ansible_unit_test: True
    var: var of case-1
  roles:
    - role: ../..
- hosts: 127.0.0.1
  connection: local
  tags:
    - case-2
  vars:
    ansible_unit_test: True
    var: var of case-2
  roles:
    - role: ../..

执行下列命令…

$ ./tests/run case-2

如果正常运行,将其注册为测试用例。

$ ./tests/run -w case-2

以后,将同时使用以下命令进行case-1和case-2的检查。

$ ./tests/run           
.. ok

真简单呢。

在考试中引发错误

当在case-1的测试案例已经被注册的状态下,改变var变量应该会导致与测试案例之间出现差异。一旦检测到这种差异,就会引发错误。

我們將根據以下方式修改遊戲策略。

  vars:
    ansible_unit_test: True
    var: var of case-one # ここを変えた

我要执行测试,然后会报告以下错误。

$ ./tests/run 
case-1


PLAY [127.0.0.1] ************************************************************** 

GATHERING FACTS *************************************************************** 

ok: [127.0.0.1]

TASK: [../.. | file state="directory" path="{{ prefix_dir }}/etc/default"] **** 

ok: [127.0.0.1]

TASK: [../.. | template src="default.j2" dest="{{ prefix_dir }}/etc/default/sample"] *** 
--- before: cases/case-1/etc/default/sample
+++ after: /Users/tumf/tmp/sample/templates/default.j2
@@ -1 +1 @@
-default test var of case-1
+default test var of case-one

changed: [127.0.0.1]

TASK: [../.. | service name="sample" state=started enabled=yes] *************** 
skipping: [127.0.0.1]

NOTIFIED: [../.. | reload systemd] ******************************************** 
skipping: [127.0.0.1]

PLAY RECAP ******************************************************************** 
127.0.0.1                  : ok=3    changed=1    unreachable=0    failed=0   
.1 error(s)

如果这个差异符合预期,请在./tests/run -w case-1中编写新的测试用例。如果有错误,请进行修正。

我会在重复测试的过程中完成预定的剧本。

Travis CI 在中国

做到这一步,我认为你会想要执行使用CI进行的自动化测试。如果是Travis CI的话,只需按照以下方式编写.travis-ci.yml文件即可。

language: python
python:
- '2.7'
install:
- pip install ansible
#- ansible-galaxy install -r tests/requirements.yml -p tests/roles
before_script:
- ansible --version
- ansible-playbook --syntax-check ./tests/test.yml -i ./tests/hosts
script:
- ./tests/run

以下这部分被注释掉了。

#- ansible-galaxy install -r tests/requirements.yml -p tests/roles

当这个角色不依赖于其他角色(也就是说,当tests/requirements.yml为空时),ansible-galaxy会报错,因此我们将其注释掉。如果有依赖的角色,并且编写了tests/requirements.yml文件,请取消注释。

以上

非常感谢您提供的这篇文章,对我非常有帮助!这篇文章介绍了如何在TravisCI上运行Ansible的role单元测试。链接:http://qiita.com/djyugg/items/627aa88e02422612f164

bannerAds