pynamodb / PynamoDB

A pythonic interface to Amazon's DynamoDB
http://pynamodb.readthedocs.io
MIT License
2.44k stars 427 forks source link

PynamoDB connection with moto #1167

Open eherozhao opened 1 year ago

eherozhao commented 1 year ago

I have a low level pynamodb update_item cal in a Model using PynamoDB Connection as follows.

def low_level_update(...):
    connection = Connection(region=AWS_REGION)
    connection.update_item(
        table_name=File.Meta.table_name,
        ...
    )

When I added tests for this method, it throw using TableConnection directly (not through a model) raised the exception.. The reason might be the table I created in the test was by boto api which was mocked by moto rather than pynamoDB model.

def ddb_client():
    with mock_dynamodb():
        yield boto3.client("dynamodb", region_name="us-west-2")
...
ddb_client.create_table(...)

So after I added connection.describe_table in the method and it worked well. When I put connection.describe_table into test case and passed the connection instance into the method as an argument, it throw the moto KeyError: 'Key' error without further logs. I am confused about the difference between the connection in pynamo model with moto.

Does anyone have any thoughts about it? Thank you in advance!

ikonst commented 1 year ago

Normally you'd want to surround your entire test with the mock_dynamodb context, not merely create the client within it.

For example, with pytest you might:

@pytest.fixture(autouse=True)
def mock_dynamodb_fixture():
    with mock_dynamodb():
        yield
eherozhao commented 1 year ago
@pytest.fixture(autouse=True)
def ddb_client():
    with mock_dynamodb():
        yield boto3.client("dynamodb", region_name="us-west-2")

I do have this context, do you mean I need another one?

ikonst commented 1 year ago

You've omitted the @pytest.fixture(autouse=True) in your previous message so I couldn't have known.

In any case, yielding the boto3 client is somewhat misleading, since any other client you would create while inside the context would also be mocked.

eherozhao commented 1 year ago

I used @pytest.fixture(autouse=True) in my local code. If you don't yield boto3.client, how do you get a aws boto client to use?

@pytest.fixture(autouse=True)
def mock_dynamodb_fixture():
    with mock_dynamodb():
        yield
ikonst commented 1 year ago

Simply - the mock_dynamodb_fixture is effectively providing the setup (start moto patching) and teardown (stop moto patching) for all your tests (since autouse=True):

@pytest.fixture(autouse=True)
def mock_dynamodb_fixture():
    with mock_dynamodb():
        yield

def test_foobar():
  my_client = boto3.client("dynamodb", region_name="us-west-2")
  my_client.do_something()

This is equivalent to not having the autouse fixture and rewriting test_foobar as:

def test_foobar():
  with mock_dynamodb():
    my_client = boto3.client("dynamodb", region_name="us-west-2")
    my_client.do_something()