Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SNS mocking with moto is not working correctly

In my unit test:

def test_my_function_that_publishes_to_sns():
    conn = boto3.client("sns", region_name="us-east-1")
    mock_topic = conn.create_topic(Name="mock-topic")
    topic_arn = mock_topic.get("TopicArn")

    os.environ["SNS_TOPIC"] = topic_arn

    # call my_function
    my_module.my_method()

The the function being tested

# inside my_module, my_function...
sns_client.publish(
            TopicArn=os.environ["SNS_TOPIC"], Message="my message",
        )

I get the error: botocore.errorfactory.NotFoundException: An error occurred (NotFound) when calling the Publish operation: Endpoint with arn arn:aws:sns:us-east-1:123456789012:mock-topic not found

Doesn't make sense, that's the topic moto is suppose to have created and mocked. Why is it saying it doesn't exist? If I call conn.publish(TopicArn=topic_arn, Message="sdfsdsdf") inside of the unit test itself it seems to mock it, but it doesn't mock it for my_module.my_method() which the unit test executes. Maybe it's destroying the mocked topic too soon?

EDIT I tried this every which way and I get the exact same error:

# Using context manager
def test_my_function_that_publishes_to_sns():
    with mock_sns():
        conn = boto3.client("sns", region_name="us-east-1")
        mock_topic = conn.create_topic(Name="mocktopic")
        topic_arn = mock_topic.get("TopicArn")
    
        os.environ["SNS_TOPIC"] = topic_arn
    
        # call my_function
        my_module.my_method()


# Using decorator
@mock_sns
def test_my_function_that_publishes_to_sns():
    conn = boto3.client("sns", region_name="us-east-1")
    mock_topic = conn.create_topic(Name="mocktopic")
    topic_arn = mock_topic.get("TopicArn")

    os.environ["SNS_TOPIC"] = topic_arn

    # call my_function
    my_module.my_method()


# Using decorator and context manager
@mock_sns
def test_my_function_that_publishes_to_sns():
    with mock_sns():
        conn = boto3.client("sns", region_name="us-east-1")
        mock_topic = conn.create_topic(Name="mocktopic")
        topic_arn = mock_topic.get("TopicArn")
    
        os.environ["SNS_TOPIC"] = topic_arn
    
        # call my_function
        my_module.my_method()

Opened GitHub issue as well: https://github.com/spulec/moto/issues/3027

like image 289
red888 Avatar asked Sep 05 '25 03:09

red888


2 Answers

maybe it will help you
keep all modules in a single class and put a decorator @mock_sns on the class too for mocking the sns, also put decorator @mock_sns on the function where you are initializing you connection to sns. Example:

@mock_sns
class TestSnsMock(unittest.TestCase):

    @classmethod
    @mock_sns
    def setUpClass(cls):
        cls.conn = boto3.client("sns", region_name="us-east-1")
        cls.conn.create_topic(Name="some-topic")
        cls.response = cls.conn.list_topics()
        cls.topic_arn = cls.response["Topics"][0]["TopicArn"]

    def test_publish_sns(self):
        message = "here is same message"
        self.sns_client.publish(TopicArn=self.topic_arn, Message=message)


if __name__ == "__main__":
    unittest.main()
like image 71
Saurav Jain Avatar answered Sep 07 '25 19:09

Saurav Jain


issue was my_module.my_method() wasn't setting a region just doing client = boto3.client("sns")

It could not find it because it was defaulting to a diff region than us-east-1 which was hard coded into the unit test

like image 20
red888 Avatar answered Sep 07 '25 20:09

red888