【unittest】Pythonで単体テスト:unittestモジュールの基本的な使用方法

概要

unittestモジュールを使用して単体テストを作成/実施する方法についてまとめた。

 

unittestモジュールのポイント

 

・unittestモジュールをインポートする
・unittest.TestCaseを継承してクラスを作成する
・慣習として「Test」をクラスの頭につける
・慣習として「test_」を関数の頭につける
・テストコードにて「実行結果」と「想定結果」を比較する

 

 

unittestモジュールの使い方

テスト対象の関数用意

まずはテスト対象の関数が存在すること。


# テスト対象の関数
def culc_multi(a, b):
    """ aとbを乗算した結果を返却 """
    return a * b

 

テストケースを作成

unittestモジュールをインポートして、テストケースを作成する。


import unittest 

# テスト対象の関数 
def culc_multi(a, b): 
    """ aとbを乗算した結果を返却 """ 
    return a * b 

# テストクラス 
class TestCulcAdd(unittest.TestCase): 
    # テストケース
    def test_culc_add(self): 
        """ 関数culc_multiが2つの引数の乗算を返すか確認. """ 
        self.assertEqual(culc_multi(3, 9), 27)

 

テスト実行方法

テスト実行方法については様々存在する。
基本的にはunittest.main()を呼び出して実行するが、jupyter labの場合は多少異なる

 

jupyter labにてテストを実行する方法

jupyter labにてunittest.main()を実行するとAttributeErrorが発生する。
以下のように記述すると、テストを実行できる。


import unittest

# テスト対象の関数
def culc_multi(a, b):
    """ aとbを乗算した結果を返却 """
    return a * b

# テストクラス
class TestCulcAdd(unittest.TestCase):
    def test_culc_add(self):
        """ 関数culc_multiが2つの引数の乗算を返すか確認. """
        self.assertEqual(culc_multi(3, 9), 27)

if __name__ == '__main__':
    #unittest.main() # AttributeError: module '__main__' has no attribute・・・というエラー
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

#----------------------------------------------------------------------
# 出力
#Ran 1 test in 0.002s
#
#OK

テストの実行時間と実行結果OKが出力される。
【if __name__(略)】の書き方が不明な場合、直接【unittest.main(argv=[‘first-arg-is-ignored’], exit=False)】と記述しても実行は可能。

 

Pythonファイルにてテストを実行する方法

jupyter labを使用している場合、以下の手順でPythonファイルを作成する。

New Launcherの立ち上げ(Ctr+Shit+L)
▲上記のPython Fileを選択

 

main.py(名前はなんでもいい)として、コードを張り付ける

Pythonファイルにテスト対象の関数とテストコードを張り付ける。
尚、実行関数はunittest.main()とすること。

▲main.pyにテスト対象の関数とテストコードを張り付ける。テスト実行コードはunittest.main()

 

ターミナルからPythonファイルの実行

ターミナルを起動。

▲上記のターミナルから起動

 

以下のコマンドを実行する。

 

ターミナル


python main.py(※main.pyの箇所は、作成したPythonファイル名)

 

▲テスト実行結果

 

Pythonファイルを指定して実行

Pythonファイルからunittest.main()の記述を削除。


import unittest

# テスト対象の関数
def culc_multi(a, b):
    """ aとbを乗算した結果を返却 """
    return a * b

# テストクラス
class TestCulcAdd(unittest.TestCase):
    def test_culc_add(self):
        """ 関数culc_multiが2つの引数の乗算を返すか確認. """
        self.assertEqual(culc_multi(3, 9), 27)

 

ターミナルから以下を実行することで、Pythonファイルに記述されているすべてのテストメソッドを実行可能。

ターミナル


python -m unittest main.py

 
特定のテストメソッドのみ実行

特定のテストメソッドを実行には以下のように、【ファイル名.クラス名.テストメソッド名】を指定して実行する。

ターミナル


python -m unittest main.TestCulcAdd.test_culc_add

 

テストメソッド(assertXXX)の使い方

unittest.TestCaseクラスに、テスト対象の「実行結果」と「想定結果」を比較するテストメソッドが用意されている。
TestCaseを継承することで使用可能になる。
主なテストメソッドは以下。

種類 詳細
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) x == True
assertFalse(x) x == False

 

以下はもともと存在するStringの関数をテストしたもの。
assert文は以下のように使用する。


import unittest


class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        """ str.upper()メソッドが大文字を返すか確認 """
        self.assertEqual("test".upper(), "TEST")

    def test_not_upper(self):
        """ str.upper()メソッドがそのままの文字列を返さない確認 """
        self.assertNotEqual("test".upper(), "test")

    def test_isdigit(self):
        """ str.isdigit()メソッドが正しく機能するか確認(True) """
        self.assertTrue("543".isdigit())

    def test_not_isdigit(self):
        """ str.isdigit()メソッドが正しく機能するか確認(False) """
        self.assertFalse("543a".isdigit())

    def test_not_isdigit_2(self):
        """ テスト結果を誤るケースその1 """
        self.assertFalse("321".isdigit()) # AssertionError: True is not false

    def test_upper_2(self):
        """ テスト結果を誤るケースその2 """
        self.assertEqual("test".upper(), "test") # AssertionError: 'TEST' != 'test'


if __name__ == "__main__":
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

#-----------------------------------------------
# 出力
#Ran 6 tests in 0.004s
#
#FAILED (failures=2) ※エラーの数

テスト結果が正しくない場合、「FAILED (failures=★)」と表示される。
※★⇒テストエラーの数

 

リストや辞書のテスト方法

リストや辞書も同様にassertEqualで比較可能。


import unittest


# Utilクラス
class CreateUtils:

    @staticmethod
    def create_nums(num):
        """ 引数から始まる10個の数で終わるリストを返却 """
        return list(range(num, num + 10))

    @staticmethod
    def create_dict(items):
        """ リストで受け取った値をインデックスと紐づけた辞書にして返却 """
        result_dict = {}
        for i, val in enumerate(items, 1):
            result_dict[i] = val

        return result_dict


# Utilテストクラス
class TestCreateUtils(unittest.TestCase):

    def test_create_nums(self):
        """ create_numsが正しい結果を返却するか確認 """
        expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        actual = CreateUtils.create_nums(1)
        self.assertEqual(expected, actual)

    def test_create_dict(self):
        """ create_dictが正しい結果を返却するか確認 """
        expected = {
            1: "りんご",
            2: "ごりら",
            3: "らっぱ"
        }
        actual = CreateUtils.create_dict(["りんご", "ごりら", "らっぱ"])
        self.assertEqual(expected, actual)


if __name__ == "__main__":
    unittest.main(argv=['first-arg-is-ignored'], exit=False)
#----------------------------------------------------------------------
#Ran 2 tests in 0.004s
#OK

 

FizzBuzzのテスト

 


import unittest


def fizzbuzz(num):
    if num % 15 == 0:
        return "FizzBuzz"
    elif num % 3 == 0:
        return "Fizz"
    elif num % 5 == 0:
        return "Buzz"
    else:
        return str(num)


class TestFizzBuzz(unittest.TestCase):

    def test_fizzbuzz_FizzBuzz(self):
        """ 15の倍数のテスト """
        self.assertEqual("FizzBuzz", fizzbuzz(90))

    def test_fizzbuzz_Fizz(self):
        """ 3の倍数のテスト """
        self.assertEqual("Fizz", fizzbuzz(33))

    def test_fizzbuzz_Buzz(self):
        """ 5の倍数のテスト """
        self.assertEqual("Buzz", fizzbuzz(50))

    def test_fizzbuzz_other(self):
        """ 3と5の倍数以外 """
        self.assertEqual("98", fizzbuzz(98))


if __name__ == "__main__":
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

#------------------------------------------------------------
#Ran 4 tests in 0.002s
#OK

スポンサーリンク