📌

pytest/PynamoDB/moto(v5) な環境でのモック方法と fixture 作成例

2024/04/24に公開

概要

Python で API を開発しています。
一部のデータは DynamoDB で管理しており、DynamoDB とのやり取りには PynamoDB を利用しています。

pytest と moto(v5) を使ってテストを作成したのですが、モックの方法で少し苦戦したので、備忘録に書いておきます。

ちなみに筆者は Python 初心者です。

環境

  • Python 3.8.12
  • PynamoDB 5.5.1
  • pytest 6.2.5
  • moto 5.0.5

備忘録

以下、テスト作成までの記録になります。

DynamoDB 環境のモック

まず、DynamoDB 環境のモックです。conftest.py に以下のコードを追加します。

import pytest
from moto import mock_aws # v5から mock_dynamodb はなくなった

# DynamoDB環境のモック
@mock_aws
@pytest.fixture(scope='session', autouse=True)
def mock_dynamodb():
    mock = mock_aws() 
    mock.start() # mock_aws.start で DynamoDB のモックが行われる

    UserRank.create_table()
    # 他にも作成すべき DynamoDB テーブルのモックがあればここに追加

    yield

    mock.stop()

これで DynamoDB 環境のモックができました。
実際の DynamoDB とデータのやり取りをすることなく、レコードの作成や読み出しが可能となります。

fixture 生成関数

次に PynamoDB モデルの fixture 生成関数です。こちらも conftest.py に追加。

from models import UserRank # PynamoDB の Model を継承して作成したモデル

# UserRank のfixture生成関数
@pytest.fixture
def dummy_user_rank_factory(dummy_user): # user は DynamoDB ではなく RDB で管理されるデータ
    def f(
            user=dummy_user,
            rank=999,
    ):
        UserRank(user_id=user.id, rank=rank).save()
    return f

# 他にも DynamoDB テーブルがあれば、以下に fixture 生成関数を追加

テストコード作成

あとは、実際のテストコードで fixture 生成関数を利用すれば OK です。

class TestUserRankView:
    def test_get(
        self,
        dummy_user_factory,
        dummy_user_rank_factory,
    ):
        user = dummy_user_rank_factory()
        dummy_user_rank_factory(user=user, rank=10)

        # /api/user_rank/<int:user_id> のようなAPIパスへのリクエスト
        response = ...

        assert response.json()['user']['id'] == user.id
        assert response.json()['user']['rank'] == 10

以上。

ハマりポイント

以下、個人的にハマったポイント。

  • PynamoDB モデルに host 指定をしているが、このせいで実際の DynamoDB に接続してしまっていた。テスト実行時に host 指定をしないよう修正。
  • mock_aws の start() だけで DynamoDB のモックまでできるとは思わず、ドキュメントを探し続けてしまった。

Discussion