##
2 テストの構成

単体テストの基礎となる構築要素は、テストケース -- セットアップと 正しさのチェックを行う、独立したシナリオ -- です。`unittest`では、テスト ケースは`unittest`モジュールの`TestCase`クラスのインスタ ンスで示します。テストケースを作成するには`TestCase`のサブクラスを 記述するか、または`FunctionTestCase`を使用します。

`TestCase`から派生したクラスのインスタンスは、このオブジェクトだけ で一件のテストと初期設定・終了処理を行います。

`TestCase`インスタンスは外部から完全に独立し、単独で実行する事も、 他の任意のテストと一緒に実行する事もできなければなりません。

以下のように、`TestCase`のサブクラスは`runTest()`をオーバライドし、 必要なテスト処理を記述するだけで簡単に書くことができます:

    import unittest

    class DefaultWidgetSizeTestCase(unittest.TestCase):
        def runTest(self):
            widget = Widget('The widget')
            self.assertEqual(widget.size(), (50,50), 'incorrect default size')

何らかのテストを行う場合、ベースクラス`TestCase`の `assert*()` か `fail*()`メソッドを使用してください。 テストが失敗すると例外が送出され、`unittest`はテスト結果を failureとします。その他の例外はerrorとなります。 これによりどこに問題があるかが判ります。failureは間違った結果 (6 になるはずが 5 だった)で発生します。errorは間違ったコード (たとえば間違った関数呼び出しによる`TypeError`)で発生します。

テストの実行方法については後述とし、まずはテストケースインスタンスの作成 方法を示します。テストケースインスタンスは、以下のように引数なしでコンス トラクタを呼び出して作成します。

    testCase = DefaultWidgetSizeTestCase()

似たようなテストを数多く行う場合、同じ環境設定処理を何度も必要となりま す。例えば上記のようなWidgetのテストが100種類も必要な場合、それぞれのサ ブクラスで`Widget`オブジェクトを生成する処理を記述するのは好ましくあり ません。

このような場合、初期化処理は`setUp()`メソッドに切り出し、テスト実 行時にテストフレームワークが自動的に実行するようにすることができます:

    import unittest

    class SimpleWidgetTestCase(unittest.TestCase):
        def setUp(self):
            self.widget = Widget('The widget')

    class DefaultWidgetSizeTestCase(SimpleWidgetTestCase):
        def runTest(self):
            self.failUnless(self.widget.size() == (50,50),
                            'incorrect default size')

    class WidgetResizeTestCase(SimpleWidgetTestCase):
        def runTest(self):
            self.widget.resize(100,150)
            self.failUnless(self.widget.size() == (100,150),
                            'wrong size after resize')

テスト中に`setUp()`メソッドで例外が発生した場合、テストフレーム ワークはテストを実行することができないとみなし、`runTest()`を実行 しません。

同様に、終了処理を`tearDown()`メソッドに記述すると、 `runTest()`メソッド終了後に実行されます:

    import unittest

    class SimpleWidgetTestCase(unittest.TestCase):
        def setUp(self):
            self.widget = Widget('The widget')

        def tearDown(self):
            self.widget.dispose()
            self.widget = None

`setUp()`が正常終了した場合、`runTest()`が成功したかどうかに従って `tearDown()`が実行されます。

このような、テストを実行する環境をfixtureと呼びます。

JUnitでは、多数の小さなテストケースを同じテスト環境で実行する場合、全て のテストについて`DefaultWidgetSizeTestCase`のような `SimpleWidgetTestCase`のサブクラスを作成する必要があります。これは 時間のかかる、うんざりする作業ですので、`unittest`ではより簡単なメカニズムを 用意しています:

    import unittest

    class WidgetTestCase(unittest.TestCase):
        def setUp(self):
            self.widget = Widget('The widget')

        def tearDown(self):
            self.widget.dispose()
            self.widget = None

        def testDefaultSize(self):
            self.failUnless(self.widget.size() == (50,50),
                            'incorrect default size')

        def testResize(self):
            self.widget.resize(100,150)
            self.failUnless(self.widget.size() == (100,150),
                            'wrong size after resize')

この例では`runTest()`がありませんが、二つのテストメソッドを定義し ています。このクラスのインスタンスは`test*()`メソッドのどちらか一 方の実行と、`self.widget`の生成・解放を行います。この場合、テスト ケースインスタンス生成時に、コンストラクタの引数として実行するメソッド名 を指定します:

    defaultSizeTestCase = WidgetTestCase('testDefaultSize')
    resizeTestCase = WidgetTestCase('testResize')

`unittest`では`テストスイート`によってテストケースインスタンスをテスト 対象の機能によってグループ化することができます。テストスイート は、`unittest`の`TestSuite`クラスで作成します。

    widgetTestSuite = unittest.TestSuite()
    widgetTestSuite.addTest(WidgetTestCase('testDefaultSize'))
    widgetTestSuite.addTest(WidgetTestCase('testResize'))

各テストモジュールで、テストケースを組み込んだテストスイートオブジェクト を作成する呼び出し可能オブジェクトを用意しておくと、テストの実行や参照が 容易になります:

    def suite():
        suite = unittest.TestSuite()
        suite.addTest(WidgetTestCase('testDefaultSize'))
        suite.addTest(WidgetTestCase('testResize'))
        return suite

または:

    def suite():
        tests = ['testDefaultSize', 'testResize']

        return unittest.TestSuite(map(WidgetTestCase, tests))

一般的には、`TestCase`のサブクラスには良く似た名前のテスト関数が複 数定義されますので、`unittest`では テストスイートを作成して個々のテストで満たすプロセスを自動化するのに使う `TestLoader`を用意しています。 たとえば、

    suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)

は`WidgetTestCase.testDefaultSize()`と`WidgetTestCase.testResize` を走らせるテストスイートを作成します。 `TestLoader`は自動的にテストメソッドを識別するのに`'test'`という メソッド名の接頭辞を使います。

いろいろなテストケースが実行される順序は、テスト関数名を組み込み関数`cmp()` でソートして決定されます。

システム全体のテストを行う場合など、テストスイートをさらにグループ化した い場合がありますが、このような場合、`TestSuite`インスタンスには `TestSuite`と同じように`TestSuite`を追加する事ができます。

    suite1 = module1.TheTestSuite()
    suite2 = module2.TheTestSuite()
    alltests = unittest.TestSuite([suite1, suite2])

テストケースやテストスイートは (widget.py のような) テスト対象のモジュール内にも記述できますが、テストは (test_widget.py のような) 独立したモジュールに置いた方が 以下のような点で有利です:

- テストモジュールだけをコマンドラインから実行することができる。
- テストコードと出荷するコードを分離する事ができる。
- テストコードを、テスト対象のコードに合わせて修正する誘惑に駆られにくい。
- テストコードは、テスト対象コードほど頻繁に更新されない。
- テストコードをより簡単にリファクタリングすることができる。
- Cで書いたモジュールのテストは、どっちにしろ独立したモジュールとなる。
- テスト戦略を変更した場合でも、ソースコードを変更する必要がない。