はしくれエンジニアもどきのメモ

情報系技術・哲学・デザインなどの勉強メモ・備忘録です。

python3.7 unittestトラブルメモ

python3.7 unittestトラブルメモ

python 3.7時点での方法,今後のアップデートで変わる可能性あり.

unittestがうまく動作しなかったときの対処メモ(大体パス通せば解決するはず). 今回はパスを通さず手っ取り早くテストできる状態にする.

以下のファイル構成とする.

project/
  test_target.py
  test/
    test_1.py
    \__init__.py

ポイントとしては, - テストパッケージが名前空間パッケージ(namespace package)になっていない(init.pyがあるか) - テストファイルを単体実行する場合,テスト対象モジュールのパスが見えるか

解決早見表

trouble solve
module = __import__(module_name) ModuleNotFoundError: No module named 'test.test_1' 使用コマンドが名前空間パッケージに対応していないので__init__.pyを追加する
import test_target ModuleNotFoundError: No module named 'test_target' (親にある)テスト対象モジュールを読めていないのでsys.path.append()でパスを追加 or
python unittest -mでunittestをモジュール呼び出しスクリプト実行する.

環境

unittestの使い方

新しい機能やオプションの確認はドキュメントにある.

docs.python.org

また具体例はDive Into Python3でもトピックがある.

diveintopython3-ja.rdy.jp

テストファイルの実行方法は4種類ほど(オプションを含めたら無数にあるが)

  • python -m unittest test.test_1:モジュール名の指定
  • python -m unittest test/test_1.py:テストファイル名の指定(上と同じ処理)
  • python -m unittest discover -s test:テストファイルをまとめて実行
  • python test/test_1.py:テストファイル自体の実行
  • IDE(ex. VSCode)の拡張機能を利用

モジュール読み込みではテストクラス(テストスイート)やテスト関数のみの実行ができる.

python -m unittest test.test_1.TestClass`:テストクラスの実行

python -m unittest test.test_1.TestClass.test_func`:TestClass内のtest_funcメソッドの実行

unittest注意点

モジュール名の指定だけでなくファイル名の指定もできる

python unittest -mの際,モジュール名しか指定できないという記述がよくあるが,ファイル名の指定もできる. ドキュメントの最初のほうにも載っている.

python unittest -m test.test_1

上と同じ

python unittest -m test/test_1.py

unittestでの名前空間パッケージの扱い

python -m unittest discoverでは名前空間パッケージ(__init__.pyなし)に対応しているが,通常の使用では対応していないことに注意.

なので,以下の構成だと

''' project/ test_target.py test/(名前空間パッケージ) test_1.py '''

discoverは問題ないが

python -m unittest discover -s test

test ok

モジュール名(ファイル名)呼び出しではモジュールが見つからない.

python -m unittest test.test_1

module = __import__(module_name) ModuleNotFoundError: No module named 'test.test_1'

__init__.pyを置いて通常のパッケージ にすれば問題なくテストできる.

テストファイル単体実行時の注意

unittestを介さず直接テストファイルを実行できる.

python test/test_1.py

ただこのときテスト対象モジュールが階層上にあると,python3ではそのファイルにはアクセスできないのでエラーValueError: attempted relative import beyond top-level packageになる.

例えば,テストファイルtest_1.pyを以下にすると読み込めずエラーになる.

import test_target
ValueError: attempted relative import beyond top-level package

この場合,sys.pathにテスト対象モジュールの絶対パス相対パスは有効にならない)を追加すれば解決する.(ただし,テストファイル毎)に必要になり面倒になる.

絶対パスの求め方はpathlibを使うと動的に求められる.

import sys
import pathlib
path = pathlib.Path('__file__')
path /= '../' # 1つ上の階層を指す
sys.path.append(str(path.resolve()))

import test_target