python docstring内にテストを記述


doctestモジュールのdoctest.testmod()関数はdocstring内にテストを記述し、verboseモードで実行することでテストが実行される

今まではこう書いてたところ

sample.py
def twice(n):
    """ 引数を 2 倍して返す関数
    >>> twice(8)
    16
    >>> twice(1850923)
    """
    return n * 2


'''TEST
twice(8)
 # 実行結果
 #16
twice(1850923)
 # 実行結果
 #3701846
'''# ←ここのトリプルクォートを'TEST'って書いてあるところまで引き上げてやることでtwice(8), twice(1850923)を実行する

TEST下のトリプルクォートをTESTのところまで上げてやるとdocstring的な複数行コメントアウトが外れ、実行したときにtwice関数が実行されてテストされる。
そのTESTってやつは関数より下にあるから、見た目が悪い。
できることならdocstringにまとめたい。

doctestモジュールを使う

しかしdoctestモジュールを使えばdocstring内に記述できて、他のモジュールから実行したときに実行させない、器用なことができる

doctest_sample.py
def twice(n):
    """ 引数を 2 倍して返す関数
    >>> twice(8)
    16
    >>> twice(1850923)
    3701846
    """
    return n * 2


print('name?>>', __name__)
if __name__ == "__main__":
    doctest.testmod()

print('EOF')

実行結果を以下の4つの方法で検証

  1. ipython
    1. %run doctest_sample -v
    2. %run doctest_use.py -v
  2. sublimetext
    1. sublimetext上でdoctest_sample.pyを実行
    2. sublimetext上でdoctest_use.py.pyを実行 > doctest_use.py.pyの内容はimportを行うだけ >>python:doctest_sample.use.py import doctest_sample

ipython上でpythonファイルを実行するときは'%run 'でrunする。
doctest.testmod()を使うときはverboseモード-vで実行する。

sublimetext上で実行するときはctrl+bでbuildする。
verboseモードの実行はデフォルトでは入ってない(嘘か本当か未検証)のでbuildシステムを作ってUserディレクトリに保存
メニュー>ツール>Buile systemから作成したPython_Verboseを選択してctrl+b

Python_Verbose.sublime-build
{
    "cmd": ["python", "$file", "-v"],
    "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
    "selector": "source.python"
}

コマンドライオプション-uと-vについて

1 コマンドラインと環境(http://docs.python.jp/3.3/using/cmdline.html)
1.1 コマンドライン
1.1.3 その他のオプション

-u¶(原文)

Force the binary layer of the stdout and stderr streams (which is available as their buffer attribute) to be unbuffered. The text I/O layer will still be line-buffered if writing to the console, or block-buffered if redirected to a non-interactive file.

PYTHONUNBUFFERED も参照してください。

-v

モジュールが初期化されるたびに、それがどこ(ファイル名やビルトインモジュール) からロードされたのかを示すメッセージを表示します。 2重に指定された場合(-vv)は、モジュールを検索するときにチェックされた各ファイルに対してメッセージを表示します。また、終了時のモジュールクリーンアップに関する情報も提供します。 PYTHONVERBOSE も参照してください。

結果1.1 ipython上で%run doctest_sample -v

1.1
In [80]: run doctest_sample.py -v
name?>> __main__
Trying:
    twice(8)
Expecting:
    16
ok
Trying:
    twice(1850923)
Expecting:
    3701846
ok
1 items had no tests:
    __main__
1 items passed all tests:
   2 tests in __main__.twice
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
EOF

name属性がmainなので、doctestが実行される
doctestの記述は
>>>で書いたところでpython形式で実行し、結果を返す
エラーがなければokと出して次のテストに進む。

1.1
Trying:
    twice(8)
Expecting:
    16
ok

テストサマリーを表示する

1.1
1 items had no tests:
    __main__
1 items passed all tests:
   2 tests in __main__.twice
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
body...

結果1.2 ipython上で%run doctest_use -v

1.2
In [81]: run doctest_use.py

出力が何も表示されない。

結果2.1 sublimetext上でdoctest_sample.pyをbuild

2.1
name?>> __main__
Trying:
    twice(8)
Expecting:
    16
ok
Trying:
    twice(1850923)
Expecting:
    3701846
ok
1 items had no tests:
    __main__
1 items passed all tests:
   2 tests in __main__.twice
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
EOF
[Finished in 0.5s]

sublimetextのビルド結果に表れる[Finished in 0.5s]以外1.1の結果と同じ

結果2.2 sublimetext上でdoctest_use.pyをbuild

2.2
name?>> doctest_sample
EOF
[Finished in 0.4s]

結果1.2と異なりprint文は実行されるようだ。

まとめ

  • docstring内にテストを記述する
  • docstring.testmod()を記述する
    • name属性がmainの時に実行するようif文の中に入れておく(doc.py参照)
  • pythonをverboseモードで実行する(python <filename> -v)
doc.py
import docstring

def hoge():
    '''
    hoge(args)
    '''


if __name__ == "__main__":
    doctest.testmod()

参考

Python でテスト
コマンドラインと環境