以前、AnsibleロールのユニットテストからTravis CIまでという記事を書きました。
この方法にgit-flowを加えた**テスト駆動開発(TDD)**がいい感じに出来るようになったのでまとめます。
新規Ansibleロールの作り方からTDDしながら機能追加していく流れを、
docker-machineのインストールを例に説明します。
Ansibleロール開発プロジェクトを始める
ロールを開発するgitリポジトリ名をansible-role-docker-machineとします。
このような名前にする理由は、将来Ansible Galaxyに登録される時にansible-role-部分は無視されるからです。(Ansible Galaxyではロール名がユーザー名.docker-machineとなります)
ロールの雛形ファイルを生成する
以下のコマンドでロールの雛形を自動生成します。
ansible-galaxy init ansible-role-docker-machine
gitリポジトリを作ってコミットします
cd ansible-role-docker-machine
git init
git add .
git commit -a -m 'Generated by `ansible-galaxy init ansible-role-docker-machine`'
AnsibleロールのユニットテストからTravis CIまでに書いた
セットアップ手順を自動化するスクリプトでユニットテストをインストールします。
curl -sL https://raw.githubusercontent.com/tumf/ansible-unit-test/master/ansible-role-test-init |bash
git add .
git commit -a -m 'Generated by `ansible-role-test-init`'
以上で雛形ファイルの準備は完了です。
docker-machineのインストールを実装する
docker-machineは公式のドキュメントにある通り、以下のコマンドでインストールできます。
$ curl -L https://github.com/docker/machine/releases/download/v0.5.5/docker-machine_linux-amd64 >/usr/local/bin/docker-machine && \
  chmod +x /usr/local/bin/docker-machine
つまり、https://github.com/docker/machine/releases/download/v0.5.5/docker-machine_linux-amd64から/usr/local/bin/docker-machineをダウンロードして実行権限をつける...ということが当面の目的(Spec)となります。
テストを**テストファーストで(!!重要!!)**書きます。ここではテストケース名を単にinstallとします。
テストケースは以下のようにしてtests/cases/install以下に作成します。
mkdir -p tests/cases/install/usr/local/bin
curl -L https://github.com/docker/machine/releases/download/v0.5.5/docker-machine_linux-amd64 >tests/cases/install/usr/local/bin/docker-machine
chmod +x tests/cases/install/usr/local/bin/docker-machine
またこのテストケースを実現するためのプレイブックをtests/test.ymlに記述します。(雛形のままです)
---
- hosts: 127.0.0.1
  connection: local
  tags:
    - install
  roles:
    - role: ../..
  tasks:
    - debug: msg="The test case is succeeded."
これでテストの準備完了です。早速テストを実行してみます。
    $ ./tests/run
    * Test: install
    PLAY [127.0.0.1] **************************************************************
    GATHERING FACTS ***************************************************************
    ok: [127.0.0.1]
    TASK: [debug msg="The test case is succeeded."] *******************************
    ok: [127.0.0.1] => {
        "msg": "The test case is succeeded."
    }
    PLAY RECAP ********************************************************************
    127.0.0.1                  : ok=2    changed=0    unreachable=0    failed=0
    Binary files tests/cases/install/usr/local/bin/docker-machine and tests/outputs/install/usr/local/bin/docker-machine differ
    TestCase: install ERROR
    1 error(s)
テストファーストなので期待通り失敗しました。これを成功させることが当面の目標となります。
ロールを実装する
ロールを実装します。tasks/main.ymlにタスクを記述します。
- name: Create Install Directory
  file: path="{{ ansible_unit_test_prefix_dir }}/usr/local/bin" state=directory recurse=yes
- name: Install Docker Machine
  get_url: >
    url="https://github.com/docker/machine/releases/download/v0.5.5/docker-machine_linux-amd64"
    dest="{{ ansible_unit_test_prefix_dir }}/usr/local/bin/docker-machine"
    mode="755"
簡単に上記のようにしました、ポイントは出力先をテスト先に向けるためのansible_unit_test_prefix_dirの記述です。
テストすると以下のように無事テストがパスするようになりました。(DockerMachineをダウンロードするため時間がかかります)
    -> % ./tests/run
    * Test: install
    PLAY [127.0.0.1] **************************************************************
    GATHERING FACTS ***************************************************************
    ok: [127.0.0.1]
    TASK: [../.. | Create Install Directory] **************************************
    changed: [127.0.0.1]
    TASK: [../.. | Install Docker Machine] ****************************************
    changed: [127.0.0.1]
    TASK: [debug msg="The test case is succeeded."] *******************************
    ok: [127.0.0.1] => {
        "msg": "The test case is succeeded."
    }
    PLAY RECAP ********************************************************************
    127.0.0.1                  : ok=4    changed=2    unreachable=0    failed=0
    TestCase: install OK
    1 test case(s) passed
ここまで一度gitにコミットしておきましょう
git-flowでfeatureを追加
粗くではありますが動くようになりましたので、git-flowでfeatureを追加していきます。
まず、git-flowを全てデフォルト設定で初期化します
git flow init -d
さて、やりたいことは沢山ありますが、まずはTDDを快適に回せるようにテスト時にDockerMachineを毎回ダウンロードするのを抑制したいと思います。
実現方法としてダウンロードURLをdocker_machine_urlのような変数として、
テスト時はこれを上書きするようにします。
まず、git-flowでfeatureを開始します
git flow feature start add_docker_machine_url
ブランチがfeatures/add_docker_machine_urlに切り替わります。
次にtaskに直書きしているURLを変数に書き換えます。
- name: Install Docker Machine
  get_url: >
    url="{{ docker_machine_url }}"
    dest="{{ ansible_unit_test_prefix_dir }}/usr/local/bin/docker-machine"
    mode="755"
デフォルト値として以下を追加します。
docker_machine_url: https://github.com/docker/machine/releases/download/v0.5.5/docker-machine_linux-amd64
テスト時はどこかもっと軽い物をダミーとして落とすことにします。
どこでもいいのですが私はGistを利用しました。
これをテスト側でセットします
- hosts: 127.0.0.1
  connection: local
  tags:
    - install
  roles:
    - role: ../..
    docker_machine_url: https://gist.githubusercontent.com/tumf/579c84e276b6d2d88e09/raw
  tasks:
    - debug: msg="The test case is succeeded."
これでテスト時のみ軽いGistからのダミーファイルダウンロードに切り替わるはずです。
早速テストしてみます。
-> % ./tests/run
* Test: install
PLAY [127.0.0.1] **************************************************************
GATHERING FACTS ***************************************************************
ok: [127.0.0.1]
TASK: [../.. | Create Install Directory] **************************************
changed: [127.0.0.1]
TASK: [../.. | Install Docker Machine] ****************************************
changed: [127.0.0.1]
TASK: [debug msg="The test case is succeeded."] *******************************
ok: [127.0.0.1] => {
    "msg": "The test case is succeeded."
}
PLAY RECAP ********************************************************************
127.0.0.1                  : ok=4    changed=2    unreachable=0    failed=0
Binary files tests/cases/install/usr/local/bin/docker-machine and tests/outputs/install/usr/local/bin/docker-machine differ
TestCase: install ERROR
1 error(s)
上記の用にテストは失敗します。最初にテストケースinstallを作った時においた本物のバイナリとGistのダミーファイルが違うと言われています。
この結果は正しいので、テストケースの方を修正します。
このような場合、テストケースの修正は以下のコマンドで上書きできます。
-> % ./tests/run -w install
テストが正しく通るようになりました。
-> % ./tests/run
* Test: install
PLAY [127.0.0.1] **************************************************************
GATHERING FACTS ***************************************************************
ok: [127.0.0.1]
TASK: [../.. | Create Install Directory] **************************************
changed: [127.0.0.1]
TASK: [../.. | Install Docker Machine] ****************************************
changed: [127.0.0.1]
TASK: [debug msg="The test case is succeeded."] *******************************
ok: [127.0.0.1] => {
    "msg": "The test case is succeeded."
}
PLAY RECAP ********************************************************************
127.0.0.1                  : ok=4    changed=2    unreachable=0    failed=0
TestCase: install OK
1 test case(s) passed
上手く行ったようなので、コミットしてadd_docker_machine_urlのfeatureを終了します。
git commit -a -m 'Impliments add_docker_machine_url feature'
git flow feature finish add_docker_machine_url 
これで、developブランチに戻りました。featureの完了ですお疲れ様でした!
以上のように、ローカルマシン上で手軽にテストを繰り返すことにより安心してAnsibleロールに機能追加することができるようになりました。
Ansibleのユニットテストは従来のVagrant等を利用したテストに変わるものではなく、
あくまでも、開発中に不具合が紛れ込む可能性を防ぎ開発効率を上げる1手です。
今回作ったUnitTesting Framework for Ansible rolesはリンク先に公開していますので不具合やご要望などありましたらPRやIssueをお気軽に!