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)
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.
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.
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.
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.
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)
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With