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