# 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` への出力を比較する「従来の」テスト形式になっていますが、この形式のテストは廃止予定です。

参考

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

## 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.``have_unicode`

  Unicode サポートが利用可能ならば `True` になります。

`test.support.``is_jython`

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

`test.support.``TESTFN`

  テンポラリファイルの名前として安全に利用できる名前に設定されます。作成した一時ファイルは全て閉じ、unlink (削除) しなければなりません。

`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 で追加.