目次

前のトピックへ

26.4. 2to3 - Python 2 から 3 への自動コード変換

次のトピックへ

27. デバッグとプロファイル

このページ

26.5. test — Python用回帰テストパッケージ

test パッケージには、Python 用の全ての回帰テストと、 test.test_support および test.regrtest モジュール が入っています。 test.test_support はテストを充実させるために使い、 test.regrtest はテストスイートを駆動するのに使います。

test パッケージ内の各モジュールのうち、名前が test_ で始まるものは、特定のモジュールや機能に対するテストスイートです。 新しいテストはすべて unittestdoctest モジュールを使って書くようにしてください 古いテストのいくつかは、 sys.stdout への出力を比較する”伝統的な”テスト形式になっていますが、 この形式のテストは廃止予定です。

参考

Module unittest
PyUnit 回帰テストを書く。
Module doctest
ドキュメンテーション文字列に埋め込まれたテスト。

26.5.1. test パッケージのためのユニットテストを書く

unittest モジュールを使ってテストを書く場合、幾つかのガイドラインに従うことが推奨されます。 1つは、テストモジュールの名前を、 test_ で始め、テスト対象となるモジュール名で終えることです。 テストモジュール中のテストメソッドは名前を test_ で始めて、 そのメソッドが何をテストしているかという説明で終えます。 これはテスト駆動プログラムにそのメソッドをテストメソッドとして認識させるため必要です。 また、テストメソッドにはドキュメンテーション文字列を入れるべきではありません。 テストメソッドのドキュメント記述には、 (# True あるいは False だけを返すテスト関数 のような) コメントを使ってください。 これは、ドキュメンテーション文字列が存在する場合にはその内容が出力されるため、どのテストを実行しているのかをいちいち表示しなくするためです。

以下のような基本的な決まり文句を使います:

import unittest
from test import test_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():
    test_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 の著書

26.5.2. test.regrtest を使ってテストを実行する

test.regrtest を使うと Python の回帰テストスイートを駆動 できます。スクリプトを単独で実行すると、自動的に test パッケージ内のすべての回帰テストを実行し始めます。パッケージ内の 名前が test_ で始まる全モジュールを見つけ、それをインポートし、もしあるなら関数 test_main() を実行してテストを行います。 実行するテストの名前もスクリプトに渡される可能性もあります。単一の回帰テストを指定 (python regrtest.py test_spam.py) すると、出力を最小限にします。テストが成功したかあるいは失敗したかだけを出力 するので、出力は最小限になります。

直接 test.regrtest を実行すると、テストに利用するリソースを設定できます。これを行うには、 -u コマンドラインオプションを使います。すべてのリソースを使うには、 python regrtest.py -uall を実行します。 -u のオプションに all を指定すると、すべてのリソースを有効にします。(よくある場合ですが) 何か一つを除く全てが必要な場合、カンマで区切った不要なリソースのリストを all の後に並べます。 コマンド python regrtest.py -uall,-audio,-largefile とすると、 audiolargefile リソースを除く 全てのリソースを使って test.regrtest を実行します。すべてのリソースのリストと追加のコマンドラインオプションを出力 するには、 python regrtest.py -h を実行してください。

テストを実行しようとするプラットフォームによっては、回帰テストを実行する別の方法があります。 Unix では、Python をビルドしたトップレベルディレクトリで make test を実行できます。 Windows上では、 PCBuild ディレクトリから rt.bat を実行すると、すべての回帰テストを実行します。

26.5.3. test.test_support — テストのためのユーティリティ関数

ノート

test.test_support モジュールは、Python 3では test.support にリネームされました。 2to3 ツールは、ソースコード内のimportを自動的にPython 3用に修正します。

test.test_support モジュールでは、 Python の回帰テストに対するサポートを提供しています。

このモジュールは次の例外を定義しています:

exception test.test_support.TestFailed

テストが失敗したとき送出される例外です。 これは、 unittest ベースのテストでは廃止予定で、 unittest.TestCase の assertXXX メソッドが推奨されます。

exception test.test_support.TestSkipped

TestFailed のサブクラスです。テストがスキップされたとき送出されます。テスト時に (ネットワーク接続のような) 必要なリソースが利用 できないときに送出されます。

exception test.test_support.ResourceDenied

TestSkipped のサブクラスです。 (ネットワーク接続のような)リソースが利用できないとき送出されます。 requires() 関数によって送出されます。

test.test_support モジュールでは、以下の定数を定義しています:

test.test_support.verbose

冗長な出力が有効な場合は True です。実行中のテストについてのより詳細な情報が欲しいときにチェックします。 verbosetest.regrtest によって設定されます。

test.test_support.have_unicode

ユニコードサポートが利用可能ならば True になります。

test.test_support.is_jython

実行中のインタプリタが Jython ならば True になります。

test.test_support.TESTFN

一時ファイルを作成するパスに設定されます。作成した一時ファイルは全て閉じ、unlink (削除) せねばなりません。

test.test_support モジュールでは、以下の関数を定義しています:

test.test_support.forget(module_name)

モジュール名 module_namesys.modules から取り除き、モジュールのバイトコンパイル済みファイルを全て削除します。

test.test_support.is_resource_enabled(resource)

resource が有効で利用可能ならば True を返します。 利用可能なリソースのリストは、 test.regrtest がテストを実行している間のみ設定されます。

test.test_support.requires(resource[, msg])

resource が利用できなければ、 ResourceDenied を送出します。その場合、 msgResourceDenied の引数になります。 __name__"__main__" である関数にから 呼び出された場合には常に真を返します。テストを test.regrtest から実行するときに使われます。

test.test_support.findfile(filename)

filename という名前のファイルへのパスを返します。一致するものが見つからなければ、 filename 自体を返します。 filename 自体もファイルへのパスでありえるので、 filename が返っても失敗ではありません。

test.test_support.run_unittest(*classes)

渡された unittest.TestCase サブクラスを実行します。この関数は名前が test_ で始まるメソッドを探して、 テストを個別に実行します。

引数に文字列を渡すことも許可されています。その場合、文字列は sys.module のキーでなければなりません。 指定された各モジュールは、 unittest.TestLoader.loadTestsFromModule() でスキャンされます。 この関数は、よく次のような test_main() 関数の形で利用されます。

def test_main():
    test_support.run_unittest(__name__)

この関数は、名前で指定されたモジュールの中の全ての定義されたテストを実行します。

test.test_support.check_warnings()

warning が正しく発行されているかどうか1つのassertionでチェックする、 warnings.catch_warnings() を使いやすくするラッパーです。 これは、 warnings.catch_warnings(record=True) を呼ぶのとほぼ同じです。

主な違いは、この関数がコンテキストマネージャーのエントリーになっていることです。 ただのリストの代わりに、 WarningRecorder のインスタンスが返されます。 warning のリストには、 recorder オブジェクトの warnings 属性からアクセスできます。 また、最後に発生した warning には、オブジェクトから直接アクセスすることができます。 warning が1つも発生しなかった場合は、後者の属性は None になります。

recorder オブジェクトは reset() メソッドを持っています。 このメソッドは warning リストをクリアします。

コンテキストマネージャーは次のようにして利用します。

with check_warnings() as w:
    warnings.simplefilter("always")
    warnings.warn("foo")
    assert str(w.message) == "foo"
    warnings.warn("bar")
    assert str(w.message) == "bar"
    assert str(w.warnings[0].message) == "foo"
    assert str(w.warnings[1].message) == "bar"
    w.reset()
    assert len(w.warnings) == 0

バージョン 2.6 で追加.

test.test_support.captured_stdout()

これは、 with 文の body で sys.stdout として StringIO.StringIO オブジェクトを利用するコンテキストマネージャーです。 このオブジェクトは、 with 文の as 節で受け取ることができます。

使用例:

with captured_stdout() as s:
    print "hello"
assert s.getvalue() == "hello"

バージョン 2.6 で追加.

test.test_support モジュールは以下のクラスを定義しています。

class test.test_support.TransientResource(exc[, **kwargs])

このクラスのインスタンスはコンテキストマネージャーで、指定された型の例外が発生した場合に ResourceDenied 例外を発生させます。 キーワード引数は全て、 with 文の中で発生した全ての例外の属性名/属性値と比較されます。 全てのキーワード引数が例外の属性に一致した場合に、 ResourceDenied 例外が発生します。

バージョン 2.6 で追加.

class test.test_support.EnvironmentVarGuard

一時的に環境変数をセット・アンセットするためのクラスです。 このクラスのインスタンスはコンテキストマネージャーとして利用されます。

バージョン 2.6 で追加.

EnvironmentVarGuard.set(envvar, value)

一時的に、 envvarvalue にセットします。

EnvironmentVarGuard.unset(envvar)

一時的に envvar をアンセットします。

class test.test_support.WarningsRecorder

ユニットテスト時にwarningを記録するためのクラスです。 上の、 check_warnings() のドキュメントを参照してください。

バージョン 2.6 で追加.