Showing posts with label moto. Show all posts
Showing posts with label moto. Show all posts

Wednesday, October 25, 2023

Moto: Alias Issue when Creating S3 Access Point is Fixed

 Two days ago, I discovered a small bug in the moto library that I use to unit test my lambdas on AWS. I needed to create an S3 Access Point with boto3, and while retrieving the its alias, I had different results when using the return value of create_access_point or get_access_point.

So I wrote a small unit test:

from moto import mock_s3control
import boto3

@mock_s3control
def test_access_point_alias():
    client = boto3.client("s3control")

    alias_from_create = client.create_access_point(
        AccountId="123456789012",
        Name="my-access-point",
        Bucket="MyBucket",
    )["Alias"]

    alias_from_get = client.get_access_point(
        AccountId="123456789012",
        Name="my-access-point",
    )["Alias"]

    assert alias_from_create == alias_from_get

I create an S3 Access Point, and retrieve its alias in two ways: from the response of the create_access_point function, and from the get_access_point function. On my moto 4.2.6, this test fails.

So I opened an issue on the project's repository. It was fixed and closed on the same day. That's reactivity!

Monday, January 2, 2023

Warnings on SQS endpoint URL while mocking with moto

 We have many unit tests using moto library to mock calls to boto3 to access AWS services.They all work fine, except for a warning on the use of this fixture:

@fixture(scope='function')
def sqs():
    with mock_sqs():
        yield boto3.client('sqs', region_name=AWS_REGION)

This is one of the correct ways of using moto mocks. But only this mock on SQS shows the following warning:

FutureWarning: The sqs client is currently using a deprecated endpoint: eu-central-1.queue.amazonaws.com. In the next minor version this will be moved to sqs.eu-central-1.amazonaws.com. See https://github.com/boto/botocore/issues/2705 for more details.

 Now, our Sonar is considering these warnings as errors, and leads our build to fail. As explained by the issue, the warning comes from the use of a deprecated attribute called sslCommonName. I didn't dive into the moto library, but there might be a way to avoid the use of this attribute. The easisest way to stop using it, is to add this environment variable to our test code:

import os
os.environ['BOTO_DISABLE_COMMONNAME'] = 'true'

This skips completely the use of the deprecated attribute.

Thursday, August 25, 2022

Modifying AWS resource for testing

 If you are writing unit tests using boto3 and moto, you might want to modify a value in a resource. I had this case, for instance, where I wanted to set the state of an AMI to pending for a test. Using moto, AMIs are always created in the available state. However, if you try to do it this way, it will fail:

ec2 = boto3.resource("ec2")
image = ec2.Image(AMI_ID)
image.state = "pending"

You will end up with this message:

AttributeError: can't set attribute 'state'

It is possible to modify the resource object. However, it will just modify this object instance, the model itself will not be updated. So if you use the resource as a parameter to a function, it will work. But if the function retrieves its data with boto3, it will get the original value.

Here is my solution:

image.meta.data.update({"State": "pending"})

That helped in my case. 

Thursday, April 15, 2021

Creating AWS resources in pytest fixtures with moto

 When unit testing functions that access AWS resources, the easiest way is to use the moto library. For instance, I have to test several functions that access an SQS queue, so I proceed this way:

import boto3
from moto import mock_sqs

def create_queue():
    client = boto3.client("sqs")
    queue_url = client.create_queue(QueueName="queue")["QueueUrl"]
    sqs = boto3.resource("sqs")
    return sqs.Queue(queue_url)

@mock_sqs
def test_something()
    queue = create_queue()
    ...

Since I had this create_queue() call at the beginning of most of my test functions, I wanted to make it a fixture. So i tried this way:

import boto3
from moto import mock_sqs
from pytest import fixture

@fixture
@mock_sqs
def queue():
    client = boto3.client("sqs")
    queue_url = client.create_queue(QueueName="queue")["QueueUrl"]
    sqs = boto3.resource("sqs")
    return sqs.Queue(queue_url)

@mock_sqs
def test_something(queue)
    ...

Unfortunately, this raised an error in my test functions stating that the queue does not exist. The reason for it is that the decorator @mock_sqs on the fixture would destroy my SQS queue as soon as it would leave the queue() method.

The solution is simple: do not use the mock as a decorator, but trigger it when the fixture is initializing and destroy it when it terminates. That means using a yield within the fixture to return the queue:

import boto3
from moto import mock_sqs
from pytest import fixture

@fixture
def queue():
    mock_sqs().start()

    client = boto3.client("sqs")
    queue_url = client.create_queue(QueueName="queue")["QueueUrl"]
    sqs = boto3.resource("sqs")
    yield sqs.Queue(queue_url)
    
    mock_sqs().stop()

def test_something(queue)
    ...

Notice that we have to drop the decorator on the test function as well.

Thanks for the answers to this moto issue.