Python Unittestについての備忘録

2024/07/06に公開

最近、Pythonでテストコードを書く機会があったので
その際に学んだことをここに残していく

unittest とは?

https://docs.python.org/ja/3/library/unittest.html
Pythonの標準ライブラリに含まれている

テストコード

構造

unittestでは、テストケースはunittest.TestCaseクラスのインスタンス
自分でテストケースを作る場合にはTestCaseのサブクラスを作成する
unittest.main()を呼び出すことで、モジュール内の全テストケースを実行してくれる

import unittest

class TestCooking(unittest.TestCase): # TestCaseを継承
    # テストメソッドの記述

テストメソッド

テストメソッドは先頭をtestで始める

def test_cooking_calculateSeasoning(self) -> None:
    # ここに具体的なテスト内容を書く
テスト開始前・実施後に実行したい処理

setUp() : テスト実行前に自動で呼び出される
setUp()で例外発生した場合は、テストに問題があると判断されテストメソッドは実行されない
tearDown() : テスト実行後に呼び出される
setUp()が成功した場合は、テストメソッドの成功・失敗にかかわらず実行される

import unittest

class TestCooking(unittest.TestCase): # TestCaseを継承
    def setUp(self):
        # 実行する内容

    def tearDown(self):
        # 何も実行しないでpassすることもできる
        pass
テスト用の関数たち(以下に書いたもの以外にもいっぱいある)
メソッド 判定
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)
def test_cooking_calculateSeasoning(self) -> None:
    # テストしたいクラス
    cooking = Cooking()

    # 確認したいクラスのメソッドの実行結果
    calculatedSeasoning = cooking.calculateSeasoning()

    self.assertEqual(
        calculatedSeasoning,
        "10g",
    )

mock

モックとは実際のオブジェクトやコンポーネント等を模造したもの
例えば以下のようなクラスがあった時

class Member:
    def getStatus(self) -> dict:
        # API実行してメンバーのステータスを取得
        apiResponse = ApiUtil.getMemberStatus()
        return {
            "type": apiResponse.get("type"),
            "plan": apiResponse.get("plan"),
        }

    def isLimited(self) -> bool:
        contents = self.getStatus()
        if contents["type"] is None or contents["plan"] is None:
            return True
        return False

MemberクラスのisLimited()をテストしたいけど、、、
テスト実行されるごとにgetStatus()が実行されてAPIが呼び出される、、、
→ getStatus()の部分をモックにしてテストを行う

import unittest
from unittest.mock import patch

class TestMember(unittest.TestCase):
    # patchデコレータでテスト対象のモジュール内のクラスやオブジェクトをモックにする
    @patch('ApiUtil.getMemberStatus')
    def test_getStatus(self, mock_get_member_status):
        # ApiUtil.getMemberStatus()のモックを設定
        mock_get_member_status.return_value = {"type": "premium", "plan": "annual"}
        
        member = Member()
        result = member.getStatus()
        
        self.assertEqual(result, {"type": "premium", "plan": "annual"})

モックを使うことで外部APIへの依存をせずにテストを行うことができる

Discussion