Pytest入門 – 概要とその特徴について

pytest
目次

Pythonのテストフレームワーク

テストはソフトウェア開発プロセスにおいては必要不可欠な要素ですよね。

Pythonでの開発においても、多くのテストフレームワークが利用されています。

以下にPythonテストフレームワークとその特徴について上げました。

スクロールできます
Pythonテストフレームワーク特徴
unittest・Pythonの標準ライブラリの一部で、JavaのJUnitを模倣したもの
・テストケースはクラスベースで作成され、継承とセットアップ/ティアダウンメソッドを利用します
nose2・unittestの拡張版として開発され、より使いやすくなっている
・プラグインアーキテクチャを備えており、カスタマイズが容易

https://docs.nose2.io/en/latest/
doctest・docstring内にテストケースを書くことができ、ドキュメントとしても機能する

https://docs.python.org/ja/3/library/doctest.html
Pytest・シンプルでありながら強力な機能を持つ、非常に人気のテストフレームワークです。
・クラスベースでなく、関数ベースのテストをサポートしており、テストの書き方が直感的です。
Pythonのテストフレームワークの種類と特徴

Pytestのメリット

次にPytestの特徴的なメリットについて記載します。

シンプルな構文

Pytestはテスト関数をシンプルに書くことができます。

テストケースをクラスにまとめる必要あはなく、Pythonの関数として定義するというのが特徴です。

# test_example.py
def test_addition():
    assert 1 + 1 == 2

def test_subtraction():
    assert 5 - 3 == 2

強力なアサーション

Pytestのアサーションは非常に読みやすく詳細です。エラーメッセージも詳細で、デバッグが容易です。

# test_assertion.py
def test_list():
    a = [1, 2, 3]
    b = [1, 2, 3, 4]
    assert a == b

プラグインのサポート

Pytestでは、プラグインを使用して機能を拡張できます。例として、pytest-xdistを使用して並列テストを実行します。

公式サイトにおいては800以上のプラグインがあるとされています。

pip install pytest-xdist
pytest -n 4

フィクスチャ

Pytestのフィクスチャを使用して、テストデータや設定を簡単に共有できます。

# test_fixture.py
import pytest

@pytest.fixture
def sample_data():
    return {"key": "value"}

def test_example_1(sample_data):
    assert "key" in sample_data

def test_example_2(sample_data):
    assert sample_data["key"] == "value"

パラメータ化されたテスト

同じテストを異なるパラメータで簡単に実行できます。

# test_parameterized.py
import pytest

@pytest.mark.parametrize("a, b, expected", [(1, 2, 3), (2, 3, 5), (3, 5, 8)])
def test_addition(a, b, expected):
    assert a + b == expected

このようなテストを効率的に開発できる機能によってPytestは人気がある理由の一つだと思います。

テストコードを記載する観点について

Pytestでカバーする範囲としては単体テスト(UTともいう)の範囲になります。

単体テストの目的

単体テストの目的は、個々のユニット(関数、メソッド、クラス)が正しく動作することを確認することです。

現在Fast APIを利用したAPIの開発を行っています。

そのAPIの単体テストに焦点を当ててどのような観点があるかというと、

エンドポイントが期待通りのレスポンスを返すか、及び適切なサイドエフェクト(データベースの更新など)を持つか確認します。

APIの単体テストの観点

主なAPIテスト観点をチームとしてまとめておくことが必要です。

そうしなければ、それぞれが独自の視点でテストを行うことになってしまいテスト品質の統一がとれなくなってしまう=アプリケーションの品質低下につながるためです。

  1. ステータスコード
    • APIエンドポイントが正しいHTTPステータスコードを返しているか確認します。
  2. レスポンスの内容
    • 返されるデータが期待通りであるか確認します。
  3. エラーハンドリング
    • 不正なパラメータやリクエストで適切なエラーメッセージが返されるか確認します。
  4. セキュリティ
    • 認証や認可が適切に機能しているか確認します。
  5. サイドエフェクト
    • API呼び出しにより、データベース等の状態が期待通りに変更されるか確認します。

Pytestを使って上記観点を取り入れたイメージのコードを記載しました。

import pytest
import requests

# テスト対象のAPIのベースURL
BASE_URL = "http://example.com/api"

# GET メソッドのテスト
def test_get_method():
    # API呼び出し
    response = requests.get(f"{BASE_URL}/items/1")
    
    # ステータスコードの確認
    assert response.status_code == 200
    
    # レスポンスの内容確認
    assert response.json()["name"] == "Item1"

# POST メソッドのテスト
def test_post_method():
    # POSTで送信するデータ
    payload = {"name": "NewItem"}
    
    # API呼び出し
    response = requests.post(f"{BASE_URL}/items", json=payload)
    
    # ステータスコードの確認
    assert response.status_code == 201
    
    # データベースにアイテムが追加されたか確認
    new_item = requests.get(f"{BASE_URL}/items/{response.json()['id']}")
    assert new_item.json()["name"] == "NewItem"

# PUT メソッドのテスト
def test_put_method():
    # PUTで送信するデータ
    payload = {"name": "UpdatedItem"}
    
    # API呼び出し
    response = requests.put(f"{BASE_URL}/items/1", json=payload)
    
    # ステータスコードの確認
    assert response.status_code == 200
    
    # データベースのアイテムが更新されたか確認
    updated_item = requests.get(f"{BASE_URL}/items/1")
    assert updated_item.json()["name"] == "UpdatedItem"

# DELETE メソッドのテスト
def test_delete_method():
    # API呼び出し
    response = requests.delete(f"{BASE_URL}/items/1")
    
    # ステータスコードの確認
    assert response.status_code == 204
    
    # データベースからアイテムが削除されたか確認
    deleted_item = requests.get(f"{BASE_URL}/items/1")
    assert deleted_item.status_code == 404

各テスト観点における詳細な実施観点は以下の項目が考えられます。

テスト実施ケース作成時に取り込みを行うかチームの基準を設ける必要があるでしょう。

プロジェクトの特性、リソース、スケジュールを加味してどの機能、網羅性についてテスト計画時にテスト要件として盛り込むと、いざテスト実施時に人によってばらばらにならず、統一された品質で作りこむことができます。

APIの単体テストの観点の詳細

ステータスコードのチェック

ステータスコードはHTTPプロトコルにおいて、サーバーがクライアントにレスポンスの状態や結果を通知するために使用される3桁の数字です。これらのコードは、特定の範囲ごとに分類され、それぞれ異なる意味を持ちます。

ステータスコード概要代表的な値と意味
1xx (情報)リクエストは受け取られており処理中
2xx (成功)リクエストは正常に受け取られた状態200 OK: リクエストが成功した状態
201 Created: リクエストが成功し新しいリソースを作成した状態
例)POSTリクエストを使ってデータベースに新しいレコードを作成したときなど
3xx (リダイレクション)さらなるアクションが必要で、通常、ブラウザはこれらのアクションを自動的に行う301 Moved Permanently: リソースが恒久的に新しいURLに移動しています。
→WebページやAPIエンドポイントのURLが変更され、今後は新しいURLでアクセスされるべきであることをクライアントに通知するために使用されます。
例)ドメインの変更、URL構造の変更、 httpからhttpsにリダイレクト
4xx (クライアントエラー)クライアント側に何らかのエラーが存在してる状態400 Bad Request: サーバーがリクエストの構文を理解できません。
403 Forbidden: サーバーはリクエストを拒否しています。
404 Not Found: サーバーがリクエストされたリソースを見つけられません。
5xx (サーバーエラー)サーバー側に何らかの問題が発生した状態500 Internal Server Error: サーバーに内部エラーが発生し、リクエストを処理できません。

APIやWebアプリケーションのテスト時には、適切なステータスコードが返されることを確認することが重要です。これは、システムが正しく動作しているか、または予期されたエラーを適切に処理しているかを確認するためです。

レスポンスの内容

レスポンスの内容の検証は、APIテストやWebアプリケーションのテストにおいて非常に重要です。

想定するリクエストから返却される内容が間違った状態で異常が検知できないと、不正なデータの状態でシステムが稼働してしまう状態となるためです。

以下はレスポンスの内容を検証する際の主な観点です。

観点説明
データの正確性レスポンスで返されるデータの整合性チェック。
例えば、ユーザー情報を要求した場合、名前、メールアドレスなどの情報が期待通りかどうかをチェックします。
データ形式レスポンスデータの形式チェック。
JSON、XMLなどのデータ形式や、データの構造(キー名、階層など)に関連しています。
データの完全性必要なすべての情報がレスポンスに含まれているかチェック。
有効範囲の限界値入力が特定の範囲内である必要がある場合、その範囲の上限および下限の値でチェック。
例えば、年齢が0から100の範囲であるとすると、0と100の値を使用してテストして正常性を確認します。
無効範囲の限界値上記の範囲のすぐ外側の値(-1および101など)を使用して、システムがこれらの値を適切に拒否またはエラー処理するかどうかをテストします。
配列やリストの境界配列やリストの要素数が上限に達しているか、または空である場合の挙動をテストします。
テキストフィールドの長さテキスト入力フィールドに最小文字数や最大文字数の制限がある場合、これらの境界値でテストを行い、適切なエラーメッセージが表示されるかどうかを確認します。
日付と時間の境界日付や時間の入力において、月の最初と最後の日うるう年、時間の上限と下限(00:00, 23:59)など、特定の境界条件での動作をテストします。

エラーハンドリング

不正なパラメータやリクエストで適切なエラーメッセージが返されるか確認します。

エラーハンドリングは、ソフトウェアが予期しない状況や無効な入力に適切に対応する能力を確認するための重要なテスト要素となります。

以下は、エラーハンドリングを検証する際の観点です。

観点説明
適切なエラーメッセージエラーが発生した場合、適切なエラーメッセージが表示されているかをチェック。
エラーの種類に応じた処理異なるエラーの種類(例:バリデーションエラー、システムエラー、ネットワークエラー)に対して、アプリケーションが適切な対応をするかどうかのチェック。
入力バリデーションユーザーからの入力が無効または不適切な場合に、アプリケーションが適切にバリデーションエラーを捕捉し、ユーザーにフィードバックを提供するかを検証します。
ロギングと監視エラーが発生したときに、システムが適切にエラーログを記録し、必要に応じてアラートを生成するかどうかをチェック。
リソースリークとクリーンアップエラーが発生したときに、アプリケーションがオープンされたリソース(ファイルハンドル、データベース接続など)を適切に解放し、システムが安定して動作し続けるかどうかをチェック。

セキュリティ

アプリケーションがセキュリティの脅威に対して適切に保護されていることを確認するために、セキュリティは非常に重要なテスト要素の一つです。

以下は、セキュリティを検証する際の主な観点です。

観点説明
認証と認可ユーザーが適切に認証されているか(ログインしているか)、また認証されたユーザーが許可された操作のみを実行できるか(認可)を確認します。
SQLインジェクションの防止アプリケーションがユーザー入力を適切にサニタイズし、SQLインジェクション攻撃に対して耐性を持っているかどうかを検証します。
クロスサイトスクリプティング (XSS) の防止ユーザー入力が適切にエスケープされ、クライアントサイドのスクリプトが意図せず実行されないようにすることで、XSS攻撃に対する脆弱性を検証します。
データ暗号化データが適切に暗号化されているか検証します。これには、トランスポート層のセキュリティ(たとえば、HTTPSを使用した通信)およびデータベース内の機密データの暗号化が含まれます。
セキュリティヘッダーとCookieの設定セキュリティヘッダー(例:Content-Security-Policy)が適切に設定されているか、Cookieには適切なフラグ(HttpOnly、Secure)が設定されているかを検証します。

セキュリティは絶えず進化する分野であるため、常に最新のセキュリティプラクティスと脅威に注意を払うことも重要です。

サイドエフェクト

プログラムまたはシステム操作の結果として予期せずに発生する効果を指します。

これは特にAPIや複雑なシステムで重要です。

以下は、サイドエフェクトの検証観点の例です。

観点説明
データベースの整合性操作後にデータベースの整合性が保たれていることを確認します。
たとえば、あるアクションがデータベースの異なる部分に予期せぬ変更をもたらしていないか、関連するエントリ間のリレーションシップが損なわれていないか確認します。
外部システムへの影響システムが外部サービスやAPIと連携している場合、操作がこれらの外部システムに予期せぬ影響を及ぼしていないか確認します。
リソースの消費操作が予期せぬリソース(メモリ、CPUなど)の消費を引き起こしていないか確認します。これには、リークや性能の低下が含まれる場合があります。
状態の変更操作によってシステムの状態が予期せぬ方法で変更されていないか確認します。これには、グローバル変数、キャッシュ、または設定の変更が含まれる場合があります。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次