Python で書く代数 - pytest 編

テストについて

前回は群を Python で書いてみました. 群は公理というルールを使って定義されていました. この公理を Python で表現するのに, テストが使われていました.

Python でのテストと言えば unittest や nose が有名ですが, pytest という新しいテストライブラリのことを知ったので, これを使ってみました.

この pytest の大きな特徴はテストメソッドで使用する値を簡単にパラメータ化できることです.

人間がテストケースを考える場面では, ただ闇雲に適当な値を渡すのはテストとしては良いものではありません. とりあえずで値を列挙すると無駄な意味の無いテストケースを作ってしまうことが多く, テストの実行自体に時間がかかります. そしてエラーが起きたとしても, そのエラーがいったいどんな原因によって起きたのか考えないといけません. (個人的にはあまり頭を使わず安直にエイヤというのは好きではないです.)

ランダムな値を渡す手法は fuzzing と呼ばれていて, 人間がテストケースを想定する範囲の外にあるバグを見付けるのに使われるそうです. 詳しくはこのスライド http://www.slideshare.net/TokorotenNakayama/fuzzing-pyfes を見ると良いと思います.

群の公理は「群の任意の元」という表現を含んでいるため, 具体値を使用するテストでは無限の時間が必要になってしまい, 全てをテストすることは不可能です. なので, 今回の Group クラスのサブクラスのテストとしては, パラメータ化されたテストで特殊値の付近だけをテストすることにしました. 例えば, Integer クラスでは整数を表現しているので特殊値として 0 があります. 0 の付近の値でテストが通れば, 高い確率で他の値でも動くものと期待できます.

pytest によるテスト

pytest は easy_install もしくは pip コマンドでインストールできます. ここでは virtualenvwrapper を使って, pytest という名前の個別の環境にインストールしています.

pip, virtualenv, virtualenvwrapper についてはこの記事 http://d.hatena.ne.jp/rudi/20110107/1294409385 に詳しく載っています. そちらを参照してください.

$ echo $VIRTUALENV_USE_DISTRIBUTE
True
$ mkvirtualenv pytest
(pytest)$ pip install pytest

テストケースの実行は pytest コマンドで行います.

$ pytest tests/grouptest.py

pytest にテストケースとして認識させるには, メソッドの先頭を test にすれば良いようです. ここは nose などと仕様を合わせているようです. (ドキュメントには明記されていなかった気がしますが.)

肝心のテスト関数にパラメータを渡すところはデコレータを使用して以下のように書きます.

@pytest.mark.parametrize('cls', groups)
def test_ZERO(cls):
    cls.ZERO
    return True

pytest.mark.parametrize の第1引数にはテスト関数の引数の名前を, 第2引数にはテスト関数に渡す値の list を入れます. この list の部分を generator で書いてしまうとエラーが出て, テストが実行できません. どうやら pytest が最初にテストケースをカウントしていて, 長さが測れるものでないといけないようです. 最初エラーメッセージの意味が分からず, 長いことハマっていました.

複数の引数を持つテスト関数を書く場合は以下のようにします.

integer_values = [Integer(i) for i in range(-2, 3)]

@pytest.mark.parametrize(
    ('a', 'b'),
    [(a, b) for a in integer_values for b in integer_values]
)
def test_equals_symmetric_law(a, b):
    if a == b:
        assert b == a

第1引数が tuple になり, パラメータの値も tuple になっています.

今回使用したテストコードの完全なソースコードは https://bitbucket.org/cocoatomo/pyarith/changeset/08e381828aeb#chg-tests/grouptest.py を参照してください.

過去のリビジョンの URL を張っているのは, 実は現在は別の形式にしているからです. その話はまた次の記事で書こうと思います.

まとめ

この記事では pytest を使ってテスト関数の引数をパラメータ化し, そこに入れる値をリストとして書きました. これによって同じテスト関数を色々な値で実行できるようになりました.

pytest にはまだまだ機能があるので, 興味がある方は本家のドキュメントを読んでみてください.

Comments

blog comments powered by Disqus

Licenses