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

何らかのテストを行う場合、ベースクラスTestCaseassert*()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ではテストスイートによってテストケースインスタンスをテスト 対象の機能によってグループ化することができます。テストスイート は、unittestTestSuiteクラスで作成します。

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のサブクラスには良く似た名前のテスト関数が複 数定義されますので、テストケースクラスの全テストケースを使ってテストス イートを作成するmakeSuite()関数を用意しています:

suite = unittest.makeSuite(WidgetTestCase)

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

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

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

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

ご意見やご指摘をお寄せになりたい方は、 このドキュメントについて... をご覧ください。