Moto: Issue with Moto and creating pytest fixture

Created on 24 May 2016  路  7Comments  路  Source: spulec/moto

Hello

I have been trying to create a pytest fixture for s3 that contains a bucket that is created in the fixture.

@pytest.fixture
@mock_s3
def s3_hook():

    result = sut.S3Hook()
    result.location = 'test_location'

    result.connection = boto3.client('s3')
    result.connection.create_bucket(Bucket=result.location)
    return result

My test looks like this:

@mock_s3
def test_check_existing_file(s3_hook):

    saved_string = "Just writing some tests."

    s3_hook.connection.put_object(Bucket=s3_hook.location,
                                  Key='test_file',
                                  Body=saved_string)

    assert s3_hook.check_for_file('test_file') 

This fails with the message

<Message>The specified bucket does not exist</Message>

If I update the test to create the bucket at the start of the test the test starts passing.

s3_hook.connection.create_bucket(Bucket=s3_hook.location)

Any thoughts on this?

I am using:
pytest==2.8.0
moto==0.4.24

Thanks

Most helpful comment

For pytest, I've had success making a fixture like this:

@pytest.yield_fixture(scope="function")
def s3_fixture():
    mock_s3().start()

    client = boto3.client("s3")
    resource = boto3.resource("s3")

    yield client, resource

    mock_s3().stop()

And then just using the fixture in my tests:

def test_s3_thing(s3_fixture):
    client = s3_fixture[0]
    client.create_bucket(Bucket="some-test-bucket")

You don't want to mock_s3() again before your test.

All 7 comments

When using the decorator the state gets cleared right after s3_hook() gets executed, so in test_* you're getting a brand new state. You could get around that by creating the mock object manually (note that m would have to be accessible by both methods):

m = mock_s3()
m.start()

Then running m.stop() after the test runs; not sure how to do that with pytest fixtures though.

For pytest, I've had success making a fixture like this:

@pytest.yield_fixture(scope="function")
def s3_fixture():
    mock_s3().start()

    client = boto3.client("s3")
    resource = boto3.resource("s3")

    yield client, resource

    mock_s3().stop()

And then just using the fixture in my tests:

def test_s3_thing(s3_fixture):
    client = s3_fixture[0]
    client.create_bucket(Bucket="some-test-bucket")

You don't want to mock_s3() again before your test.

I've had success making a fixture like this

Me too. You know, I feel like you just deprecated pytest-moto with that!

Going to close this. Feel free to reopen if this solution doesn't work.

@mikegrima ty for the "you don't want to mock again before test" line. fixed my problem!

Fixing double instantiation of mock_s3() in @mikegrima's above comment (breaks in latest version of moto) referenced here: https://github.com/spulec/moto/issues/2559

@pytest.yield_fixture(scope="function")
def s3_fixture():
    mocks3 = mock_s3()
    mocks3.start()

    client = boto3.client("s3")
    resource = boto3.resource("s3")

    yield client, resource

    mocks3.stop()

Using a context manager:

@pytest.yield_fixture(scope="function")
def s3_fixture():
    with mock_s3():
        client = boto3.client("s3")
        resource = boto3.resource("s3")

        yield client, resource
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dazza-codes picture dazza-codes  路  3Comments

kevgliss picture kevgliss  路  4Comments

muhammad-ammar picture muhammad-ammar  路  3Comments

domingosnovo picture domingosnovo  路  5Comments

mfogel picture mfogel  路  4Comments