doctest を unittest と統合して実行する (Python)
doctest とは
ここに検索で来られた方には不要かもしれませんが、説明の都合上、簡単に書いておきます。
doctest は、以下のような docstring 内の対話実行例が正しく実行できるか確認するモジュールです。 docstring 内だけでなく、テキストファイルの実行例を確認する事も可能です。
1 2 3 4 5 6 7 8 9 10 11 12 | # -*- coding: utf-8 -*- def times(a, b): """ 2つの入力を掛け算して出力 >>> times(4, 9) 35 """ return a * b |
例えば、上記のコードを doctest にかけると間違いを指摘してくれます。
$ python -m doctest mytestlib/core.py ********************************************************************** File "mytestlib/core.py" , line 8, in core.times Failed example: times(4, 9) Expected: 35 Got: 36 ********************************************************************** 1 items had failures: 1 of 1 in core.times ***Test Failed*** 1 failures. |
さらに詳しい情報は公式ドキュメントをご確認ください。
参考
doctest を unittest に統合する
doctest も定常的に実行しないと意味がないので、普段のテスト実行に統合したいと思うのは当然でしょう。
別途スクリプトを書くことも可能ですが、doctest はユニットテストのインスタンスを生成するAPIを提供しているのでこれを使うのが簡単です。
参考
以下のようなディレクトリ構造があるとして、test_core.py
がユニットテストファイルだとします。
ユニットテストモジュールの中で load_tests()
関数を定義し、doctest.DocTestSuite()
によって生成されたユニットテストインスタンスを tests
に加えます。
これにより、通常のユニットテストに加えて、doctest が実行されるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env python # -*- coding: utf-8 -*- import unittest import doctest import mytestlib.core # ここに普通のユニットテスト def load_tests(loader, tests, ignore): tests.addTests(doctest.DocTestSuite(mytestlib.core)) return tests if __name__ = = '__main__' : unittest.main() |
__init__.py
内で名前空間を再定義している場合は注意
これだけだと公式ドキュメントを読めば良いのですが、上記のようにパッケージを定義している場合、少し注意が必要です。
パッケージの場合、__init__.py
の中で以下のように名前空間を再定義しているケースが多いと思います。
1 2 3 | # -*- coding: utf-8 -*- from .core import * |
この場合、mytestlib.times()
も mytestlib.core.times()
もアクセス可能なので、以下のように書いても動作するように思えます。
(.core
が削除されている)
11 | tests.addTests(doctest.DocTestSuite(mytestlib)) |
ところが、これでは doctest が実行されません。 doctest の仕様上、import されたオブジェクトは検索対象から外れてしまうからです。 必ずオリジナルのモジュールを参照するようにしましょう。
del
を使っている場合は、さらに注意
一部のパッケージ1では、名前空間をシンプルに保つため、del
を用いて大元のモジュールを削除しているケースがあります2。
1 2 3 4 5 | # -*- coding: utf-8 -*- from .core import * del core |
この場合、mytestlib.core
を参照できなくなるので、上記のやり方だとうまくいきません。
AttributeError: module 'mytestlib' has no attribute 'core'
その場合は、以下のように 'mytestlib.core'
と文字列でモジュール名を指定すると動作するようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env python # -*- coding: utf-8 -*- import unittest import doctest # import mytestlib.core # ここに普通のユニットテスト def load_tests(loader, tests, ignore): tests.addTests(doctest.DocTestSuite( 'mytestlib.core' )) return tests if __name__ = = '__main__' : unittest.main() |
0 件のコメント:
コメントを投稿