☁️

motoでdynamoのモックを作成する

2023/03/12に公開

結論から。自分はこのように書いています。

from moto import mock_dynamodb
import pytest
import boto3
import logging


class TestHoge:

    @pytest.fixture(autouse=True)
    def dynamo_setup(self):
        with mock_dynamodb():
            # テーブル作成
            dynamodb = boto3.resource("dynamodb", region_name="ap-northeast-1")
            table = dynamodb.create_table(
                TableName="your_table",
                KeySchema=[
                    {
                        "AttributeName": "employee_id",
                        "KeyType": "HASH",
                    },
                ],
                AttributeDefinitions=[
                    {
                        "AttributeName": "employee_id",
                        "AttributeType": "N",
                    },
                ],
                BillingMode="PAY_PER_REQUEST",
            )
            table.wait_until_exists()

            # データ投入
            table.put_item(
                Item={
                    "employee_id": 1,
                },
            )
            yield

    def test_このようにテストケースを書く(self):
	# fixtureで作成したDynamoテーブルにアクセス出来る
        dynamodb = boto3.resource("dynamodb", region_name="ap-northeast-1")
        table = dynamodb.Table("your_table")
        try:
            # fixtureで投入したデータにアクセス出来る
            item = table.get_item(
                Key={
                    "employee_id": 1,
                },
            )
        except dynamodb.exceptions.ResourceNotFoundException:
            logging.error("Item not found")
	    raise

fixtureの中で、with mock_dynamodb()でモックのコンテキストを引き継がせた状態で、ジェネレータを使ってテーブルの作成や初期データの投入をしています。テーブル作成とデータ投入は別のfixture関数にして、後者はテストケース毎に異なるものを使うようにしても良いかもしれません。

補足: mock_dynamodb2ではデコレータが使えていた

mock_dynamodb2は、v4.0.0から無くなっているとのことです。

ご参照: https://qiita.com/shuyaeer/items/2edaae39011b19b3d16a

mock_dynamodb2の時は、このようにデコレータで記述することが出来ていました。

from moto import mock_dynamodb2


@mock_dynamodb2
class IntersectionMediationCreateTests(TestCase):

    @pytest.fixture(scope="function", autouse=True)
    @mock_dynamodb2
    def dynamo_setup(self):
        import boto3

        dynamodb = boto3.resource("dynamodb")
        table = dynamodb.create_table(
            TableName="your_table",
            KeySchema=[
                {
                    "AttributeName": "employee_id",
                    "KeyType": "HASH",
                },
            AttributeDefinitions=[
                    {
                        "AttributeName": "employee_id",
                        "AttributeType": "N",
                    },
            ],
        )

        table.batch.put_item(
            Item={
                "employee_id": 1,
            }
        )
        return table.name

あとは、最初の例と同じように、test_xxxとテストケースを書けば、fixtureで投入したデータを参照できました。

mock_dynamodbで同じ記述をすると、テストケースでfixtureで作成したテーブルにアクセス出来ず、ResourceNotFoundExceptionが出ました。デコレータで記述出来る方がシンプルな気がしますが、記述の仕方がいまだ分からずです。あまりこだわる所でもないので、前述のように書いてますが、何か記載の方法があればコメントください。

Discussion