25.5. test
--- Python 用回帰テストパッケージ¶
注釈
test
パッケージは、 Python の内部で使用されるためのものです。ドキュメントが書かれたのは Python のコア開発者の利便性を考えてのことです。 Python の標準ライブラリ以外でこのパッケージを使用することは、あまりお勧めできません。ここで触れられているコードは、Python のリリースの間に予告なく変更されたり削除されたりすることがあります。
test
パッケージには、Python 用の全ての回帰テストの他に、 test.support
モジュールと test.regrtest
モジュールが入っています。 test.support
はテストを充実させるために使い、 test.regrtest
はテストスイートを実行するのに使います。
test
パッケージ内のモジュールのうち、名前が test_
で始まるものは、特定のモジュールや機能に対するテストスイートです。新しいテストはすべて unittest
か doctest
モジュールを使って書くようにしてください。古いテストのいくつかは、 sys.stdout
への出力を比較する「従来の」テスト形式になっていますが、この形式のテストは廃止予定です。
25.5.1. test
パッケージのためのユニットテストを書く¶
unittest
モジュールを使ってテストを書く場合、幾つかのガイドラインに従うことが推奨されます。 1つは、テストモジュールの名前を、 test_
で始め、テスト対象となるモジュール名で終えることです。テストモジュール中のテストメソッドは名前を test_
で始めて、そのメソッドが何をテストしているかという説明で終えます。これはテスト実行プログラムが、そのメソッドをテストメソッドとして認識するために必要です。また、テストメソッドにはドキュメンテーション文字列を入れるべきではありません。コメント(例えば # True あるいは False だけを返すテスト関数
)を使用して、テストメソッドのドキュメントを記述してください。これは、ドキュメンテーション文字列が存在する場合はその内容が出力されてしまうため、どのテストを実行しているのかをいちいち表示したくないからです。
以下のような決まり文句を使います:
import unittest
from test import support
class MyTestCase1(unittest.TestCase):
# Only use setUp() and tearDown() if necessary
def setUp(self):
... code to execute in preparation for tests ...
def tearDown(self):
... code to execute to clean up after tests ...
def test_feature_one(self):
# Test feature one.
... testing code ...
def test_feature_two(self):
# Test feature two.
... testing code ...
... more test methods ...
class MyTestCase2(unittest.TestCase):
... same structure as MyTestCase1 ...
... more test classes ...
def test_main():
support.run_unittest(MyTestCase1,
MyTestCase2,
... list other tests ...
)
if __name__ == '__main__':
test_main()
この定型的なコードによって、テストスイートを regrtest.py
から起動できると同時に、スクリプト自体からも実行できるようになります。
回帰テストの目的はコードを解き明かすことです。そのためには以下のいくつかのガイドラインに従ってください:
テストスイートから、すべてのクラス、関数および定数を実行するべきです。これには外部に公開される外部APIだけでなく「プライベートな」コードも含みます。
ホワイトボックス・テスト(対象のコードの詳細を元にテストを書くこと)を推奨します。ブラックボックス・テスト(公開されるインタフェース仕様だけをテストすること)は、すべての境界条件を確実にテストするには完全ではありません。
すべての取りうる値を、無効値も含めてテストするようにしてください。そのようなテストを書くことで、全ての有効値が通るだけでなく、不適切な値が正しく処理されることも確認できます。
コード内のできる限り多くのパスを網羅してください。分岐するように入力を調整したテストを書くことで、コードの多くのパスをたどることができます。
テスト対象のコードにバグが発見された場合は、明示的にテスト追加するようにしてください。そのようなテストを追加することで、将来コードを変更した際にエラーが再発することを防止できます。
テストの後始末 (例えば一時ファイルをすべて閉じたり削除したりすること) を必ず行ってください。
テストがオペレーティングシステムの特定の状況に依存する場合、テスト開始時に条件を満たしているかを検証してください。
import するモジュールをできるかぎり少なくし、可能な限り早期に import を行ってください。そうすることで、てテストの外部依存性を最小限にし、モジュールの import による副作用から生じる変則的な動作を最小限にできます。
できる限りテストコードを再利用するようにしましょう。時として、入力の違いだけを記述すれば良くなるくらい、テストコードを小さくすることができます。例えば以下のように、サブクラスで入力を指定することで、コードの重複を最小化することができます:
class TestFuncAcceptsSequences(unittest.TestCase): func = mySuperWhammyFunction def test_func(self): self.func(self.arg) class AcceptLists(TestFuncAcceptsSequences): arg = [1, 2, 3] class AcceptStrings(TestFuncAcceptsSequences): arg = 'abc' class AcceptTuples(TestFuncAcceptsSequences): arg = (1, 2, 3)
参考
- Test Driven Development
コードより前にテストを書く方法論に関する Kent Beck の著書。
25.5.2. コマンドラインインタフェースを利用してテストを実行する¶
test.regrtest
はスクリプトとして Python の回帰テストスイートを実行できます。 -m
オプションを利用して、 python -m test.regrtest として実行します。スクリプトを実行すると、自動的に test
パッケージ内のすべての回帰テストを実行し始めます。パッケージ内の名前が test_
で始まる全モジュールを見つけ、それをインポートし、もしあるなら関数 test_main()
を実行してテストを行います。実行するテストの名前もスクリプトに渡される可能性があります。単一の回帰テストを指定 (python -m test.regrtest test_spam) すると、出力を最小限にします。テストが成功したかあるいは失敗したかだけを出力するので、出力は最小限になります。
直接 test.regrtest
を実行すると、テストに利用するリソースを設定できます。これを行うには、 -u
コマンドラインオプションを使います。 -u
のオプションに all
を指定すると、すべてのリソースを有効にします: python -m test.regrtest -uall 。(よくある場合ですが) 何か一つを除く全てが必要な場合、カンマで区切った不要なリソースのリストを all
の後に並べます。コマンド python -m test.regrtest -uall,-audio,-largefile とすると、 audio
と largefile
リソースを除く全てのリソースを使って test.regrtest
を実行します。すべてのリソースのリストと追加のコマンドラインオプションを出力するには、 python -m test.regrtest -h を実行してください。
テストを実行しようとするプラットフォームによっては、回帰テストを実行する別の方法があります。 Unix では、Python をビルドしたトップレベルディレクトリで make test を実行できます。 Windows上では、 PCBuild
ディレクトリから rt.bat を実行すると、すべての回帰テストを実行します。
バージョン 2.7.14 で変更: The test
package can be run as a script: python -m test.
This works the same as running the test.regrtest
module.
25.6. test.support
--- Utility functions for tests¶
注釈
The test.test_support
module has been renamed to test.support
in Python 3.x and 2.7.14. The name test.test_support
has been retained
as an alias in 2.7.
The test.support
module provides support for Python's regression
tests.
このモジュールは次の例外を定義しています:
-
exception
test.support.
TestFailed
¶ テストが失敗したとき送出される例外です。これは、
unittest
ベースのテストでは廃止予定で、unittest.TestCase
の assertXXX メソッドが推奨されます。
-
exception
test.support.
ResourceDenied
¶ unittest.SkipTest
のサブクラスです。 (ネットワーク接続のような) リソースが利用できないとき送出されます。requires()
関数によって送出されます。
test.support
モジュールでは、以下の定数を定義しています:
-
test.support.
verbose
¶ 冗長な出力が有効な場合は
True
です。実行中のテストについてのより詳細な情報が欲しいときにチェックします。 verbose はtest.regrtest
によって設定されます。
-
test.support.
TESTFN
¶ テンポラリファイルの名前として安全に利用できる名前に設定されます。作成した一時ファイルは全て閉じ、unlink (削除) しなければなりません。
-
test.support.
TEST_HTTP_URL
¶ ネットワークテスト専用の HTTP サーバーの URL を定義します。
test.support
モジュールでは、以下の関数を定義しています:
-
test.support.
forget
(module_name)¶ モジュール名 module_name を
sys.modules
から取り除き、モジュールのバイトコンパイル済みファイルを全て削除します。
-
test.support.
is_resource_enabled
(resource)¶ resource が有効で利用可能ならば
True
を返します。利用可能なリソースのリストは、test.regrtest
がテストを実行している間のみ設定されます。
-
test.support.
requires
(resource[, msg])¶ resource が利用できなければ、
ResourceDenied
を送出します。その場合、 msg はResourceDenied
の引数になります。__name__
が"__main__"
である関数にから呼び出された場合には常にTrue
を返します。テストをtest.regrtest
から実行するときに使われます。
-
test.support.
findfile
(filename)¶ filename という名前のファイルへのパスを返します。一致するものが見つからなければ、 filename 自体を返します。 filename 自体もファイルへのパスでありえるので、 filename が返っても失敗ではありません。
-
test.support.
run_unittest
(*classes)¶ 渡された
unittest.TestCase
サブクラスを実行します。この関数は名前がtest_
で始まるメソッドを探して、テストを個別に実行します。引数に文字列を渡すことも許可されています。その場合、文字列は
sys.module
のキーでなければなりません。指定された各モジュールは、unittest.TestLoader.loadTestsFromModule()
でスキャンされます。この関数は、よく次のようなtest_main()
関数の形で利用されます。def test_main(): support.run_unittest(__name__)
この関数は、名前で指定されたモジュールの中の全ての定義されたテストを実行します。
-
test.support.
check_warnings
(*filters, quiet=True)¶ warning が正しく発行されているかどうかチェックする、
warnings.catch_warnings()
を使いやすくするラッパーです。これは、warnings.simplefilter()
をalways
に設定して、記録された結果を自動的に検証するオプションと共にwarnings.catch_warnings(record=True)
を呼ぶのとほぼ同じです。check_warnings
は("message regexp", WarningCategory)
の形をした 2要素タプルをポジション引数として受け取ります。1つ以上の filters が与えられた場合や、オプションのキーワード引数 quiet がFalse
の場合、警告が期待通りであるかどうかをチェックします。指定された各 filter は最低でも1回は囲われたコード内で発生した警告とマッチしなければテストが失敗しますし、指定されたどの filter ともマッチしない警告が発生してもテストが失敗します。前者のチェックを無効にするには、 quiet をTrue
にします。引数が1つもない場合、デフォルトでは次のようになります:
check_warnings(("", Warning), quiet=True)
この場合、全ての警告は補足され、エラーは発生しません。
コンテキストマネージャーに入る時、
WarningRecorder
インスタンスが返されます。このレコーダーオブジェクトのwarnings
属性から、catch_warnings()
から得られる警告のリストを取得することができます。便利さのために、レコーダーオブジェクトから直接、一番最近に発生した警告を表すオブジェクトの属性にアクセスできます(以下にある例を参照してください)。警告が1つも発生しなかった場合、それらの全ての属性はNone
を返します。レコーダーオブジェクトの
reset()
メソッドは警告リストをクリアします。コンテキストマネージャーは次のようにして使います:
with check_warnings(("assertion is always true", SyntaxWarning), ("", UserWarning)): exec('assert(False, "Hey!")') warnings.warn(UserWarning("Hide me!"))
この場合、どちらの警告も発生しなかった場合や、それ以外の警告が発生した場合は、
check_warnings()
はエラーを発生させます。警告が発生したかどうかだけでなく、もっと詳しいチェックが必要な場合は、次のようなコードになります:
with check_warnings(quiet=True) as w: warnings.warn("foo") assert str(w.args[0]) == "foo" warnings.warn("bar") assert str(w.args[0]) == "bar" assert str(w.warnings[0].args[0]) == "foo" assert str(w.warnings[1].args[0]) == "bar" w.reset() assert len(w.warnings) == 0
全ての警告をキャプチャし、テストコードがその警告を直接テストします。
バージョン 2.6 で追加.
バージョン 2.7 で変更: 新しいオプション引数 filters と quiet
-
test.support.
check_py3k_warnings
(*filters, quiet=False)¶ check_warnings()
と似ていますが、 Python 3 互換性警告のためのものです。sys.py3kwarning == 1
の時、警告が実際に発生していることをチェックします。sys.py3kwarning == 0
の時、警告が発生していないことをチェックします。ポジション引数として("message regexp", WarningCategory)
の形をした 2要素タプルを受け取ります。オプション引数 quiet がTrue
のとき、filter になにもマッチしなくても失敗しません。引数がない場合は次と同じになります:check_py3k_warnings(("", DeprecationWarning), quiet=False)
バージョン 2.7 で追加.
-
test.support.
captured_stdout
()¶ これは、
with
文の body でsys.stdout
としてStringIO.StringIO
オブジェクトを利用するコンテキストマネージャーです。このオブジェクトは、with
文のas
節で受け取ることができます。使用例:
with captured_stdout() as s: print "hello" assert s.getvalue() == "hello\n"
バージョン 2.6 で追加.
-
test.support.
import_module
(name, deprecated=False)¶ この関数は name で指定されたモジュールを import して返します。通常の import と異なり、この関数はモジュールを import できなかった場合に
unittest.SkipTest
例外を発生させます。deprecated が
True
の場合、 import 中はモジュールとパッケージの廃止メッセージが抑制されます。バージョン 2.7 で追加.
-
test.support.
import_fresh_module
(name, fresh=(), blocked=(), deprecated=False)¶ この関数は、 name で指定された Python モジュールを、 import 前に
sys.modules
から削除することで新規に import してそのコピーを返します。reload()
関数と違い、もとのモジュールはこの操作によって影響をうけません。fresh は、同じように import 前に
sys.modules
から削除されるモジュール名の iterable です。blocked もモジュール名の iterable で、 import 中にモジュールキャッシュ内でその名前を
0
に置き換えることで、そのモジュールを import しようとするとImportError
を発生させます。指定されたモジュールと fresh や blocked 引数内のモジュール名は import 前に保存され、 fresh import が完了したら
sys.modules
に戻されます。deprecated が
True
の場合、 import 中はモジュールとパッケージの廃止メッセージが抑制されます。この関数はモジュールを import できなかった場合に
unittest.SkipTest
例外を送出します。使用例:
# Get copies of the warnings module for testing without # affecting the version being used by the rest of the test suite # One copy uses the C implementation, the other is forced to use # the pure Python fallback implementation py_warnings = import_fresh_module('warnings', blocked=['_warnings']) c_warnings = import_fresh_module('warnings', fresh=['_warnings'])
バージョン 2.7 で追加.
test.support
モジュールでは、以下のクラスを定義しています:
-
class
test.support.
TransientResource
(exc[, **kwargs])¶ このクラスのインスタンスはコンテキストマネージャーで、指定された型の例外が発生した場合に
ResourceDenied
例外を発生させます。キーワード引数は全て、with
文の中で発生した全ての例外の属性名/属性値と比較されます。全てのキーワード引数が例外の属性に一致した場合に、ResourceDenied
例外が発生します。バージョン 2.6 で追加.
-
class
test.support.
EnvironmentVarGuard
¶ 一時的に環境変数をセット・アンセットするためのクラスです。このクラスのインスタンスはコンテキストマネージャーとして利用されます。また、
os.environ
に対する参照・更新を行う完全な辞書のインタフェースを持ちます。コンテキストマネージャーが終了した時、このインスタンス経由で環境変数へ行った全ての変更はロールバックされます。バージョン 2.6 で追加.
バージョン 2.7 で変更: 辞書のインタフェースを追加しました。
-
EnvironmentVarGuard.
set
(envvar, value)¶ 一時的に、
envvar
をvalue
にセットします。
-
EnvironmentVarGuard.
unset
(envvar)¶ 一時的に
envvar
をアンセットします。
-
class
test.support.
WarningsRecorder
¶ ユニットテスト時に warning を記録するためのクラスです。上の、
check_warnings()
のドキュメントを参照してください。バージョン 2.6 で追加.