Piotr P
Piotr P

Reputation: 241

mock_s3 decorating pytest fixture

I wonder why mock_s3 decorator doesn't work when used as a decorator for pytest fixture. test_with_fixture fails while it provides the same code as the test_without fixture. Well, "the same" as it is decorated explicitly.

test_with_fixture raises AccessDenied error, but the type of S3 error it not relevant in this case. The problem is that, client.list_objects is not mocked in the test which uses fixture.

pytest - 3.1.2
moto - 1.0.1
boto3 - 1.0.4

import pytest
import boto3

from moto import mock_s3

BUCKET = 'Foo'


@pytest.fixture()
@mock_s3
def moto_boto():
    res = boto3.resource('s3')
    res.create_bucket(Bucket=BUCKET)


def test_with_fixture(moto_boto):
    client = boto3.client('s3')
    client.list_objects(Bucket=BUCKET)


@mock_s3
def test_without_fixture():     
    res = boto3.resource('s3')
    res.create_bucket(Bucket=BUCKET)

    client = boto3.client('s3')
    client.list_objects(Bucket=BUCKET)

Upvotes: 24

Views: 14713

Answers (3)

Diego Mora Cespedes
Diego Mora Cespedes

Reputation: 3872

Using a context manager:

import pytest
import boto3

from moto import mock_s3

BUCKET = 'Foo'


@pytest.fixture()
def moto_boto():
    with mock_s3():
        res = boto3.resource('s3')
        res.create_bucket(Bucket=BUCKET)
        yield


def test_with_fixture(moto_boto):
    client = boto3.client('s3')
    client.list_objects(Bucket=BUCKET)

Using the context manager, start and stop are invoked under the hood.

Upvotes: 13

rabrol
rabrol

Reputation: 181

An alternative is to use an 'autouse' test fixture in which you start and stop the moto server and create your test bucket.

This is based on mikegrima's comment on https://github.com/spulec/moto/issues/620.

import pytest
import boto3

from moto import mock_s3

BUCKET = 'Foo'


@pytest.fixture(autouse=True)
def moto_boto():
    # setup: start moto server and create the bucket
    mocks3 = mock_s3()
    mocks3.start()
    res = boto3.resource('s3')
    res.create_bucket(Bucket=BUCKET)
    yield
    # teardown: stop moto server
    mocks3.stop()


def test_with_fixture():
    client = boto3.client('s3')
    client.list_objects(Bucket=BUCKET)

Upvotes: 18

Enrique Saez
Enrique Saez

Reputation: 2700

The problem of your fixture is that you are not using it later although it is in the signature of your test test_with_fixture(moto_boto). I suggest you to create a fixture that returns a function that can be instantiated within your test to create the mocked objects that your test requires (the s3 bucket). An example of such an implementation could be as follows:

import pytest
import boto3

from moto import mock_s3

BUCKET = 'Foo'

@pytest.fixture()
def moto_boto():
    @mock_s3
    def boto_resource():
        res = boto3.resource('s3')
        res.create_bucket(Bucket=BUCKET)
        return res
    return boto_resource

@mock_s3
def test_with_fixture(moto_boto):
        moto_boto()
        client = boto3.client('s3')
        client.list_objects(Bucket=BUCKET)

In this case I am using the moto library through a decorator in both the fixture and the test but the context manager could be similarly used as explained in the moto README

Upvotes: 10

Related Questions