🧪 Pytest上級編:モックと例外処理、CI/CD連携まで
🚀 はじめに
この上級編では、Pytestをさらに強力に活用するための実践的テクニックを紹介します。具体的には、テストの対象となる関数や外部依存(API、データベース、ファイルシステムなど)をどのように扱うか、例外が発生する状況での適切なテスト方法、そしてGitHub Actionsなどの継続的インテグレーション(CI)ツールとどのように連携してテストの自動化を行うかを学びます。
これらの技術は、大規模開発やチーム開発において品質保証の要となるものであり、信頼性の高いコードベースの構築に不可欠です。特に外部要因に依存しない安定したテスト、再現性のあるテスト環境、迅速なフィードバックの提供が求められる現場では、今回紹介する上級テクニックが大いに役立ちます。
🎭 モック(Mock)の活用
外部API、データベースアクセス、ファイル読み書きなど、テスト中に実際に実行したくない処理を安全に置き換えるために「モック」を使います。Python標準ライブラリの unittest.mock や、よりPytestと親和性の高い pytest-mock を使うことで、柔軟かつ簡潔にモック処理を実現できます。
モックの基本:unittest.mock
from unittest.mock import Mock
def get_username(api):
return api.get_user()['name']
def test_get_username():
fake_api = Mock()
fake_api.get_user.return_value = {'name': 'Taro'}
assert get_username(fake_api) == 'Taro'
この例では、外部の api.get_user() の挙動を任意の値に置き換えてテストしています。これにより、実際のAPIアクセスをせずに関数のロジックのみを確認できます。
pytest-mock を使った例:
pip install pytest-mock
def get_status():
import requests
r = requests.get("https://example.com")
return r.status_code
def test_status_code(mocker):
mock_response = mocker.Mock()
mock_response.status_code = 200
mocker.patch("requests.get", return_value=mock_response)
assert get_status() == 200
Pytestでモックを活用することで、テストの信頼性と実行スピードを両立できます。特にCI環境などで実行する際には、外部依存を完全に排除するのが理想です。
⚠️ 例外処理のテスト
エラーや例外が発生する状況をテストすることも重要です。Pytestの raises() を使えば、指定した例外が正しく発生したかを簡潔に検証できます。
基本例:
import pytest
def divide(x, y):
if y == 0:
raise ValueError("0で割ることはできません")
return x / y
def test_divide_by_zero():
with pytest.raises(ValueError, match="0で割る"):
divide(10, 0)
ここでは、ゼロ除算時に ValueError が発生し、エラーメッセージも期待通りであることを確認しています。例外テストを正確に行うことで、エラーハンドリングの健全性を担保できます。
複数の例外タイプを扱うケース
def access_file(path):
if not isinstance(path, str):
raise TypeError("パスは文字列である必要があります")
raise FileNotFoundError("ファイルが見つかりません")
def test_access_file_type():
with pytest.raises(TypeError):
access_file(123)
def test_access_file_not_found():
with pytest.raises(FileNotFoundError):
access_file("/invalid/path")
🤖 継続的インテグレーション(CI)との連携
CIを導入することで、コードが更新されるたびに自動でテストが実行されるようになります。これにより、人為的ミスを減らし、変更の影響を迅速に把握できます。
GitHub Actions の例:.github/workflows/test.yml
name: Run Pytest
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: pytest --tb=short -v
これを設定すれば、GitHub上のプルリクエストごとにテストが走り、結果が可視化されます。自動化されたテストの導入は、プロジェクトの信頼性と開発スピードの両方を大幅に向上させます。
📦 より高度なテスト技法
以下はさらに発展的なテスト設計テクニックです:
- パラメータの動的生成:CSVやJSONファイルなどから読み込んだデータを使って
parametrizeを自動構築 - フィクスチャのスコープ管理:
scope="session"やscope="module"を用いて、セットアップの実行効率を最適化 - 依存関係の注入(DI):引数ベースで依存性を渡し、テスト用の疑似オブジェクトやモックと差し替え可能にする
@pytest.fixture(scope="module")
def db_connection():
conn = create_connection()
yield conn
conn.close()
これらを組み合わせることで、再利用性と柔軟性の高いテスト環境を構築できます。
🔺 まとめ
この上級編では、実践的かつ応用的なPytestの使い方を網羅的に紹介しました。特に次の点を学びました:
- モックによる外部依存の排除とテストの独立性の確保
-
pytest.raisesを用いた例外発生の精密なテスト - GitHub ActionsなどのCIツールを活用した継続的テストの実装
- 動的パラメータ生成、スコープ制御、DIによる柔軟なテスト設計
Pytestの真価は、こうした高度な構成を組み合わせて、保守しやすく信頼性の高いテストスイートを作ることにあります。継続的なテスト自動化を通じて、チーム全体の開発体験を向上させましょう。
株式会社ONE WEDGE
【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
Discussion