2016/09/20更新

[Python] ユニットテストをPythonで書く

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

こんにちは、@yoheiMuneです。
皆様はUnitテスト書かれていますかー?僕は自分のアプリケーションでunitテストを書くようにしています。今日はPythonでのUnitテストの書き方をブログに書きたいと思います。

画像

目次




単体テストを書く理由

僕が単体テストを書く理由は、別に誰からか強制されているわけではなく、自分のために書きます。ユニットテストを書くことで以下のメリットがあるなぁと感じています。

  • コーディングしながら同時にテストもできて品質を確保しやすい
  • テストしやすいメソッド設計になって、今後のメンテナンスをしやすい(例えば将来仕様変更があった場合にも、メソッドを修正してテストも修正すれば、最低限品質を担保できる
  • 細かくテストができてテストバリエーションを確保でき、品質に繋がる。例えばHTTP〜アプリ〜DBを通してテストする場合には、どうしても細かい部分はテストしづらい)
  • 新しいライブラリなど使う場合に、いろいろと実験できて(機能検証に使えて)嬉しい。必要に応じて細かい挙動なども確認できる
つまりユニットテストを書くことで、コーディング中に感じる不安をすぐにテストで払拭できるし、仕様変更に対する品質確保もしやすいし、新しい技術を取り入れるときにも理解が進むしで、とても良いと思います。時間がかかると思われがちですが、バグや悩みが減ることで、逆にスピードアップに繋がっています。

さてUnitテストに関する所感は以上にするとして、単体テストに着いてPythonでも幾つかのライブラリが存在します。けど、標準ライブラリのunittestが強力でそれで十分かなーと思うので、今日はそれをブログに書きたいと思います。



unittestモジュールについて

unittestモジュールはPythonの標準ライブラリで、JUnitライクなテストフレームワークです(JUnitっぽいフレームワークは、JSやPHPなど様々な言語で存在します)。強力なライブラリで必要なことは大体ありますので、必要に応じて以下のリファレンスをご確認ください。

http://docs.python.jp/3/library/unittest.html

それではさっそく使ってみたいと思います。



Unitテストの準備

準備は非常に簡単で、以下のインポート文を書くだけです(標準ライブラリなのでpipする必要もありません)。
import unittest



単体テストを書いてみる

それでは早速単体テストを書いて見たいと思います。unittest.TestCaseクラスを継承したテスト用のクラスを作成します。
# test.py

# unittest.TestCaseの子クラスを作成します
class MyTest(unittest.TestCase):

    # メソッド名は必ず「test」をプレフィックスに付けます
    def test_do_something(self):
        # 検証を行います
        one = 1
        self.assertEqual(one, 1, "one is 1")
これでテストコードは完成です。簡単ですね!
実行は以下のように行います。
$ python -m unittest test.py
または、test.pyに起動処理を実装して、それを使うこともできます。
# test.py
if __name__ == "__main__":
    unittest.main()
$ python test.py
こっちの方がコマンド実行が楽なので便利ですねー。

また途中に出てきたassertEqualについては検証を行うメソッドですが、他にもassertTrueなど色々とあります。詳細は以下をご確認ください。
http://docs.python.jp/3/library/unittest.html#unittest.TestCase.assertEqual

基本的には上記のコードで十分にテストができます。必要なだけテストを書いて、プログラム本体の品質を担保します。



setUpとtearDown

setUpメソッドを用いることでテスト前に処理を実行できて、tearDownメソッドを用いることでテスト後に処理を実行することができます。
class MyTest(unittest.TestCase):

    # 各テスト前に呼び出される
    # DBのセットアップやテストデータの準備などを行う
    def setUp(self):
        print("setUp is called.")

    # 各テスト後に呼び出される
    # テストの後処理(成果物の削除)やDB処理の後処理などを行う
    def tearDown(self):
        print("tearDown is called.")

    def test_do_something(self):
        print("test_do_something")
        one = 1
        self.assertEqual(one, 1, "one is 1")

    def test_do_otherthing(self):
        print("test_do_otherthing")
        two = 2
        self.assertTrue(two == 2, "two is 2")

if __name__ == "__main__":
    unittest.main()
これを実行すると以下のように出力され、それぞれがテスト前後に実行されていることがわかります。
$ python test.py
setUp is called.
test_do_otherthing
tearDown is called.
.setUp is called.
test_do_something
tearDown is called.

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
各メソッドで共通的な前/後処理がある場合には便利です。



setUpClassとtearDownClass

直前の内容に似ていますが、setUpClasstearDownClassは、テストクラスの最初と最後にそれぞれ呼び出されます。
class MyTest(unittest.TestCase):

    # テスト開始時に1回だけ呼び出される
    # クラスメソッドとして定義する
    @classmethod
    def setUpClass(cls):
        print("setUpClass is called.")

    # テスト終了時に1回だけ呼び出される
    # クラスメソッドとして定義する
    @classmethod
    def tearDownClass(cls):
        print("tearDownClass is called.")

    def test_do_something(self):
        print("test_do_something")
        one = 1
        self.assertEqual(one, 1, "one is 1")

    def test_do_otherthing(self):
        print("test_do_otherthing")
        two = 2
        self.assertTrue(two == 2, "two is 2")

if __name__ == "__main__":
    unittest.main()
$ python test.py 
setUpClass is called.
test_do_otherthing
.test_do_something
.tearDownClass is called.

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
こちらも全テストで共通の前/後処理(で且つテスト中に不変なこと)があれば、ここで記述すると便利です。



テストのスキップ

unittest.skipデコレーターをメソッドやクラスにつけることで、そのテストをスキップすることができます。
# メソッドをスキップする場合
# カッコ内はスキップ理由などを書く
@unittest.skip("今は動かしたくない。そう自分のエゴだけどね。")
def test_do_thing_for_skip(self):
    print("skip this method, so it is never called.")
# クラスにも付与することができます
# テストスイートなどで使うときに重宝します
@unittest.skip("ちょっと時間がかかるから今はスキップ")
class MyTest(unittest.TestCase):
様々なテストを書いた場合に、一部のテストのみを実行したくてスキップする時が時々あります。



最後に

今日はPythonのユニットテストでした。今回は書けなかったけど今後にテストスイートについても書きたいと思います(業務ではよく使うので)。テストを書いて品質確保をぜひぜひやっていきたいところです(手動テストは非常に辛いから減らしたい・・・)。

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

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





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

RSS画像

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