Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
like image 439
Piotr P Avatar asked Jun 23 '17 10:06

Piotr P


People also ask

Is pytest fixture a decorator?

pytest. fixture decorator makes it possible to inject the return value in the test functions whose have in their signature the decorated function name. Easy, isn't it? You can potentially generate and create everything you need in these fixture-functions and then use it in all the tests you need.

What is pytest fixture scope =' function ')?

Software test fixtures initialize test functions. They provide a fixed baseline so that tests execute reliably and produce consistent, repeatable, results. Initialization may setup services, state, or other operating environments.

Can a pytest fixture have arguments?

You can pass arguments to fixtures with the params keyword argument in the fixture decorator, and you can also pass arguments to tests with the @pytest.

Can pytest fixtures use other fixtures?

A fixture can use multiple other fixtures. Just like a test method can take multiple fixtures as arguments, a fixture can take multiple other fixtures as arguments and use them to create the fixture value that it returns.


3 Answers

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)
like image 150
rabrol Avatar answered Oct 23 '22 03:10

rabrol


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.

like image 9
Diego Mora Cespedes Avatar answered Oct 23 '22 03:10

Diego Mora Cespedes


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

like image 8
Enrique Saez Avatar answered Oct 23 '22 02:10

Enrique Saez