Closed5

Python3_unittestモジュール, unittest.mockモジュール

かじるかじる

Python実践レシピより

Python3エンジニア認定実践試験メモ

テスト対象のコード

target.py
# coding: utf-8

# 正しい結果を返す関数
def div_ok(a, b):
    return a / b

# 間違えた結果を返す関数
def div_ng(a, b):
    return 1.0

テスト実行コード

main.py
import unittest
from target import div_ok, div_ng

# TestCaseクラスを継承
class DivTest(unittest.TestCase):
    # test_xxxの名前で関数を用意
    def test_the_div_of_two_integers(self):
        actual = div_ok(6, 3)# テスト対象を実行(正しい結果を返す関数)
        #actual = div_ng(6, 3)# テスト対象を実行(間違えた結果を返す関数)
        expected = 2.0
        self.assertEqual(actual, expected)# 値が等しいか確認

if __name__ == "__main__":
    unittest.main()# テストを実行する

テストの実行と結果

command
python3 main.py
output
div_ok関数の場合(正しい結果を返す関数をテストした場合)
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

div_ng関数の場合(間違った結果を返す関数をテストした場合)
F
======================================================================
FAIL: test_the_div_of_two_integers (__main__.DivTest.test_the_div_of_two_integers)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/xxx(省略)", line 20, in test_the_div_of_two_integers
    self.assertEqual(actual, expected)
AssertionError: 1.0 != 2.0

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)
かじるかじる

テスト対象のコード

target.py
# 正しい結果を返す関数
def div_ok(a, b):
    return a / b

# 間違えた結果を返す関数
def div_ng(a, b):
    return 1.0

テスト実行コード

main.py
import unittest
from target import div_ok

# 割り算の結果を格納している配列
# 1件目は正しい結果、2件目と3件目は間違った結果を格納
examples = [
    [6, 3, 2.0],
    [6, 2, 5.0],
    [6, 1, 4.0]
]

# TestCaseクラスを継承
class DivTest(unittest.TestCase):
    # test_xxxの名前で関数を用意
    def test_the_div_of_two_integers(self):
        # データを順番に取り出す
        for idx, example in enumerate(examples):
            # 1件づつテストデータを取り出す
            a, b, expected = example
            # subTestの引数にテストデータを渡す
            with self.subTest(f"{a}/{b}={expected}", idx=idx):
                self.assertEqual(div_ok(a, b), expected)# テスト実行(正しい結果を返す関数)

if __name__ == "__main__":
    unittest.main()# テストを実行する

テストの実行と結果

command
python3 main.py
output
FF
======================================================================
FAIL: test_the_div_of_two_integers (__main__.DivTest.test_the_div_of_two_integers) [6/2=5.0] (idx=1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/xxx(省略)", line 30, in test_the_div_of_two_integers
    self.assertEqual(div_ok(a, b), expected)# テスト実行
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 3.0 != 5.0

======================================================================
FAIL: test_the_div_of_two_integers (__main__.DivTest.test_the_div_of_two_integers) [6/1=4.0] (idx=2)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/xxx(省略)", line 30, in test_the_div_of_two_integers
    self.assertEqual(div_ok(a, b), expected)# テスト実行
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 6.0 != 4.0

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=2)
かじるかじる

テストの事前処理に使う

SetUp

main.py
import unittest

class SetUpAndSetUpClassTest(unittest.TestCase):

    def setUp(self):
        print("setUp!!")

    @classmethod
    def setUpClass(clz):
        print("setUpClass!!")

    def test_hop(self):
        print("test_hop!!")

    def test_step(self):
        print("test_step!!")

    def test_jump(self):
        print("test_jump!!")

if __name__ == "__main__":
    unittest.main()# テストを実行する

実行と結果

command
python3 main.py

実行の順番に注目

main.py
setUpClass!!
setUp!!
test_hop!!
.setUp!!
test_jump!!
.setUp!!
test_step!!
.
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK
かじるかじる

テストの事後処理に使う

TearDown

main.py
import unittest

class TearDownAndTearDownClassTest(unittest.TestCase):

    def tearDown(self):
        print("tearDown!!")

    @classmethod
    def tearDownClass(clz):
        print("tearDownClass!!")

    def test_hop(self):
        print("test_hop!!")

    def test_step(self):
        print("test_step!!")

    def test_jump(self):
        print("test_jump!!")

if __name__ == "__main__":
    unittest.main()# テストを実行する

実行と結果

command
python3 main.py

実行の順番に注目

output
test_hop!!
tearDown!!
.test_jump!!
tearDown!!
.test_step!!
tearDown!!
.tearDownClass!!

----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK
かじるかじる

MagicMock

テスト対象のクラス

api_shopping.py
class ShoppingAPI:

	def get_items(self, name):# 中身を確認(テスト対象)
		return ["商品1", "商品2", "商品3"]

	def add_item(self, name):# 追加(使っていない)
		pass

	def remove_item(self, name):# 削除(使っていない)
		pass

	def purchase_items(self):# 清算(使っていない)
		pass

実行と結果

main.py
from unittest.mock import MagicMock
from api_shopping import ShoppingAPI

# テスト対象のクラス
api = ShoppingAPI()

# テスト対象の関数をMagicMockで上書き
api.get_items = MagicMock()
print(api.get_items)# 上書きを確認
# <MagicMock id='4493138336'>

# テスト対象の関数の戻り値を設定する
api.get_items.return_value = ["モック1", "モック2", "モック3"]
print(api.get_items("商品1"))
# ['モック1', 'モック2', 'モック3']

# テスト対象の関数に例外を設定できる
api.get_items.side_effect = Exception("例外を設定します")
print(api.get_items("商品1"))
# Traceback (most recent call last):
# ...(省略)
# Exception: 例外を設定します
このスクラップは2ヶ月前にクローズされました