##
5.3.2 テストの構成

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

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

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

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

    import unittest

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

何らかのテストを行う場合、ベースクラス`TestCase`の `assert*()` か `fail*()`メソッドを使用してください。テスト ケース実行時、テストが失敗すると例外が送出され、テストフレームワークはテ スト結果をfailureとします。`assert*()` と`fail*()`以 外からの例外が発生した場合、テスト結果はerrorsとなります。

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

    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`のサブクラスを作成する必要があります。これは 時間のかかる、うんざりする作業ですので、PyUnitではより簡単なメカニズムを 用意しています:

    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")

PyUnitでは`テストスイート`によってテストケースインスタンスをテスト 対象の機能によってグループ化することができます。`テストスイート` は、`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

または:

    class WidgetTestSuite(unittest.TestSuite):
        def __init__(self):
            unittest.TestSuite.__init__(self,map(WidgetTestCase,
                                                  ("testDefaultSize",
                                                   "testResize")))

(小心者は前者を使うべし)

一般的に、`TestCase`のサブクラスには良く似た名前のテスト関数が複数 定義されます。そこで`unittest`モジュールには、テストケースクラ スの全テストケースを使ってテストスイートを作成する`makeSuite()` 関数を用意しています。

    suite = unittest.makeSuite(WidgetTestCase,'test')

`makeSuite()`でテストスイートを作成した場合、テストケースの実行 順序はテストケース関数名を`cmp()`組み込み関数でソートした順番と なります。

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

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

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

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