ちょっと野良Ansibleモジュールを作る感じになったので、 「せっかくだからある程度テストコードも書いてみよう」と思い、 あれこれ試行錯誤している最中のメモ。

前提

課題1: AnsibleModuleexit_json , fail_json がSystemExitを返すのでテストが死ぬ

内容

Ansibleモジュールを定義するときには、最終的に AnsibleModule オブジェクトを用意して、

  • パラメーターの読み込みとチェック
  • 正否の結果をAnsibleに通知

を行うことになっている。

で、後者の「正否判定の通知」は exit_json , fail_json を使って結果を伝えているのだが、 Ansibleの挙動上、リモート先でのモジュールは1個のコマンドとして実行され、 どちらのコマンドも最後は sys.exit が呼ばれて終了する。

py.testでモジュール実行自体をテストしようとすると、何度も呼び出す必要が出てくるのだが、 sys.exit だ途中に入るため、テストが回らずに死んでしまう。

対策

import pytest
import local_module  # テスト対象のモジュール。ちゃんと処理内で、パラメーターのチェックをする

def test_no_args():
    """何もパラメーターが無いと、リターンコード1が返る
    """
    with pytest.raises(SystemExit) as exited:
        local_module.main()
    assert exited.value.code == 1

sys.exit()SystemExit を送信するので、これをwithコンテキストでキャッチして、 中身を検証する構成を基本としてみている。

課題2: テスト中にパラメーター読み込みで結果が変わらず、テストが機能しない

このテストが全てパスしてくれず、どちらかがNGになる模様。

import io
import json
import pytest
import local_module  # テスト対象のモジュール。ちゃんと処理内で、パラメーターのチェックをする

def test_no_args():
    """何もパラメーターが無いと、リターンコード1が返る
    """
    with pytest.raises(SystemExit) as exited:
        local_module.main()
    assert exited.value.code == 1

def test_with_args(monkeypatch):
    """nameパラメーターがあれば、リターンコード0が返る
    """
    with pytest.raises(SystemExit) as exited:
        stdin = json.dumps({'ANSIBLE_MODULE_ARGS': {'name': 'hello'}})
        monkeypatch('sys.stdin', io.String(stdin))
        local_module.main()
    assert exited.value.code == 0

なにもカスタマイズせずに AnsibleModule を呼び出すと、 内部で _load_params というパラメーター読み取り用の関数が呼ばれる。 これがモジュール内変数 _ANSIBLE_ARGSglobal 宣言してキャッシュに使っており、 ユニットテストなどで、複数回 _load_params を呼んでも最初の1回分しか使用されないらしい。

対策

_ANSIBLE_ARGS にもmonkeypatchを当てる。以上

import io
import json
import pytest
import local_module  # テスト対象のモジュール。ちゃんと処理内で、パラメーターのチェックをする

def test_no_args(monkeypatch):
    """何もパラメーターが無いと、リターンコード1が返る
    """
    monkeypatch('sys.stdin', None)
    with pytest.raises(SystemExit) as exited:
        local_module.main()
    assert exited.value.code == 1

def test_with_args(monkeypatch):
    """nameパラメーターがあれば、リターンコード0が返る
    """
    monkeypatch('sys.stdin', None)
    with pytest.raises(SystemExit) as exited:
        stdin = json.dumps({'ANSIBLE_MODULE_ARGS': {'name': 'hello'}})
        monkeypatch('sys.stdin', io.String(stdin))
        local_module.main()


DjangoCongress JP 2019に行ってきた

2019-05-19(Sun) / Tech

直近でゴタゴタしてて勉強会らしいのにも行けてなかったので、久方ぶりに参加した。ちなみに、2年連続。

直近の自分のDjango事情としては、社内向けのツールにDjangoを投入してるぐらいで、ほぼ使ってないけどいいでしょ。

(遅刻したので、オープニングと最初のトークは不参加。相変わらず最初から参加できない病がひどい …

>>read more


Errbot 6.0.0の更新内容整理

2019-04-28(Sun) / Tech

Pythonのコードを書く時間を取れなくなっている間に、Errbotの6.0.0がリリースされました。

>>read more


Laravel JP Conferenceに参加して、LT枠で喋ってました

2019-02-21(Thu) / Tech

https://conference2019.laravel.jp/

「小ネタ喋って懇親会」メソッドを実践してみようと思い、 LTのCfPを出したら採択されたので、 勢いで参加しました。

>>read more


sphinx-revealjsを作ってSphinxCon JP 2018でトークした

2018-12-10(Mon) / Tech

少し時間が経過してしまったけれど、sphinx-revealjsを0.4.0へ更新したので、 ちゃんとブログとして書くことに。

SphinxCon JP 2018

11月の終わりに SphinxCon …

>>read more


Ansible Night in Tokyo 2018.12

2018-12-07(Fri) / Tech

今回もブログ枠に滑り込んで参加。

メイン1: Ansible Fest サマリ & 事例関連の報告

スライド:https://www.slideshare …

>>read more


技術書典5に行ったログ

2018-10-08(Mon) / Tech

社内のSlackで「あるよ!」ってメッセージを書いたら、なんか増えて最終的に7人で行くことに。

今年は開場時刻ぐらいの11時に到着、会場入りが12時前。 整理券制じゃなかったけど、一時離脱が13時前なので、いいペースで回れたと思う。

購入リスト …

>>read more


Golangの素振りを兼ねて、Mackerelのプラグインを作ってみた

2018-10-07(Sun) / Tech

社内で話題に登ったのをきっかけに、「ドメインの有効期限をチェックする」Mackerelのチェックプラグインを作りました。

https://github.com/attakei/mackerel-plugin-check-whois

これぐらいの記述なら普段使ってるPythonのほうが楽そうな気もしたが、 素振りのサイズとして手頃なのとMackerel本体が書かれているという理由で …

>>read more


Ansible Night in Tokyo 2018.09

2018-09-25(Tue) / Tech

年末以来の参加。 今回はブロガー枠ですが、すみません遅刻しました。

テーマは、「GitLab」と「インフラCI」

GitLab で実現する Ansible …

>>read more


July Tech Festa 2018感想

2018-07-29(Sun) / Tech

  • インフラ系サーバサイドエンジニアが、
  • インフラ系中心のカンファレンスで
  • フロント系技術の登壇をする

という面白い経験をしました。

多分、発表内容寄りの感想は自社のエンジニアブログに書く想定

「発表する」ということ

過去最長の尺となる45分枠だったが、 CfP出した時点で …

>>read more