以下の内容はhttps://shikaku-sh.hatenablog.com/entry/python-practice-about-doctestより取得しました。


python doctest 学習

doctest は python の標準ライブラリです。追加のインストールは不要で、python をインストールすると最初から使えるはずです。

doctest

一番最初にテストコードで doctest をテストしてみます。

def add(a, b):
    """
    Add two numbers.

    Examples:
        >>> add(2, 3)
        5
        >>> add(-1, 1)
        0
    """
    return a + b

実行コード:

python -m doctest -v your_module.py

実行したときのログは次のとおりです。

Trying:
    add(2, 3)
Expecting:
    5
ok
Trying:
    add(-1, 1)
Expecting:
    0
ok
1 items had no tests:
    doctest-sample
1 items passed all tests:
   2 tests in doctest-sample.add
2 tests in 2 items.
2 passed and 0 failed.
Test passed.

-v をつけると上述のように、テストのログがコンソールに出力されるようになります。

-v をつけなかったときは、テストに成功した場合だと、なにも出力しません。しかし、テストに失敗した場合はコンソールにエラーの内容を出力します。なので -v は verbose(冗長な)オプションかもしれません。

*******************************************************
File "doctest-sample.py", line 8, in doctest-sample.add
Failed example:
    add(-1, 1)
Expected:
    2
Got:
    0
*******************************************************
1 items had failures:
   1 of   2 in doctest-sample.add
***Test Failed*** 1 failures.

また doctest を実行した際も __pycache__ フォルダが生成されて、フォルダの中にバイトコード化された .pyc ファイルができます。

これは結果的に -m オプションをつけるかどうかで、基本的には生成するかどうかが決まっています。(キャッシュを作ることが目的ではないが)

もしもキャッシュを作りたくないときは:

def add(a, b):
    """
    Add two numbers.

    Examples:
        >>> add(2, 3)
        5
        >>> add(-1, 1)
        0
    """
    return a + b

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

これだと単純な命令でテストを実行できるし、キャッシュも生成されない。でも、テストコード用のファイルみたいになってしまうので痛し痒し。

python your_module.py

補足として、テスト結果を外部ファイルに出力したい場合もこの書き方になる。

import doctest

if __name__ == "__main__":
    doctest.testfile('output.txt')

基本的な考え方として python は「再利用されるモジュール」だけ(自動的に)キャッシュにして残す設計だと思います。スクリプトの実行は限られた使い捨ての利用なので、キャッシュにする必要が(基本的に)ない、ということ。

本質的には -m をつけたから __pycache__ フォルダができたのだ、という理解は、ちょっと違うと思う。

テストの書き方

対話モードを示す >>> のあとにテストコードを示すだけです。テストコードのインデントの深さは関係していません。

def add(a, b):
    """
    Add two numbers.

    Examples:
        >>> add(2, 3)
        5
        >>> add(-1, 1)
        0
    """
    return a + b

基本的には対話モードで示した関数の実行例の返却値を次の行で示します。

例外をテストする場合は?

def divide(a, b):
    """
    Divide a by b.

    >>> divide(4, 2)
    2.0
    >>> divide(1, 0)
    Traceback (most recent call last):
        ...
    ZeroDivisionError: division by zero
    """
    return a / b

例外をテストしたいときの書き方は決まったやり方が必要です。

Traceback (most recent call last): を例外発生を示す呪文として記述する必要があります。さらに次の行はインデントを加えたうえで ... が必要。その上、最後に発生する例外 Error +その詳細も正確に明記する必要がある。なので、通常は division by zero の部分 message もしっかり書かないとダメ。

例外の message 部分を無視したいときは?

def divide(a, b):
    """
    >>> divide(4, 2)
    2.0
    >>> divide(1, 0)  # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
        ...
    ZeroDivisionError
    """
    return a / b

+IGNORE_EXCEPTION_DETAIL オプションを使用する。

このときは ... の行をなくしても OK の判定がでます。これは以下のとおりだと思います。

個人的には、例外の詳細を IGNORE_EXCEPTION_DETAIL で吸収して、その手前の ... は慣例として付けておいたらいいと思います。

例外の ... は、この書き方で特別に省略が許されているケースみたいなので。

自分のテキスト出力で、一致の省略をしたいときは?

+ELLIPSIS オプションで一致する部分を省略できる。例えば 'Result:' だけに限定することができる。(後方一致がしたいなら ... completed などになる)

def verbose_result():
    """
    >>> verbose_result()  # doctest: +ELLIPSIS
    'Result: ...'
    """
    return "Result: 123 completed"

細かいポイントだけど、返却されるテスト結果を 'Result: ...' つまり '' 文字列として評価している。この違いは次のケースで示すことができる。テスト実行中に print(つまり標準出力)に与えられたときは '' が不要です。

整理すると、戻り値として文字列を返却するときは文字列なので '' が必要。

def verbose_result():
    """
    >>> verbose_result()  # doctest: +ELLIPSIS
    Result: ...
    """
    print("Result: 123 completed")

なにも返却しない関数の場合は?

補足ですが、まったくなにも返却するデータの無い関数の場合は次のようにすることができます。

def noop():
    """
    >>> noop()
    """
    return None
Trying:
    noop()
Expecting nothing
ok
1 items had no tests:
    doctest-sample2
1 items passed all tests:
   1 tests in doctest-sample2.noop
1 tests in 2 items.
1 passed and 0 failed.
Test passed.

参考




以上の内容はhttps://shikaku-sh.hatenablog.com/entry/python-practice-about-doctestより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14