Pythonでdocstringにテストコードを記述するdoctestの書き方、使い方

Pythonには、docstringの内容に応じたテストを行うdoctestモジュールが標準で含まれています。docstringの中に入出力例を書くだけなので簡単、かつ、ドキュメントとしても分かりやすくなります。

26.3. doctest — 対話的な実行例をテストする — Python 3.6.3 ドキュメント

ここでは以下の内容について説明します。

doctestによるテストのシンプルな例 エラーがない場合 エラーがある場合 オプション、引数による出力結果の制御 -vオプション verbose引数 コマンドラインからdoctestモジュールを実行 外部のテキストファイルにテストを記述する テキストファイルの書き方 pyファイルから呼び出し テキストファイルを直接実行docstringについては

doctestによるテストのシンプルな例

テストを加えたい関数のdocstring("""または'''で囲まれた文字列)にPython対話モードの形式で関数の呼び出しと期待される出力値を記述し、doctest.testmod()で実行します。

エラーがない場合

関数の内容とdocstringの内容に間違いがないコードを作成します。

def add(a, b):
    '''
    >>> add(1, 2)
    3
    >>> add(5, 10)
    15
    '''

    return a + b
if __name__ == '__main__':
    import doctest
    doctest.testmod()

このファイルを実行します。

$ python3 doctest_example.py

エラーがない場合は何も出力されません。 なお、if name == 'main'は「該当のスクリプトファイルがコマンドラインから実行された場合にのみ以降の処理を実行する」という意味。詳細は

エラーがある場合

以下のような間違ったコードを作って実行するとエラーが出力されます。

def add(a, b):
    '''
    >>> add(1, 2)
    3
    >>> add(5, 10)
    10
    '''

    return a * b
if __name__ == '__main__':
    import doctest
    doctest.testmod() 

$ python3 doctest_example_error.py
**********************************************************************
File "doctest_example_error.py", line 3, in __main__.add
Failed example:
    add(1, 2)
Expected:
    3
Got:
    2
**********************************************************************
File "doctest_example_error.py", line 5, in __main__.add
Failed example:
    add(5, 10)
Expected:
    10
Got:
    50
**********************************************************************
1 items had failures:
   2 of   2 in __main__.add
***Test Failed*** 2 failures.

doctestに書いた期待される出力値がExpected、実際の出力値がGotで示されています。

オプション、引数による出力結果の制御

-vオプション

エラーがない場合でも出力結果を表示させたい場合は、コマンドラインで-vオプションを付けて実行します。

$ python3 doctest_example.py -v
Trying:
    add(1, 2)
Expecting:
    3
ok
Trying:
    add(5, 10)
Expecting:
    15
ok
1 items had no tests:
    __main__
1 items passed all tests:
   2 tests in __main__.add
2 tests in 2 items.
2 passed and 0 failed.
Test passed.

verbose引数

常に出力結果を表示させたい場合は、pyファイル内のdoctest.testmod()で引数verbose=Trueを指定します。

if __name__ == '__main__':
    import doctest
    doctest.testmod(verbose=True)

-vオプションを付けなくても、常に出力結果が表示されます。

$ python3 doctest_example_verbose.py
Trying:
    add(1, 2)
Expecting:
    3
ok
Trying:
    add(5, 10)
Expecting:
    15
ok
1 items had no tests:
    __main__
1 items passed all tests:
   2 tests in __main__.add
2 tests in 2 items.
2 passed and 0 failed.
Test passed.

コマンドラインからdoctestモジュールを実行

if name == 'main'内では別の処理をさせたい場合、pyファイル内でdoctest.testmod()を呼ばなくてもコマンドラインから直接doctestモジュールを実行することができます。 例えば、次のような場合です。

def add(a, b):
    '''
    >>> add(1, 2)
    3
    >>> add(5, 10)
    15
    '''

    return a + b
if __name__ == '__main__':
    import sys
    result = add(int(sys.argv[1]), int(sys.argv[2]))
    print(result)

通常のようにコマンドライン引数を受け取って処理を実行することができます。

$ python3 doctest_example_without_import.py 3 4
7

-mオプションを付けてdoctestをスクリプトとして実行すると、doctestが書かれた関数に対してテストが行われる。出力結果を表示させたい場合はこれまでと同様に-vを付ける。

$ python3 -m doctest doctest_example_without_import.py

$ python3 -m doctest -v doctest_example_without_import.py
Trying:
    add(1, 2)
Expecting:
    3
ok
Trying:
    add(5, 10)
Expecting:
    15
ok
1 items had no tests:
    doctest_example_without_import
1 items passed all tests:
   2 tests in doctest_example_without_import.add
2 tests in 2 items.
2 passed and 0 failed.
Test passed.

外部のテキストファイルにテストを記述する

docstringの中ではなく外部のテキストファイルにテストコードを記述することもできます。

テキストファイルの書き方

docstringに書いていたように、Python対話モードの形式で書きます。使用する関数をimportしておく必要があります。 テスト対象の.pyファイルと同じディレクトリにテキストファイルを置く場合はfrom ファイル名 import 関数名とすれば問題ありません。

>>> from doctest_example import add
>>> add(1, 2)
3
>>> add(5, 10)
15

pyファイルから呼び出し

テスト用の別の.pyファイルでdoctest.testfile()を呼び出す。 doctest.testfile()の引数にテストコードが記述されたテキストファイルのパスを指定します。

import doctest
doctest.testfile('doctest_text.txt')

このpyファイルを実行します。

$ python3 doctest_example_testfile.py -v
Trying:
    from doctest_example import add
Expecting nothing
ok
Trying:
    add(1, 2)
Expecting:
    3
ok
Trying:
    add(5, 10)
Expecting:
    15
ok
1 items passed all tests:
   3 tests in doctest_text.txt
3 tests in 1 items.
3 passed and 0 failed.
Test passed.

テキストファイルを直接実行

pyファイルがなくても、コマンドラインから直接テキストファイルを読んでテストを実行することもできます。 Pythonコマンドに-mオプションを付けてdoctestをスクリプトとして実行します。テキストファイルのパスをコマンドライン引数として指定すれば問題ありません。

$ python3 -m doctest -v doctest_text.txt
Trying:
    from doctest_example import add
Expecting nothing
ok
Trying:
    add(1, 2)
Expecting:
    3
ok
Trying:
    add(5, 10)
Expecting:
    15
ok
1 items passed all tests:
   3 tests in doctest_text.txt
3 tests in 1 items.
3 passed and 0 failed.
Test passed.
Last Updated: 6/26/2019, 10:34:03 PM