Moto: Use moto mocks in pytest fixtures

Created on 15 Apr 2018  路  7Comments  路  Source: spulec/moto

Hi,

I am trying to create dynamodb tables in a pytest fixuture. Unfortantly it seems, that the tables are removed after the fixture has been run.

Here is an example project:

import pytest
from moto.dynamodb2 import dynamodb_backend2
from moto.dynamodb2 import mock_dynamodb2


@pytest.fixture()
def dynamodb_table():
    dynamodb_backend2.create_table("test-table", schema=[
        {u'KeyType': u'HASH', u'AttributeName': u'name'}])
    yield "test-table"

@mock_dynamodb2
def test_example(dynamodb_table):
    assert dynamodb_table in dynamodb_backend2.tables

Within the test, dynamodb_backend2.tables is empty.
Am I using the mock incorrectly how to do this?
Thanks

debugging

Most helpful comment

It's sufficient to use the mock_s3 context manager once in a fixture, no need to mock the test function again. Make sure that you yield the s3 client and not return it. This keeps the context manager active while the test is executed.

@pytest.fixture
def s3_client():
    with mock_s3():
        s3 = boto3.client('s3')
        yield s3

@pytest.fixture
def s3_bucket(s3_client):
    my_bucket = 'lalala'
    s3_client.create_bucket(Bucket=my_bucket)
    return my_bucket

class Test:
    def test_something(self, s3_client, s3_bucket):
        s3_client.put_object(Bucket=s3_bucket, Key='mykey', Body='test')

All 7 comments

I'm not positive the order of operations for pytest fixtures, but the following seems to work for me

```import pytest
from moto.dynamodb2 import dynamodb_backend2, mock_dynamodb2

@pytest.fixture()
def dynamodb_table():
with mock_dynamodb2():
dynamodb_backend2.create_table("test-table", schema=[
{u'KeyType': u'HASH', u'AttributeName': u'name'}])
yield "test-table"

def test_example(dynamodb_table):
assert dynamodb_table in dynamodb_backend2.tables
```

Cool, thank you!

I can't seem to make this work...:

@pytest.fixture
def s3_client():
    s3 = boto3.client('s3')
    return s3


@pytest.fixture
def s3_bucket(s3_client):
    with mock_s3():
        s3_client.create_bucket(Bucket='lalala')
        yield 's3-bucket'


class Test:
    @mock_s3
    def test_something(self, s3_client, s3_bucket):
        s3_client.put_object(Bucket='lalala', Key='', Body='')

I get:

E           botocore.errorfactory.NoSuchBucket: An error occurred (NoSuchBucket) when calling the PutObject operation: The specified bucket does not exist

I believe the problem is using the moto context manager inside of the fixture and the decorator around the test. Can you try just using one or the other?

It's sufficient to use the mock_s3 context manager once in a fixture, no need to mock the test function again. Make sure that you yield the s3 client and not return it. This keeps the context manager active while the test is executed.

@pytest.fixture
def s3_client():
    with mock_s3():
        s3 = boto3.client('s3')
        yield s3

@pytest.fixture
def s3_bucket(s3_client):
    my_bucket = 'lalala'
    s3_client.create_bucket(Bucket=my_bucket)
    return my_bucket

class Test:
    def test_something(self, s3_client, s3_bucket):
        s3_client.put_object(Bucket=s3_bucket, Key='mykey', Body='test')

@rzijp What if I need to get the boto3.resource().Bucket() object from the s3_bucket fixture, and this fixture should be independent of the s3_client() fixture? Meaning that some tests do not rely on s3_client but only on s3_bucket

I tried refactoring my fixtures like that but can't seem to get it to work.

@BigChief45 Not sure if I understand your challenge properly, could you elaborate please? Why would you need the two fixtures to be independent? Would you want to mock only the bucket fixture, while not mocking the client fixture?
The following combines the client and bucket into one fixture (but depending on your situation this might be cheating), to make the test only dependent on the s3_bucket fixture. Please note that that fixture is now yielding an actual bucket resource, while the previous version returned the name of the bucket as a string.
I also added an assertion to make the test more meaningful, for which I added the pytest built-in tmpdir fixture.

@pytest.fixture
def s3_bucket():
    with mock_s3():
        boto3.client('s3').create_bucket(Bucket='lalala')
        yield boto3.resource('s3').Bucket('lalala')


class Test:
    def test_something(self, s3_bucket, tmpdir):
        s3_bucket.put_object(Key='anykey', Body='anybody')
        s3_bucket.download_file(Key='anykey', Filename=str(tmpdir / "tmp.txt"))
        assert (tmpdir / "tmp.txt").read() == "anybody"
Was this page helpful?
0 / 5 - 0 ratings