(32) pytest入門:AgenticAIシステムのテスト基盤構築
こんにちは!今回は、AgenticAIシステムのテストにおいて欠かせないツール「pytest」について、基本的な使い方から実践的な活用法まで、一緒に学んでいきましょう。ReadyTensorのWeek 9 Lesson 2aでは、なぜpytestがエージェントシステムに最適なのか、そして実際にどのように使うかを詳しく解説しています。
なぜAgenticAIにpytestなのか?
Pythonでコードを書いて「これがずっと動作し続けてほしい」と思ったことがある方なら、pytestの存在価値を理解する準備はできています。pytestは、従来のテストフレームワークとは根本的に異なるアプローチを取っています。
従来のテストフレームワークでは、
2 + 2 == 4
をチェックするだけでも、クラスやボイラープレートコードを大量に書く必要がありました。しかし、pytestは正反対のアプローチを取ります。普通の関数を書き、普通のassert
文を使う、それだけです。これでテストが完成します。
このシンプルさこそが、AgenticAIシステムでpytestを使う最大の理由です。エージェントシステムが成長するにつれて、エージェントノード、ツール、レトリーバー、フォーマッター、フォールバックロジックなど、多くのコンポーネントが追加されます。これらすべてをテストする必要があるのに、複雑なテストフレームワークでは開発の足枷になってしまいます。
pytestの基本的な使い方
インストールと設定
pytestのインストールは非常に簡単です:
pip install pytest
ただし、実際のプロジェクトでは、テスト依存関係を明確に管理することが重要です。
requirements-test.txt
ファイルを作成して、テストに必要なパッケージをリストアップしましょう:
# requirements-test.txt
pytest>=8.4
これにより、本番システムとテストシステムの依存関係を分離できます。本番システムはテストツールを必要としないため、この分離により軽量なコンテナでの実行が可能になります。
最初のテストを書く
基本的な計算機の例から始めてみましょう。まず、テスト対象のコードを作成します:
# calculator.py
def add(a, b):
"""Add two numbers and return the result."""
return a + b
def multiply(a, b):
"""Multiply two numbers and return the result."""
return a * b
def divide(a, b):
"""Divide two numbers and return the result."""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
次に、対応するテストファイルを作成します:
# test_calculator.py
import pytest
from calculator import add, multiply, divide
def test_add_positive_numbers():
result = add(2, 3)
assert result == 5
def test_add_negative_numbers():
result = add(-1, -1)
assert result == -2
def test_multiply_basic():
result = multiply(4, 3)
assert result == 12
def test_divide_basic():
result = divide(10, 2)
assert result == 5.0
def test_divide_by_zero():
with pytest.raises(ValueError, match="Cannot divide by zero"):
divide(10, 0)
ここで注目すべきポイントは:
- 各テスト関数は
test_
で始まる - 結果の検証に普通の
assert
文を使用 - 例外の発生を期待する場合は
pytest.raises
を使用 - テストメッセージの内容も検証可能
テストの実行と結果の確認
テストを実行するには、プロジェクトディレクトリで以下のコマンドを実行します:
pytest
pytestは自動的に
test_<em>.py
または</em><em>test.py
にマッチするファイルを検索し、test</em>
で始まるすべての関数を実行します。
実行結果は以下のような形式で表示されます:
===================================== test session starts =========================================
collected 5 items
tests\test_calculator.py ..... [100%]
====================================== 5 passed in 0.03s ==========================================
各ドットは成功したテストを表し、失敗した場合は
F
が表示されます。この視覚的なフィードバックにより、テストの状況を即座に把握できます。
より複雑なテストの作成
クラスベースのテスト
実際のプロジェクトでは、より複雑なオブジェクトをテストする必要があります。銀行口座のクラスを例に見てみましょう:
# bank_account.py
class BankAccount:
def __init__(self, initial_balance=0):
if initial_balance < 0:
raise ValueError("Initial balance cannot be negative")
self._balance = initial_balance
@property
def balance(self):
return self._balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("Deposit amount must be positive")
self._balance += amount
return self._balance
def withdraw(self, amount):
if amount <= 0:
raise ValueError("Withdrawal amount must be positive")
if amount > self._balance:
raise ValueError("Insufficient funds")
self._balance -= amount
return self._balance
対応するテストクラスは以下のようになります:
# test_bank_account.py
import pytest
from bank_account import BankAccount
class TestBankAccount:
def test_initial_balance_default(self):
"""Test account creation with default balance."""
account = BankAccount()
assert account.balance == 0
def test_deposit_positive_amount(self):
"""Test depositing positive amount."""
account = BankAccount(100)
new_balance = account.deposit(50)
assert new_balance == 150
assert account.balance == 150
def test_withdraw_insufficient_funds(self):
"""Test that withdrawing more than balance raises error."""
account = BankAccount(50)
with pytest.raises(ValueError, match="Insufficient funds"):
account.withdraw(100)
pytest の高度な機能
フィクスチャーによるテストセットアップ
複数のテストで同じセットアップが必要な場合、フィクスチャーを使用して重複を避けることができます:
import pytest
from bank_account import BankAccount
@pytest.fixture
def sample_account():
return BankAccount(100)
@pytest.fixture
def two_accounts():
return BankAccount(100), BankAccount(50)
def test_deposit_with_fixture(sample_account):
sample_account.deposit(50)
assert sample_account.balance == 150
def test_transfer_with_fixture(two_accounts):
account1, account2 = two_accounts
account1.transfer(25, account2)
assert account1.balance == 75
assert account2.balance == 75
パラメータ化テスト
同じテストロジックを複数の入力で実行する場合、パラメータ化テストが有効です:
@pytest.mark.parametrize("a,b,expected", [
(1, 1, 2),
(2, 3, 5),
(-1, 1, 0),
(0, 0, 0),
])
def test_add_parametrized(a, b, expected):
assert add(a, b) == expected
pytestはこのテストを4回実行し、各入力の組み合わせを個別に検証します。
テストマーカーによる分類
テストにマーカーを付けることで、特定のテストグループを選択的に実行できます:
@pytest.mark.slow
def test_complex_operation():
1. 時間のかかる処理をシミュレート
import time; time.sleep(2)
assert True
@pytest.mark.integration
def test_external_api_call():
assert True
@pytest.mark.unit
def test_simple_math():
assert 2 + 2 == 4
実行時に特定のマーカーを指定できます:
# ユニットテストのみ実行
pytest -m "unit"
1. 遅いテストをスキップ
pytest -m "not slow"
1. 統合テストを実行
pytest -m "integration"
効率的なテスト実行
便利なコマンドラインオプション
pytestには、開発効率を向上させる多くのオプションがあります:
# より詳細なテスト出力を表示
pytest -v
1. 最初の失敗後に停止
pytest -x
1. 最も遅い10個のテストを表示
pytest --durations=10
1. 前回失敗したテストのみ実行
pytest --lf
1. 失敗したテストを最初に実行
pytest --ff
1. 並列でテストを実行(pytest-xdistが必要)
pytest -n auto
1. コードカバレッジレポートを生成(pytest-covが必要)
pytest --cov=code --cov-report=html
設定ファイルの活用
プロジェクトルートに
pytest.ini
ファイルを作成することで、デフォルトの設定を定義できます:
# pytest.ini
[pytest]
addopts = --verbose --tb=short
markers =
slow: mark tests as slow (use with -m "not slow")
integration: mark integration tests
unit: mark unit tests
python_files = test_*.py
この設定により、毎回同じオプションを入力する必要がなくなり、チーム全体で一貫したテスト実行環境を構築できます。
テスト組織化のベストプラクティス
ディレクトリ構造
テストファイルは、メインコードの構造をミラーリングする形で組織化することが推奨されます:
my_project/
├── code/
│ ├── data_processing/
│ │ └── loader.py
│ └── agents/
│ └── tagger.py
└── tests/
├── data_processing/
│ └── test_loader.py
└── agents/
└── test_tagger.py
効果的なテスト名
テスト名は、何をテストしているか、どのような条件下でテストしているかを明確に示すべきです:
# 良い例
def test_withdraw_insufficient_funds():
pass
def test_deposit_negative_amount_raises_error():
pass
1. 悪い例
def test_case_3():
pass
def test_error():
pass
AgenticAIシステムでのpytest活用
AgenticAIシステムでは、従来のソフトウェアテストに加えて、非決定的な出力を生成するLLMコンポーネントのテストも必要になります。pytestの柔軟性により、これらの複雑な要求にも対応できます。
次のレッスンでは、実際のエージェントコンポーネント(エージェントノード、ツールラッパー、プロンプトテンプレート、メモリマネージャー)のテスト方法を学び、変動性があるAIシステムでも信頼性を構築する方法を探求していきます。
pytestは、シンプルでありながら強力なテストフレームワークとして、AgenticAIシステムの品質保証における重要な基盤となります。基本的な使い方を習得することで、より複雑なAIシステムのテストにも対応できるようになるでしょう。
コメント