Python unittest

セクション6: doctest モジュール

doctest モジュールは、ドキュメンテーション文字列(docstring)内に記述されたサンプルコードを実行し、その出力が記述された期待値と一致するかをテストします。これにより、コード例の正確性を保証することができます。

6.1 基本的な使用方法

例:基本的な doctest

def add(a, b):
    """
    2つの数値を加算して返します。

    >>> add(2, 3)
    5
    >>> add(-1, 1)
    0
    >>> add(0, 0)
    0
    """
    return a + b

if __name__ == "__main__":
    import doctest
    doctest.testmod()

このコードを実行すると、関数 add のドキュメンテーション内のテストが実行されます。出力が期待値と一致するかを確認します。

doctestの詳細設定

オプションの使用

doctest では、さまざまなオプションを使用してテストの挙動をカスタマイズできます。

IGNORE_EXCEPTION_DETAIL: 例外の詳細を無視します。
NORMALIZE_WHITESPACE: ホワイトスペースの違いを無視します。

def divide(a, b):
    """
    2つの数値を除算して返します。

    >>> divide(4, 2)
    2.0
    >>> divide(1, 0)  # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ZeroDivisionError: division by zero
    """
    return a / b

if __name__ == "__main__":
    import doctest
    doctest.testmod(optionflags=doctest.IGNORE_EXCEPTION_DETAIL)

テスト結果の保存と表示

doctest の結果を保存し、表示することができます。

6.2 外部ファイルからのテスト

ドキュメントファイルからテストを実行することも可能です。

例:外部ファイルからの doctest

example.txt ファイルの内容:

>>> add(2, 3)
5
>>> add(-1, 1)
0

Pythonスクリプト:

def add(a, b):
    return a + b

if __name__ == "__main__":
    import doctest
    doctest.testfile("example.txt")

セクション7: unittest モジュール

unittest モジュールは、標準的なテストフレームワークであり、テストケース、テストスイート、テストランナー、テストフィクスチャなどの機能を提供します。

7.1 基本的な使用方法

例:基本的な unittest

import unittest

def add(a, b):
    return a + b

class TestAdd(unittest.TestCase):
    def test_add_positive(self):
        self.assertEqual(add(2, 3), 5)

    def test_add_negative(self):
        self.assertEqual(add(-1, 1), 0)

    def test_add_zero(self):
        self.assertEqual(add(0, 0), 0)

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

このコードを実行すると、テストケースが実行され、すべてのアサーションが成功するかどうかが確認されます。

アサーションメソッド

unittest には多くのアサーションメソッドが用意されています。以下はその一部です。

assertEqual(a, b): a と b が等しいことを確認します。
assertNotEqual(a, b): a と b が等しくないことを確認します。
assertTrue(x): x が真であることを確認します。
assertFalse(x): x が偽であることを確認します。
assertIs(a, b): a と b が同一オブジェクトであることを確認します。
assertIsNot(a, b): a と b が同一オブジェクトでないことを確認します。

import unittest

def add(a, b):
    return a + b

class TestAdd(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(2, 3), 5)
        self.assertNotEqual(add(2, 2), 5)
        self.assertTrue(add(1, 1) == 2)
        self.assertFalse(add(1, 1) == 3)

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

7.2 テストフィクスチャ

テストフィクスチャは、テストケースの実行前後にセットアップおよびクリーンアップを行うためのメソッドです。setUp と tearDown メソッドを使用します。

例:テストフィクスチャの使用

import unittest

class TestExample(unittest.TestCase):
    def setUp(self):
        self.data = [1, 2, 3]

    def tearDown(self):
        self.data = None

    def test_data_length(self):
        self.assertEqual(len(self.data), 3)

    def test_data_sum(self):
        self.assertEqual(sum(self.data), 6)

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

7.3 テストスイート

テストスイートは、複数のテストケースをまとめて実行するためのものです。

例:テストスイートの作成

import unittest

def add(a, b):
    return a + b

class TestAdd(unittest.TestCase):
    def test_add_positive(self):
        self.assertEqual(add(2, 3), 5)

    def test_add_negative(self):
        self.assertEqual(add(-1, 1), 0)

    def test_add_zero(self):
        self.assertEqual(add(0, 0), 0)

class TestSubtract(unittest.TestCase):
    def test_subtract(self):
        self.assertEqual(add(5, -2), 3)

if __name__ == "__main__":
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(TestAdd))
    suite.addTest(unittest.makeSuite(TestSubtract))
    
    runner = unittest.TextTestRunner()
    runner.run(suite)

セクション8: unittest.mock モジュール

unittest.mock モジュールは、モックオブジェクトを使用して依存関係をシミュレートし、単体テストを容易にするためのツールを提供します。

8.1 基本的なモックの使用

例:基本的なモック

from unittest import mock
import unittest

def get_data():
    # 実際のデータベースや外部APIからデータを取得する処理
    pass

def process_data():
    data = get_data()
    return data * 2

class TestProcessData(unittest.TestCase):
    @mock.patch('__main__.get_data', return_value=10)
    def test_process_data(self, mock_get_data):
        result = process_data()
        self.assertEqual(result, 20)
        mock_get_data.assert_called_once()

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

モックのパッチング

モジュールやクラスのメソッドをモックで置き換えます。

例:モジュールのメソッドのパッチング

from unittest import mock
import unittest

def get_data():
    return "real data"

class TestPatchExample(unittest.TestCase):
    @mock.patch('__main__.get_data', return_value="mocked data")
    def test_patch(self, mock_get_data):
        result = get_data()
        self.assertEqual(result, "mocked data")
        mock_get_data.assert_called_once()

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

8.2 サイドエフェクトの設定

モックオブジェクトにサイドエフェクトを設定することで、例外を発生させたり、複数の戻り値を指定したりできます。

例:サイドエフェクトの設定

from unittest import mock
import unittest

def get_data():
    raise ValueError("データを取得できませんでした")

def process_data():
    try:
        data = get_data()
    except ValueError as e:
        return str(e)

class TestProcessData(unittest.TestCase):
    @mock.patch('__main__.get_data', side_effect=ValueError("モックされたエラー"))
    def test_process_data(self, mock_get_data):
        result = process_data()
        self.assertEqual(result, "モックされたエラー")
        mock_get_data.assert_called_once()

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

まとめ

ここまでで、doctest、unittest、unittest.mock について詳しく解説しました。これらのモジュールを使うことで、コードのテストを効率的に行うことができます。特に、ドキュメントベースのテスト、自動化された単体テスト、依存関係のモックなど、さまざまなテスト手法を理解しておくことが重要です。


この記事が気に入ったらサポートをしてみませんか?