🐈

Relearning testinfra 2025

に公開

インフラ構築結果のテストのため 2017年に一度触った testinfra の再学習です。職業プログラマではないので、単体テストフレームワークには詳しくありません。趣味プログラマとしては自動化できるところに嬉しさは感じるので、なるべく自分を嬉しい状態に置けるようセットアップはしておきたいものです。

今回も環境は Windows WSL 上の Ubuntu です。

$ head -1 /etc/os-release
PRETTY_NAME="Ubuntu 24.04.2 LTS"

セットアップ

Ansible の 時に使った pipx でインストールしてもコマンドラインからの実行には問題ないようですが、VS code のテスト支援機能で pytest を python3 -m pytest で呼び出そうとするようなのでそのままでは恩恵を受けられません。
調整事項は少ないほうがいいので root 権限でのセットアップを選択したかったのですが、のちに Windows への接続を試みた時に接続できないことが分かったので venv での環境構築とします。

python3 -m venv .venv
. .venv/bin/activate
pip install pytest pytest-testinfra pywinrm 

最初のテスト

pytest がテストを見つける方法に沿うよう、 *.py ファイルを作ります。

要素 デフォルト検出ルール
ファイル名 test_*.py, *_test.py
クラス名 Test*(大文字 T で始まる)
関数名 test_*(小文字で始まる)

テストコードを書くときには VS Code が Python のインタプリタを .venv 以下から選択しているようにしてください。
どのマシンをテストするのかは、testinfra のドキュメント、

  • デフォルトではローカルマシンで実行する
  • --hosts 引数、またはモジュール変数 testinfra_hosts に設定したマシンに ssh 接続して実行する

に従って、testinfra_hosts を設定します。

import testinfra

testinfra_hosts = ["username@10.0.0.10"]

def test_ping_localhost(host):
    cmd = host.run("ping -c 1 localhost")
    assert cmd.rc == 0, "Ping to localhost failed"

デフォルトのコネクションメソッドは paramiko で、SSH に関連する警告が出ることがあります。paramiko 自身に問題があるわけではなさそうですが、これは次に解消します。

特定ユーザ(rootやoracleなど)でのコマンド実行結果をテストするために sudo メソッドが使用できるよう、テスト対象 Linux で sodures 設定をしておく必要が出てくるでしょう。これはテスト用に接続してくるアカウントの作成と合わせて、初期の OS インストールや Ansible の範疇だと思います。

コネクションメソッドを指定する

環境の前提を WSL 上の Ubuntu にしたので、

  • テスト実行者の共通鍵をテスト対象のマシンのアカウントに配置する
  • テスト実行のたびに共通のユーザとパスワードを指定する

のどちらかになります。パスワードの記載個所は少ないほうがいいので公開鍵を配置します。

接続先の情報を Ansible のインベントリから取得することもできるようですが、これでは Ansible の用意ができるまでテストを実行できません。また、インベントリの設定ミスを検出することもできないので、testinfra が使用するテスト対象の一覧は Ansible のインベントリとは別に用意したほうが好ましいと思います。できればプレイブックを書く人とテストコードを書く人を別にしたいところです(運用フェースで一人しか残らないとわかっていても)。

コネクションメソッドはホスト名の指定に ssh:// をつけてもいいのですが、これを省略するために設定ファイルを用意します。テストコードを置くディレクトリの最上位に配置するとよいでしょう。

pytest.ini
[pytest]
addopts = --connection=ssh

テストコードの起点を指定する

VS code はワークスペースを起点に pytest を起動するようです。テスト対象のフォルダは指定できますが、そこは pytest.ini のある場所ではありません。pytest.ini の場所から実行するように指定します。

.vscode/setting.json
{
    "python.testing.cwd": "${workspaceFolder}/src/testinfra"  // pytest.ini のある場所
}

これでPythonを知らなくても、Linuxのコマンドを打つのに抵抗があっても、VS Code さえ使えればテストを実行するだけの役割で要員を活用できます(自動化されるまでは)。

少しだけ pytest に踏み込んでみる

モジュール変数 testinfra_hosts

テスト対象には testinfra_hosts というモジュール変数を設定する、のは良いのですが、これがどこから出てきているのかソースを検索してみます。

https://github.com/pytest-dev/pytest-testinfra/blob/dca7b056607bcbc9f8b4b7e9051cbc86f8cb956a/testinfra/plugin.py#L112-L134

ここで、pytest のオプション --hosts が指定指定されていない場合の値をモジュール変数から取得していることが想像できます。つまりこの変数名は変えられません。

引数 host

初めてテストコードを見る人には引数の host が魔法のキーワードに見えてしまい、落ち着きません。コード上矛盾がなければ、引数の名前をほかのものに変えてもよさそうですがそうはなりません。これには、

testinfra の正体は pytest に fixture host を追加するものである。

ことに理解が必要です。 fixrute の一覧は以下のコマンドで確認できます。

pytest --fixtures

定義個所を見ると host という名前の関数が特別な属性で定義されていることが見えます。

@pytest.fixture(scope="module")
def host(_testinfra_host: testinfra.host.Host) -> testinfra.host.Host:
    return _testinfra_host

fixture って?

改めて言い直すと、Python の単体テストフレームワーク pytest にインフラ構築のテストをするにあたって有用な機能を追加するのが testinfra です。テストの実行は pytest の仕組みを使います。

このことが、インフラ構築のテストフレームワーク選定と説明に苦労するところであり、最初から

インフラの構築結果を python でテストする

と説明すれば、その知名度から管理職の理解も得られるでしょうし、利用者の理解の順番も前後しなくてすむでしょう。

ともかく、実際にテストを書いてもらうには fixture という言葉に慣れてもらわないと苦しみます。まずは説明を理解できるか確認します。(ここから先はお気に入りの AI に説明を求めていいと思います。)

https://docs.pytest.org/en/latest/how-to/fixtures.html#requesting-fixtures

At a basic level, test functions request fixtures they require by declaring them as arguments.

とあるように、基本的にはテスト関数に渡された引数を fixture として使おうとします。

平坦に説明すると「fixture はテストに使用するパラメータをほかの個所で定義するしくみ」で、テスト用関数の引数の名前と同じ名前の fixture を使用する仕組みになっています。

pytest の機能の活用

  • @pytest.mark.skip
  • @pytest.mark.parametrize

などはよく使います。一覧は以下で確認。

pytest --markers 

テスト対象のサーバは testinfra_hosts で定義して引数 host で参照しますが、テスト対象の項目(起動してあってほしいサービス名の一覧など)は独自の fixture や parametrize で指定することになるとおもいます。

ここまで理解したらあとは引数 host で使用可能なメソッドを testinfra のマニュアルで確認し、その他パラメータ指定の効率化やテストモジュールの書き方は pytest のマニュアルや豊富なサンプル、AI の生成結果を参考に進められるものと思います。

Discussion