単体テストの基礎となる構築要素は、テストケース -- セットアップと 正しさのチェックを行う、独立したシナリオ -- です。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のサブクラスには良く似た名前のテスト関数が複 数定義されますので、テストケースクラスの全テストケースを使ってテストス イートを作成する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 のような) 独立したモジュールに置いた方が 以下のような点で有利です:
ご意見やご指摘をお寄せになりたい方は、 このドキュメントについて... をご覧ください。