Python テストコードの書き方について [Python] [Django]

Python テストコードの書き方について [Python] [Django]


donald chi / 2018-02-14 22:15

音声ファイル作成中...
Audio: Visited: 1096


Pythonには、unittestというフレームワークがあり、これを使うと素早く単体テストを自動化することができる。

unittestの基本的な使い方に関しては、検索すれば関連文章が山ほどある。

ただ、残念ながら、それらの文章は単なる使い方の紹介であり、単体テストの書き方に関しては紹介していない気がしたので、基本的なものではあるが、その内の一つの考え方を紹介する。

単体テストとは、関数、メソッドなどの小さな単位でプログラムが仕様にない振舞または欠陥(バグ)を出していないかをテストする作業のことを指す。(あくまでも個人の意見)

普段、我々は、プログラムを書きながら、サンプルデータを使ったり、プログラムの何らかのところで、変数の値を出力したりして、動作確認を行うが、この作業はテマがかかる。まだ、最悪プログラムに変化があるたびに似たような作業を繰り返さなければならない。

このような無駄な作業を自動化するための存在がunittest フレームワークである。unittestを使って、特定関数を呼び出し、実行結果を予測結果と比較するテストプログラムを書いて置くことで、毎回テストが必要な時に、このテストプログラムを使って、関数の振る舞いやバグをチェックすることができる。

例えば、以下のような書き方で

# get_sum.py
def get_sum(a, b):
    return a+b



# test_sum.py
import unittest
from get_sum import get_sum
class TestGetSum(unittest.TestCase):
    def test_sum(self):
            v1 = 3
            v2 = 5
            expected = 8
            actual = get_sum(v1, v2)
            self.assertEqual(expected, actual)

    if __name__ == "__main__":
            unittest.main()

上のような使い方はたくさんのブログで紹介されている。

実は、unittestでもっと重要な部分は、mockを使ったテストコードの作成ではないかと思う。

単体テストで、一個の関数ではなく、複数の関数を使って実装した小さい機能を単位とした場合、あるいは、関数単位でテストしたいが、関数間で依存関係が存在する場合はmockで出番である。

例えば、以下のような場合。

# get_divmod.py
def get_div(a, b):
    return a/b

def get_mod(a, b):
    return a%b

def get_divmod_product(a, b):
    div = get_div(a, b)
    mod = get_div(a, b)

    return div, mod

上のmoduleで、get_divmod_productをテストしようとする場合、get_divmod_product自体がget_divとget_modに依存するので、get_divmod_productのみのテストは不可能である。

この場合、mockを使うと、get_divmod_productを独立した関数と見なし、テスト可能になる。

mockは、関数やオブジエクトをモックオブジェクトで置き換え、元の関数やオブジエクトをブラックバックス化し、元の関数やオブジエクトが呼び出されたように、想定の戻り値を返すことができる。

get_divmod.pyでは、get_divget_modをmock化して、戻り値を設定することで、get_divmod_productをテストすることを可能にする。

テストコードの書き方は以下のようになる。

 # test_divmod.py
import unittest
from unittest.mock import Mock
from get_divmod import get_div, get_mod, get_divmod_product
class TestDivMod(unittest.TestCase):
    def define_mock():
        get_div = Mock()
        get_div.return_value = 3
        get_mod  = Mock()
        get_mod.return_value = 1 
    def test_divmod(self):
            v1 = 10
            v2 = 3
            expected = 3
            actual = get_divmod_product(v1, v2)
            self.assertEqual(expected, actual)

    if __name__ == "__main__":
            unittest.main()

以上は、簡単なmockを使った、単体テストコードになる。実は、Mockには、他にも色んな機能があり、関数の呼び出し回数をチェックしたり、呼び出されたか否かをチェックしたり、エラー処理をmock化したりなど多様なテストができる。詳しくは、オフィシャルサイトで。

以上。


0

0

Share with: Facebook Twitter Google+ LinkedIn Wechat Email Print
Tags: python

Comments: 0 件

There is no Comment now.

Add Comment

Name:
Email:
Comment: