2016/09/23更新

[Python] unittestモジュールでテストスイートを実現する2つの方法

このエントリーをはてなブックマークに追加            

こんにちは、絶賛Pythonコーディング中の@yoheiMuneです。
今日は前回の「ユニットテストをPythonで書く」からの続きで、unittestモジュールを用いたテストスイートのやり方をブログに書きたいと思います。

画像

目次




テストスイートとは

テストスイートとは、複数のテストクラス(やテストスイート)をまとめたもので、まとめたものを一気にテストできる仕組みです。実務でテストをする場合には、機能別にテストを書くことが多く、それらをまとめるためにテストスイートを利用します。ちなみに僕は最初の方では1つのテストクラスに全てのテストケースを書いていましたが、それはなかなか大変でした(行数が多くて見つけづらいし、テストのスキップもしづらい)。

前回のブログから引き続きunittestモジュールを用いたテストについて書きたいと思います。unittestモジュールでは、大きく2つの方法でテストスイートを作成することができます。それぞれ見ていきたいと思います。



手動でテストスイートを作成する

まずは自分でテストスイートを作る方法です。以下のように複数のテストクラスがあるとします。
.
├── suite.py : テストスイートを定義して実行するクラス(起点となるクラス)
├── test.py  : とあるテストクラス1
└── other.py : とあるテストクラス2
テストスイートとして読み込むtest.pyother.pyは、以下のような普通のテストクラスです。

test.py

import unittest
class MyTest(unittest.TestCase):
    def test_mytest_01(self):
        print("test_mytest_01 is called")
        one = 1
        self.assertEqual(one, 1, "one is 1")

other.py

import unittest
class OtherTest(unittest.TestCase):
    def test_other_01(self):
        print("test_other_01 is called")
        two = 2
        self.assertEqual(two, 2, "two is 2")
そして上記2つのテストケースを、suite.pyでは手動でテストスイートとしてまとめます。以下のようにすることでテストスイートを定義できます。

suite.py

import unittest

# テストケースのクラスをそれぞれ読み込む
import test
import other

# テストスイートを作成して返却します
def suite():
    # テストスイートを定義します
    test_suite = unittest.TestSuite()
    # addTestを用いてテストスイートに追加していきます
    test_suite.addTest(unittest.makeSuite(test.MyTest))
    test_suite.addTest(unittest.makeSuite(other.OtherTest))
    return test_suite

if __name__ == "__main__":
    # 作成したテストスイートを呼び出して、TextTestRunnerで実行します
    mySuite = suite()
    unittest.TextTestRunner().run(mySuite)
そして実行は以下のように、普通な感じに行うことができます。
$ python3 suite.py 
test_mytest_01 is called
.test_other_01 is called
.
-------------------------
Ran 2 tests in 0.000s
OK
以上のように、TestSuiteaddTestでテストスイートを作成し、TextTestRunnerで実行することで、手動でテストスイートの作成&実行を行うことができます。



自動的にテストスイートを作成する

unittestモジュールの強力な機能の一つで、discoverを用いることで自動的にテストクラスを見つけてきてくれて、それをテストスイートとして作成してくれるものがあります。今回は以下のようなディレクトリ構成としましょう。
.
├── discover.py : テスト起動用のスクリプト
└── test        : テストケースを置いてあるディレクトリ
    ├── __init__.py
    ├── test_sub1.py : テストクラス1
    ├── test_sub2.py : テストクラス2
    └── sub_dir
        ├── __init__.py
        └── test_subsub1.py : テストクラス3
今回はtest_sub1.pytest_sub2.pytest_subsub1.pyの3つのテストクラスをもとに、テストスイートを作成してテストを実行します。

そしてそれを行うためにdiscoverを用いて、testディレクトリ以下にあるテストケースを自動的に収集してテストスイートを作成します。具体的には以下のように実装します。

discover.py

import unittest

# テストスイートを作成します
def suite():
    # 前述と同じく、TestSuiteから空っぽのテストスイートを作成します
    test_suite = unittest.TestSuite()
    # discoverメソッドを用いて、testディレクトリ以下からテストクラスを見つけます
    all_test_suite = unittest.defaultTestLoader.discover("test", pattern="test_*.py")
    # 見つけたテストクラスをテストスイートに追加します
    for ts in all_test_suite:
        test_suite.addTest(ts)
    return test_suite

if __name__ == "__main__":
    # テストスイートを呼び出して実行します
    mySuite = suite()
    unittest.TextTestRunner().run(mySuite)
上記のようにdiscover("test", pattern="test_*.py")のところでテストケースを走査しています。
これの実行は以下のように行います。
$ python3 discover.py 
test_subsub1_01 is called
.test_sub1_01 is called
.test_sub2_01 is called
.
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK
discoverを使った方法の方がテストスイートをメンテナンスしなくていいので便利ですね。もしスキップしたいテストなどがある場合には、テストメソッド(またはテストクラス)に@unittest.skipをつければスキップもできるので、コントロールも便利です(詳細は前回のブログをご参照ください)。

また上記と同じ内容について、コマンドのみでも実行することができます。
$ python -m unittest discover -s test -p "test_*.py"

# または省略形でも可
$ python -m unittest discover test "test_*.py"
コードでもコマンドでもできるのでお好みの方法で使ってみてください。

あと、discoverされたクラスですが、import地点はdiscoverしているところ(今回だとdiscover.pyのところ)が起点となります。例えば以下の場合には、
.
├── discover.py
├── get_one.py  : test_subsub1.pyでインポートしたいクラス
└── test
    ├── __init__.py
    └── sub_dir
        ├── __init__.py
        └── test_subsub1.py : discoverされるクラス
test_subsub1.pyからは以下のようにget_one.pyをインポートして使うことができます。
import unittest

# discover.pyの地点をルートとしてimportできる
from get_one import get_one
class SubSub1Test(unittest.TestCase):
    def test_subsub1_01(self):
        self.assertEqual(get_one(), 1, "get_one() must return 1")



参考資料

Pythonのunittestモジュールについては、以下の記事を参照しました。ありがとうございます。

- 26.4. unittest — ユニットテストフレームワーク — Python 3.5.2 ドキュメント

- unit testing - Trying to implement python TestSuite - Stack Overflow

- how to run all Python unit tests in a directory - Stack Overflow



最後に

最初はテストスイートどうやるんだろうって色々と調べてしまいました。ドキュメントを最初に読むよりは、StackOverFlowとかで事例を見てから、その後にドキュメント読むといいですね。なんとなくすんなり理解できる気がします。Pythonについては細かいところをもっともっと身につけていかないとなーと思う今日この頃です。

最後になりますが本ブログでは、Python・フロントエンド・開発関連・Swift・Linux・Java・機械学習など雑多に情報発信をしていきます。自分の第2の脳にすべく、情報をブログに貯めています。気になった方は、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。

最後までご覧頂きましてありがとうございました!





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。