Repository: getmoto/moto
Pull request #6634 (2023-08-11 19:22:11)

Techdebt: Replace sure with regular assertions in SQS
See https://github.com/getmoto/moto/issues/6035

This one is a little tricky because of the use of "sure" constructions in pytest parameterizations.  Also, one file is large.


kbalk: There was one other gotcha.  I made the following replacement, which seemed correct:

```
     working_url = q_response["QueueUrl"]
     # https://queue.amazonaws.com/012341234/test-queue
     # http://localhost:5000/012341234/test-queue in ServerMode
-    working_url.should.match(f"/{ACCOUNT_ID}/{name}")
+    assert re.match(f"/{ACCOUNT_ID}/{name}", working_url)
```
 
However, that failed as the leading part of the string contained other info:
```
=================================== FAILURES ===================================
__________ test_receive_message_using_name_should_return_name_as_url ___________

    @mock_sqs
    def test_receive_message_using_name_should_return_name_as_url():
        sqs = boto3.resource("sqs", region_name="us-east-1")
        conn = boto3.client("sqs", region_name="us-east-1")
        name = str(uuid4())[0:6]
        q_response = conn.create_queue(QueueName=name)
        working_url = q_response["QueueUrl"]
        # https://queue.amazonaws.com/012341234/test-queue
        # http://localhost:5000/012341234/test-queue in ServerMode
        assert re.match(f"/{ACCOUNT_ID}/{name}", working_url)
>       queue = sqs.Queue(name)
E       AssertionError: assert None
E        +  where None = <function match at 0x7f1529db48b0>('/123456789012/c46aba', 'https://sqs.us-east1.amazonaws.com/123456789012/c46aba')
E        +    where <function match at 0x7f1529db48b0> = re.match

tests/test_sqs/test_sqs.py:3051: AssertionError
```

I fixed it by using `endswith` rather than `re.match()`.  The "sure" library must be using `re.search()` vs. `re.match()`.
```
assert working_url.endswith(f"/{ACCOUNT_ID}/{name}")
```

Patch:
diff --git a/moto/core/custom_responses_mock.py b/moto/core/custom_responses_mock.py
index b979b318b..e72cf4810 100644
--- a/moto/core/custom_responses_mock.py
+++ b/moto/core/custom_responses_mock.py
@@ -7,15 +7,7 @@ from urllib.parse import urlparse
 from werkzeug.wrappers import Request
 from .responses import TYPE_RESPONSE
 
-from moto.utilities.distutils_version import LooseVersion
-
-try:
-    from importlib.metadata import version
-except ImportError:
-    from importlib_metadata import version
-
-
-RESPONSES_VERSION = version("responses")
+from moto.core.versions import is_responses_0_17_x
 
 
 class CallbackResponse(responses.CallbackResponse):
@@ -155,7 +147,7 @@ def get_response_mock() -> responses.RequestsMock:
     """
     responses_mock = None
 
-    if LooseVersion(RESPONSES_VERSION) >= LooseVersion("0.17.0"):
+    if is_responses_0_17_x():
         from .responses_custom_registry import CustomRegistry
 
         responses_mock = responses.RequestsMock(
@@ -170,7 +162,7 @@ def get_response_mock() -> responses.RequestsMock:
 
 
 def reset_responses_mock(responses_mock: responses.RequestsMock) -> None:
-    if LooseVersion(RESPONSES_VERSION) >= LooseVersion("0.17.0"):
+    if is_responses_0_17_x():
         from .responses_custom_registry import CustomRegistry
 
         responses_mock.reset()
diff --git a/moto/core/responses.py b/moto/core/responses.py
index 173f93142..bfdbac1e8 100644
--- a/moto/core/responses.py
+++ b/moto/core/responses.py
@@ -128,7 +128,9 @@ class _TemplateEnvironmentMixin(object):
 class ActionAuthenticatorMixin(object):
     request_count: ClassVar[int] = 0
 
-    def _authenticate_and_authorize_action(self, iam_request_cls: type) -> None:
+    def _authenticate_and_authorize_action(
+        self, iam_request_cls: type, resource: str = "*"
+    ) -> None:
         if (
             ActionAuthenticatorMixin.request_count
             >= settings.INITIAL_NO_AUTH_ACTION_COUNT
@@ -145,7 +147,7 @@ class ActionAuthenticatorMixin(object):
                 headers=self.headers,  # type: ignore[attr-defined]
             )
             iam_request.check_signature()
-            iam_request.check_action_permitted()
+            iam_request.check_action_permitted(resource)
         else:
             ActionAuthenticatorMixin.request_count += 1
 
@@ -154,10 +156,15 @@ class ActionAuthenticatorMixin(object):
 
         self._authenticate_and_authorize_action(IAMRequest)
 
-    def _authenticate_and_authorize_s3_action(self) -> None:
+    def _authenticate_and_authorize_s3_action(
+        self, bucket_name: Optional[str] = None, key_name: Optional[str] = None
+    ) -> None:
+        arn = f"{bucket_name or '*'}/{key_name}" if key_name else (bucket_name or "*")
+        resource = f"arn:aws:s3:::{arn}"
+
         from moto.iam.access_control import S3IAMRequest
 
-        self._authenticate_and_authorize_action(S3IAMRequest)
+        self._authenticate_and_authorize_action(S3IAMRequest, resource)
 
     @staticmethod
     def set_initial_no_auth_action_count(initial_no_auth_action_count: int) -> Callable[..., Callable[..., TYPE_RESPONSE]]:  # type: ignore[misc]
@@ -237,6 +244,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
         use_raw_body: Use incoming bytes if True, encode to string otherwise
         """
         self.is_werkzeug_request = "werkzeug" in str(type(request))
+        self.parsed_url = urlparse(full_url)
         querystring: Dict[str, Any] = OrderedDict()
         if hasattr(request, "body"):
             # Boto
@@ -258,9 +266,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
             self.body = self.body.decode("utf-8")
 
         if not querystring:
-            querystring.update(
-                parse_qs(urlparse(full_url).query, keep_blank_values=True)
-            )
+            querystring.update(parse_qs(self.parsed_url.query, keep_blank_values=True))
         if not querystring:
             if (
                 "json" in request.headers.get("content-type", [])
@@ -298,7 +304,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
 
         self.uri = full_url
 
-        self.path = urlparse(full_url).path
+        self.path = self.parsed_url.path
         self.querystring = querystring
         self.data = querystring
         self.method = request.method
@@ -307,7 +313,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
 
         self.headers = request.headers
         if "host" not in self.headers:
-            self.headers["host"] = urlparse(full_url).netloc
+            self.headers["host"] = self.parsed_url.netloc
         self.response_headers = {
             "server": "amazon.com",
             "date": datetime.datetime.now().strftime("%a, %d %b %Y %H:%M:%S GMT"),
diff --git a/moto/core/versions.py b/moto/core/versions.py
index 82d564f39..8aa754f10 100644
--- a/moto/core/versions.py
+++ b/moto/core/versions.py
@@ -6,8 +6,13 @@ except ImportError:
     from importlib_metadata import version
 
 
+RESPONSES_VERSION = version("responses")
 WERKZEUG_VERSION = version("werkzeug")
 
 
+def is_responses_0_17_x() -> bool:
+    return LooseVersion(RESPONSES_VERSION) >= LooseVersion("0.17.0")
+
+
 def is_werkzeug_2_3_x() -> bool:
     return LooseVersion(WERKZEUG_VERSION) >= LooseVersion("2.3.0")
diff --git a/moto/databrew/responses.py b/moto/databrew/responses.py
index aa7de83c1..b06f5a0e8 100644
--- a/moto/databrew/responses.py
+++ b/moto/databrew/responses.py
@@ -1,6 +1,5 @@
 import json
 from typing import Any, Dict, Union
-from urllib.parse import urlparse
 
 from moto.core.common_types import TYPE_RESPONSE
 from moto.core.responses import BaseResponse
@@ -40,8 +39,7 @@ class DataBrewResponse(BaseResponse):
         self.setup_class(request, full_url, headers)
         # https://docs.aws.amazon.com/databrew/latest/dg/API_DeleteRecipeVersion.html
         if request.method == "DELETE":
-            parsed_url = urlparse(full_url)
-            split_path = parsed_url.path.strip("/").split("/")
+            split_path = self.parsed_url.path.strip("/").split("/")
             recipe_name = split_path[1]
             recipe_version = split_path[3]
             self.databrew_backend.delete_recipe_version(recipe_name, recipe_version)
@@ -100,8 +98,7 @@ class DataBrewResponse(BaseResponse):
     def publish_recipe(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
         if request.method == "POST":
-            parsed_url = urlparse(full_url)
-            recipe_name = parsed_url.path.strip("/").split("/", 2)[1]
+            recipe_name = self.parsed_url.path.strip("/").split("/", 2)[1]
             recipe_description = self.parameters.get("Description")
             self.databrew_backend.publish_recipe(recipe_name, recipe_description)
             return 200, {}, json.dumps({"Name": recipe_name})
@@ -128,9 +125,8 @@ class DataBrewResponse(BaseResponse):
     @amzn_request_id
     def recipe_response(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
 
-        recipe_name = parsed_url.path.rstrip("/").rsplit("/", 1)[1]
+        recipe_name = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
 
         if request.method == "PUT":
             return self.put_recipe_response(recipe_name)
@@ -180,9 +176,8 @@ class DataBrewResponse(BaseResponse):
     @amzn_request_id
     def ruleset_response(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
 
-        ruleset_name = parsed_url.path.split("/")[-1]
+        ruleset_name = self.parsed_url.path.split("/")[-1]
 
         if request.method == "PUT":
             response = self.put_ruleset_response(ruleset_name)
@@ -285,9 +280,8 @@ class DataBrewResponse(BaseResponse):
     @amzn_request_id
     def dataset_response(self, request: Any, full_url: str, headers: Any) -> Union[str, TYPE_RESPONSE]:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
 
-        dataset_name = parsed_url.path.split("/")[-1]
+        dataset_name = self.parsed_url.path.split("/")[-1]
 
         if request.method == "POST":
             return self.create_dataset()
@@ -339,9 +333,8 @@ class DataBrewResponse(BaseResponse):
     @amzn_request_id
     def job_response(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
 
-        job_name = parsed_url.path.rstrip("/").rsplit("/", 1)[1]
+        job_name = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
 
         if request.method == "GET":
             return self.get_job_response(job_name)
@@ -430,9 +423,8 @@ class DataBrewResponse(BaseResponse):
     @amzn_request_id
     def profile_job_response(self, request: Any, full_url: str, headers: Any) -> str:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
 
-        job_name = parsed_url.path.rstrip("/").rsplit("/", 1)[1]
+        job_name = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
 
         if request.method == "PUT":
             return self.update_profile_job_response(job_name)
@@ -440,9 +432,8 @@ class DataBrewResponse(BaseResponse):
     @amzn_request_id
     def recipe_job_response(self, request: Any, full_url: str, headers: Any) -> str:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
 
-        job_name = parsed_url.path.rstrip("/").rsplit("/", 1)[1]
+        job_name = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
 
         if request.method == "PUT":
             return self.update_recipe_job_response(job_name)
diff --git a/moto/dynamodb/models/__init__.py b/moto/dynamodb/models/__init__.py
index 019e8eece..9be813030 100644
--- a/moto/dynamodb/models/__init__.py
+++ b/moto/dynamodb/models/__init__.py
@@ -316,7 +316,7 @@ class DynamoDBBackend(BaseBackend):
         limit: int,
         exclusive_start_key: Dict[str, Any],
         scan_index_forward: bool,
-        projection_expression: str,
+        projection_expression: Optional[str],
         index_name: Optional[str] = None,
         expr_names: Optional[Dict[str, str]] = None,
         expr_values: Optional[Dict[str, str]] = None,
@@ -351,11 +351,11 @@ class DynamoDBBackend(BaseBackend):
         filters: Dict[str, Any],
         limit: int,
         exclusive_start_key: Dict[str, Any],
-        filter_expression: str,
+        filter_expression: Optional[str],
         expr_names: Dict[str, Any],
         expr_values: Dict[str, Any],
         index_name: str,
-        projection_expression: str,
+        projection_expression: Optional[str],
     ) -> Tuple[List[Item], int, Optional[Dict[str, Any]]]:
         table = self.get_table(table_name)
 
diff --git a/moto/dynamodb/models/table.py b/moto/dynamodb/models/table.py
index 4d433ea7c..78f18e809 100644
--- a/moto/dynamodb/models/table.py
+++ b/moto/dynamodb/models/table.py
@@ -637,7 +637,7 @@ class Table(CloudFormationModel):
         limit: int,
         exclusive_start_key: Dict[str, Any],
         scan_index_forward: bool,
-        projection_expression: str,
+        projection_expression: Optional[str],
         index_name: Optional[str] = None,
         filter_expression: Any = None,
         **filter_kwargs: Any,
diff --git a/moto/dynamodb/responses.py b/moto/dynamodb/responses.py
index 1adc7430b..8f94fcb6f 100644
--- a/moto/dynamodb/responses.py
+++ b/moto/dynamodb/responses.py
@@ -351,6 +351,22 @@ class DynamoHandler(BaseResponse):
                     + dump_list(actual_attrs)
                 )
 
+    def _get_filter_expression(self) -> Optional[str]:
+        filter_expression = self.body.get("FilterExpression")
+        if filter_expression == "":
+            raise MockValidationException(
+                "Invalid FilterExpression: The expression can not be empty;"
+            )
+        return filter_expression
+
+    def _get_projection_expression(self) -> Optional[str]:
+        expression = self.body.get("ProjectionExpression")
+        if expression == "":
+            raise MockValidationException(
+                "Invalid ProjectionExpression: The expression can not be empty;"
+            )
+        return expression
+
     def delete_table(self) -> str:
         name = self.body["TableName"]
         table = self.dynamodb_backend.delete_table(name)
@@ -521,7 +537,7 @@ class DynamoHandler(BaseResponse):
                 f"empty string value. Key: {empty_keys[0]}"
             )
 
-        projection_expression = self.body.get("ProjectionExpression")
+        projection_expression = self._get_projection_expression()
         attributes_to_get = self.body.get("AttributesToGet")
         if projection_expression and attributes_to_get:
             raise MockValidationException(
@@ -631,9 +647,9 @@ class DynamoHandler(BaseResponse):
     def query(self) -> str:
         name = self.body["TableName"]
         key_condition_expression = self.body.get("KeyConditionExpression")
-        projection_expression = self.body.get("ProjectionExpression")
+        projection_expression = self._get_projection_expression()
         expression_attribute_names = self.body.get("ExpressionAttributeNames", {})
-        filter_expression = self.body.get("FilterExpression")
+        filter_expression = self._get_filter_expression()
         expression_attribute_values = self.body.get("ExpressionAttributeValues", {})
 
         projection_expression = self._adjust_projection_expression(
@@ -726,8 +742,8 @@ class DynamoHandler(BaseResponse):
         return dynamo_json_dump(result)
 
     def _adjust_projection_expression(
-        self, projection_expression: str, expr_attr_names: Dict[str, str]
-    ) -> str:
+        self, projection_expression: Optional[str], expr_attr_names: Dict[str, str]
+    ) -> Optional[str]:
         def _adjust(expression: str) -> str:
             return (
                 expr_attr_names[expression]
@@ -762,10 +778,10 @@ class DynamoHandler(BaseResponse):
             comparison_values = scan_filter.get("AttributeValueList", [])
             filters[attribute_name] = (comparison_operator, comparison_values)
 
-        filter_expression = self.body.get("FilterExpression")
+        filter_expression = self._get_filter_expression()
         expression_attribute_values = self.body.get("ExpressionAttributeValues", {})
         expression_attribute_names = self.body.get("ExpressionAttributeNames", {})
-        projection_expression = self.body.get("ProjectionExpression", "")
+        projection_expression = self._get_projection_expression()
         exclusive_start_key = self.body.get("ExclusiveStartKey")
         limit = self.body.get("Limit")
         index_name = self.body.get("IndexName")
diff --git a/moto/emr/responses.py b/moto/emr/responses.py
index 8d2555e7d..77819e31d 100644
--- a/moto/emr/responses.py
+++ b/moto/emr/responses.py
@@ -4,7 +4,6 @@ from datetime import datetime, timezone
 from functools import wraps
 from typing import Any, Callable, Dict, List, Pattern
 
-from urllib.parse import urlparse
 from moto.core.responses import AWSServiceSpec
 from moto.core.responses import BaseResponse
 from moto.core.responses import xml_to_json_response
@@ -65,9 +64,8 @@ class ElasticMapReduceResponse(BaseResponse):
         super().__init__(service_name="emr")
 
     def get_region_from_url(self, request: Any, full_url: str) -> str:
-        parsed = urlparse(full_url)
         for regex in ElasticMapReduceResponse.emr_region_regex:
-            match = regex.search(parsed.netloc)
+            match = regex.search(self.parsed_url.netloc)
             if match:
                 return match.group(1)
         return self.default_region
diff --git a/moto/iam/access_control.py b/moto/iam/access_control.py
index a1e384add..905dcf971 100644
--- a/moto/iam/access_control.py
+++ b/moto/iam/access_control.py
@@ -216,7 +216,7 @@ class IAMRequestBase(object, metaclass=ABCMeta):
         if original_signature != calculated_signature:
             self._raise_signature_does_not_match()
 
-    def check_action_permitted(self) -> None:
+    def check_action_permitted(self, resource: str) -> None:
         if (
             self._action == "sts:GetCallerIdentity"
         ):  # always allowed, even if there's an explicit Deny for it
@@ -226,7 +226,7 @@ class IAMRequestBase(object, metaclass=ABCMeta):
         permitted = False
         for policy in policies:
             iam_policy = IAMPolicy(policy)
-            permission_result = iam_policy.is_action_permitted(self._action)
+            permission_result = iam_policy.is_action_permitted(self._action, resource)
             if permission_result == PermissionResult.DENIED:
                 self._raise_access_denied()
             elif permission_result == PermissionResult.PERMITTED:
diff --git a/moto/managedblockchain/responses.py b/moto/managedblockchain/responses.py
index 46e6e6ecd..faec856a6 100644
--- a/moto/managedblockchain/responses.py
+++ b/moto/managedblockchain/responses.py
@@ -1,6 +1,6 @@
 import json
 from typing import Any, Dict, Optional
-from urllib.parse import urlparse, parse_qs
+from urllib.parse import parse_qs
 
 from moto.core.common_types import TYPE_RESPONSE
 from moto.core.responses import BaseResponse
@@ -291,8 +291,7 @@ class ManagedBlockchainResponse(BaseResponse):
 
     def _node_response(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         method = request.method
-        parsed_url = urlparse(full_url)
-        querystring = parse_qs(parsed_url.query, keep_blank_values=True)
+        querystring = parse_qs(self.parsed_url.query, keep_blank_values=True)
         network_id = networkid_from_managedblockchain_url(full_url)
         member_id = memberid_from_managedblockchain_request(full_url, self.body)
         if method == "GET":
diff --git a/moto/route53/responses.py b/moto/route53/responses.py
index 4c43eddf3..7356cd6fc 100644
--- a/moto/route53/responses.py
+++ b/moto/route53/responses.py
@@ -1,7 +1,7 @@
 """Handles Route53 API requests, invokes method and returns response."""
 import datetime
 import re
-from urllib.parse import parse_qs, urlparse
+from urllib.parse import parse_qs
 
 from jinja2 import Template
 from typing import Any
@@ -95,8 +95,7 @@ class Route53(BaseResponse):
         self, request: Any, full_url: str, headers: Any
     ) -> TYPE_RESPONSE:
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
-        query_params = parse_qs(parsed_url.query)
+        query_params = parse_qs(self.parsed_url.query)
         dnsnames = query_params.get("dnsname")
 
         dnsname, zones = self.backend.list_hosted_zones_by_name(dnsnames)
@@ -108,8 +107,7 @@ class Route53(BaseResponse):
         self, request: Any, full_url: str, headers: Any
     ) -> TYPE_RESPONSE:
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
-        query_params = parse_qs(parsed_url.query)
+        query_params = parse_qs(self.parsed_url.query)
         vpc_id = query_params.get("vpcid")[0]  # type: ignore
         zones = self.backend.list_hosted_zones_by_vpc(vpc_id)
         template = Template(LIST_HOSTED_ZONES_BY_VPC_RESPONSE)
@@ -125,8 +123,7 @@ class Route53(BaseResponse):
 
     def get_or_delete_hostzone_response(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
-        zoneid = parsed_url.path.rstrip("/").rsplit("/", 1)[1]
+        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
 
         if request.method == "GET":
             the_zone = self.backend.get_hosted_zone(zoneid)
@@ -149,10 +146,9 @@ class Route53(BaseResponse):
         # TODO: implement enable/disable dnssec apis
         self.setup_class(request, full_url, headers)
 
-        parsed_url = urlparse(full_url)
         method = request.method
 
-        zoneid = parsed_url.path.rstrip("/").rsplit("/", 2)[1]
+        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]
 
         if method == "GET":
             self.backend.get_dnssec(zoneid)
@@ -163,8 +159,7 @@ class Route53(BaseResponse):
     ) -> TYPE_RESPONSE:
         self.setup_class(request, full_url, headers)
 
-        parsed_url = urlparse(full_url)
-        zoneid = parsed_url.path.rstrip("/").rsplit("/", 2)[1]
+        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]
 
         elements = xmltodict.parse(self.body)
         comment = vpc = elements.get("AssociateVPCWithHostedZoneRequest", {}).get(
@@ -184,8 +179,7 @@ class Route53(BaseResponse):
     ) -> TYPE_RESPONSE:
         self.setup_class(request, full_url, headers)
 
-        parsed_url = urlparse(full_url)
-        zoneid = parsed_url.path.rstrip("/").rsplit("/", 2)[1]
+        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]
 
         elements = xmltodict.parse(self.body)
         comment = vpc = elements.get("DisassociateVPCFromHostedZoneRequest", {}).get(
@@ -202,10 +196,9 @@ class Route53(BaseResponse):
     def rrset_response(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
 
-        parsed_url = urlparse(full_url)
         method = request.method
 
-        zoneid = parsed_url.path.rstrip("/").rsplit("/", 2)[1]
+        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]
 
         if method == "POST":
             elements = xmltodict.parse(self.body)
@@ -242,7 +235,7 @@ class Route53(BaseResponse):
             return 200, headers, CHANGE_RRSET_RESPONSE
 
         elif method == "GET":
-            querystring = parse_qs(parsed_url.query)
+            querystring = parse_qs(self.parsed_url.query)
             template = Template(LIST_RRSET_RESPONSE)
             start_type = querystring.get("type", [None])[0]  # type: ignore
             start_name = querystring.get("name", [None])[0]  # type: ignore
@@ -314,9 +307,8 @@ class Route53(BaseResponse):
     def health_check_response2(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
 
-        parsed_url = urlparse(full_url)
         method = request.method
-        health_check_id = parsed_url.path.split("/")[-1]
+        health_check_id = self.parsed_url.path.split("/")[-1]
 
         if method == "GET":
             health_check = self.backend.get_health_check(health_check_id)
@@ -351,14 +343,12 @@ class Route53(BaseResponse):
     def health_check_status_response(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
 
-        parsed_url = urlparse(full_url)
         method = request.method
 
-        health_check_id = re.search(
-            r"healthcheck/(?P<health_check_id>[^/]+)/status$", parsed_url.path
-        ).group(  # type: ignore[union-attr]
-            "health_check_id"
+        health_check_match = re.search(
+            r"healthcheck/(?P<health_check_id>[^/]+)/status$", self.parsed_url.path
         )
+        health_check_id = health_check_match.group("health_check_id")  # type: ignore[union-attr]
 
         if method == "GET":
             self.backend.get_health_check(health_check_id)
@@ -390,9 +380,8 @@ class Route53(BaseResponse):
     def list_or_change_tags_for_resource_request(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
 
-        parsed_url = urlparse(full_url)
-        id_ = parsed_url.path.split("/")[-1]
-        type_ = parsed_url.path.split("/")[-2]
+        id_ = self.parsed_url.path.split("/")[-1]
+        type_ = self.parsed_url.path.split("/")[-2]
 
         if request.method == "GET":
             tags = self.backend.list_tags_for_resource(id_)
@@ -419,8 +408,7 @@ class Route53(BaseResponse):
         self.setup_class(request, full_url, headers)
 
         if request.method == "GET":
-            parsed_url = urlparse(full_url)
-            change_id = parsed_url.path.rstrip("/").rsplit("/", 1)[1]
+            change_id = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
             template = Template(GET_CHANGE_RESPONSE)
             return 200, headers, template.render(change_id=change_id, xmlns=XMLNS)
 
@@ -470,8 +458,7 @@ class Route53(BaseResponse):
 
     def get_or_delete_query_logging_config_response(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
-        query_logging_config_id = parsed_url.path.rstrip("/").rsplit("/", 1)[1]
+        query_logging_config_id = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
 
         if request.method == "GET":
             query_logging_config = self.backend.get_query_logging_config(
@@ -520,8 +507,7 @@ class Route53(BaseResponse):
 
     def reusable_delegation_set(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE:  # type: ignore[return]
         self.setup_class(request, full_url, headers)
-        parsed_url = urlparse(full_url)
-        ds_id = parsed_url.path.rstrip("/").rsplit("/")[-1]
+        ds_id = self.parsed_url.path.rstrip("/").rsplit("/")[-1]
         if request.method == "GET":
             delegation_set = self.backend.get_reusable_delegation_set(
                 delegation_set_id=ds_id
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index fdd3c3a2d..cef5bd7a5 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -292,7 +292,7 @@ class S3Response(BaseResponse):
     def _bucket_response(
         self, request: Any, full_url: str
     ) -> Union[str, TYPE_RESPONSE]:
-        querystring = self._get_querystring(request, full_url)
+        querystring = self._get_querystring(request)
         method = request.method
         region_name = parse_region_from_url(full_url, use_default_region=False)
         if region_name is None:
@@ -327,7 +327,7 @@ class S3Response(BaseResponse):
                 f"Method {method} has not been implemented in the S3 backend yet"
             )
 
-    def _get_querystring(self, request: Any, full_url: str) -> Dict[str, Any]:  # type: ignore[misc]
+    def _get_querystring(self, request: Any) -> Dict[str, Any]:  # type: ignore[misc]
         # Flask's Request has the querystring already parsed
         # In ServerMode, we can use this, instead of manually parsing this
         if hasattr(request, "args"):
@@ -338,7 +338,6 @@ class S3Response(BaseResponse):
                 query_dict[key] = val if isinstance(val, list) else [val]
             return query_dict
 
-        parsed_url = urlparse(full_url)
         # full_url can be one of two formats, depending on the version of werkzeug used:
         # http://foobaz.localhost:5000/?prefix=bar%2Bbaz
         # http://foobaz.localhost:5000/?prefix=bar+baz
@@ -347,14 +346,14 @@ class S3Response(BaseResponse):
         #
         # Workaround - manually reverse the encoding.
         # Keep the + encoded, ensuring that parse_qsl doesn't replace it, and parse_qsl will unquote it afterwards
-        qs = (parsed_url.query or "").replace("+", "%2B")
+        qs = (self.parsed_url.query or "").replace("+", "%2B")
         return parse_qs(qs, keep_blank_values=True)
 
     def _bucket_response_head(
         self, bucket_name: str, querystring: Dict[str, Any]
     ) -> TYPE_RESPONSE:
         self._set_action("BUCKET", "HEAD", querystring)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(bucket_name=bucket_name)
 
         try:
             bucket = self.backend.head_bucket(bucket_name)
@@ -416,7 +415,7 @@ class S3Response(BaseResponse):
         self, headers: Dict[str, str], bucket_name: str
     ) -> TYPE_RESPONSE:
         # Return 200 with the headers from the bucket CORS configuration
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(bucket_name=bucket_name)
         try:
             bucket = self.backend.head_bucket(bucket_name)
         except MissingBucket:
@@ -478,7 +477,7 @@ class S3Response(BaseResponse):
         self, bucket_name: str, querystring: Dict[str, Any]
     ) -> Union[str, TYPE_RESPONSE]:
         self._set_action("BUCKET", "GET", querystring)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(bucket_name=bucket_name)
 
         if "object-lock" in querystring:
             (
@@ -823,7 +822,7 @@ class S3Response(BaseResponse):
             return 411, {}, "Content-Length required"
 
         self._set_action("BUCKET", "PUT", querystring)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(bucket_name=bucket_name)
 
         if "object-lock" in querystring:
             config = self._lock_config_from_body()
@@ -1009,7 +1008,7 @@ class S3Response(BaseResponse):
         self, bucket_name: str, querystring: Dict[str, Any]
     ) -> TYPE_RESPONSE:
         self._set_action("BUCKET", "DELETE", querystring)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(bucket_name=bucket_name)
 
         if "policy" in querystring:
             self.backend.delete_bucket_policy(bucket_name)
@@ -1060,7 +1059,7 @@ class S3Response(BaseResponse):
         if self.is_delete_keys():
             self.data["Action"] = "DeleteObject"
             try:
-                self._authenticate_and_authorize_s3_action()
+                self._authenticate_and_authorize_s3_action(bucket_name=bucket_name)
                 return self._bucket_response_delete_keys(bucket_name)
             except BucketAccessDeniedError:
                 return self._bucket_response_delete_keys(
@@ -1068,7 +1067,7 @@ class S3Response(BaseResponse):
                 )
 
         self.data["Action"] = "PutObject"
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(bucket_name=bucket_name)
 
         # POST to bucket-url should create file from form
         form = request.form
@@ -1249,9 +1248,8 @@ class S3Response(BaseResponse):
     def _key_response(
         self, request: Any, full_url: str, headers: Dict[str, Any]
     ) -> TYPE_RESPONSE:
-        parsed_url = urlparse(full_url)
-        url_path = self.get_safe_path_from_url(parsed_url)
-        query = parse_qs(parsed_url.query, keep_blank_values=True)
+        url_path = self.get_safe_path_from_url(self.parsed_url)
+        query = parse_qs(self.parsed_url.query, keep_blank_values=True)
         method = request.method
 
         key_name = self.parse_key_name(request, url_path)
@@ -1361,7 +1359,9 @@ class S3Response(BaseResponse):
         headers: Dict[str, Any],
     ) -> TYPE_RESPONSE:
         self._set_action("KEY", "GET", query)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(
+            bucket_name=bucket_name, key_name=key_name
+        )
 
         response_headers = self._get_cors_headers_other(headers, bucket_name)
         if query.get("uploadId"):
@@ -1478,7 +1478,9 @@ class S3Response(BaseResponse):
         key_name: str,
     ) -> TYPE_RESPONSE:
         self._set_action("KEY", "PUT", query)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(
+            bucket_name=bucket_name, key_name=key_name
+        )
 
         response_headers = self._get_cors_headers_other(request.headers, bucket_name)
         if query.get("uploadId") and query.get("partNumber"):
@@ -1750,7 +1752,9 @@ class S3Response(BaseResponse):
         headers: Dict[str, Any],
     ) -> TYPE_RESPONSE:
         self._set_action("KEY", "HEAD", query)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(
+            bucket_name=bucket_name, key_name=key_name
+        )
 
         response_headers: Dict[str, Any] = {}
         version_id = query.get("versionId", [None])[0]
@@ -2149,7 +2153,9 @@ class S3Response(BaseResponse):
         self, headers: Any, bucket_name: str, query: Dict[str, Any], key_name: str
     ) -> TYPE_RESPONSE:
         self._set_action("KEY", "DELETE", query)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(
+            bucket_name=bucket_name, key_name=key_name
+        )
 
         if query.get("uploadId"):
             upload_id = query["uploadId"][0]
@@ -2190,7 +2196,9 @@ class S3Response(BaseResponse):
         key_name: str,
     ) -> TYPE_RESPONSE:
         self._set_action("KEY", "POST", query)
-        self._authenticate_and_authorize_s3_action()
+        self._authenticate_and_authorize_s3_action(
+            bucket_name=bucket_name, key_name=key_name
+        )
 
         encryption = request.headers.get("x-amz-server-side-encryption")
         kms_key_id = request.headers.get("x-amz-server-side-encryption-aws-kms-key-id")


Test patch:
diff --git a/tests/test_core/test_auth.py b/tests/test_core/test_auth.py
index 5e9e10417..8c5f8fa04 100644
--- a/tests/test_core/test_auth.py
+++ b/tests/test_core/test_auth.py
@@ -766,3 +766,68 @@ def test_s3_invalid_token_with_temporary_credentials():
     assert err["Code"] == "InvalidToken"
     assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
     assert err["Message"] == "The provided token is malformed or otherwise invalid."
+
+
+@set_initial_no_auth_action_count(3)
+@mock_s3
+@mock_iam
+def test_allow_bucket_access_using_resource_arn() -> None:
+    user_name = "test-user"
+    policy_doc = {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Action": ["s3:*"],
+                "Effect": "Allow",
+                "Resource": "arn:aws:s3:::my_bucket",
+                "Sid": "BucketLevelGrants",
+            },
+        ],
+    }
+    access_key = create_user_with_access_key_and_inline_policy(user_name, policy_doc)
+
+    s3_client = boto3.client(
+        "s3",
+        region_name="us-east-1",
+        aws_access_key_id=access_key["AccessKeyId"],
+        aws_secret_access_key=access_key["SecretAccessKey"],
+    )
+
+    s3_client.create_bucket(Bucket="my_bucket")
+    with pytest.raises(ClientError):
+        s3_client.create_bucket(Bucket="my_bucket2")
+
+    s3_client.head_bucket(Bucket="my_bucket")
+    with pytest.raises(ClientError):
+        s3_client.head_bucket(Bucket="my_bucket2")
+
+
+@set_initial_no_auth_action_count(3)
+@mock_s3
+@mock_iam
+def test_allow_key_access_using_resource_arn() -> None:
+    user_name = "test-user"
+    policy_doc = {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Action": ["s3:*"],
+                "Effect": "Allow",
+                "Resource": ["arn:aws:s3:::my_bucket", "arn:aws:s3:::*/keyname"],
+                "Sid": "KeyLevelGrants",
+            },
+        ],
+    }
+    access_key = create_user_with_access_key_and_inline_policy(user_name, policy_doc)
+
+    s3_client = boto3.client(
+        "s3",
+        region_name="us-east-1",
+        aws_access_key_id=access_key["AccessKeyId"],
+        aws_secret_access_key=access_key["SecretAccessKey"],
+    )
+
+    s3_client.create_bucket(Bucket="my_bucket")
+    s3_client.put_object(Bucket="my_bucket", Key="keyname", Body=b"test")
+    with pytest.raises(ClientError):
+        s3_client.put_object(Bucket="my_bucket", Key="unknown", Body=b"test")
diff --git a/tests/test_dynamodb/exceptions/test_dynamodb_exceptions.py b/tests/test_dynamodb/exceptions/test_dynamodb_exceptions.py
index ad08f8322..97e7d9e2f 100644
--- a/tests/test_dynamodb/exceptions/test_dynamodb_exceptions.py
+++ b/tests/test_dynamodb/exceptions/test_dynamodb_exceptions.py
@@ -144,7 +144,7 @@ def test_empty_expressionattributenames_with_empty_projection():
     table = ddb.Table("test-table")
     with pytest.raises(ClientError) as exc:
         table.get_item(
-            Key={"id": "my_id"}, ProjectionExpression="", ExpressionAttributeNames={}
+            Key={"id": "my_id"}, ProjectionExpression="a", ExpressionAttributeNames={}
         )
     err = exc.value.response["Error"]
     assert err["Code"] == "ValidationException"
@@ -1067,3 +1067,30 @@ def test_list_append_errors_for_unknown_attribute_value():
         ExpressionAttributeValues={":i": {"L": [{"S": "bar2"}]}},
         ReturnValues="UPDATED_NEW",
     )
+
+
+@mock_dynamodb
+def test_query_with_empty_filter_expression():
+    ddb = boto3.resource("dynamodb", region_name="us-east-1")
+    ddb.create_table(
+        TableName="test-table", BillingMode="PAY_PER_REQUEST", **table_schema
+    )
+    table = ddb.Table("test-table")
+    with pytest.raises(ClientError) as exc:
+        table.query(
+            KeyConditionExpression="partitionKey = sth", ProjectionExpression=""
+        )
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ValidationException"
+    assert (
+        err["Message"]
+        == "Invalid ProjectionExpression: The expression can not be empty;"
+    )
+
+    with pytest.raises(ClientError) as exc:
+        table.query(KeyConditionExpression="partitionKey = sth", FilterExpression="")
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ValidationException"
+    assert (
+        err["Message"] == "Invalid FilterExpression: The expression can not be empty;"
+    )
diff --git a/tests/test_sqs/test_server.py b/tests/test_sqs/test_server.py
index 9a737c093..ec9fe1487 100644
--- a/tests/test_sqs/test_server.py
+++ b/tests/test_sqs/test_server.py
@@ -1,22 +1,18 @@
+"""Test different server responses."""
 import datetime
 import re
-import sure  # noqa # pylint: disable=unused-import
 import threading
 import time
 
 import moto.server as server
 
-"""
-Test the different server responses
-"""
-
 
 def test_sqs_list_identities():
     backend = server.create_backend_app("sqs")
     test_client = backend.test_client()
 
     res = test_client.get("/?Action=ListQueues")
-    res.data.should.contain(b"ListQueuesResponse")
+    assert b"ListQueuesResponse" in res.data
 
     # Make sure that we can receive messages from queues whose name contains dots (".")
     # The AWS API mandates that the names of FIFO queues use the suffix ".fifo"
@@ -35,11 +31,11 @@ def test_sqs_list_identities():
         )
 
         message = re.search("<Body>(.*?)</Body>", res.data.decode("utf-8")).groups()[0]
-        message.should.equal("test-message")
+        assert message == "test-message"
 
     res = test_client.get("/?Action=ListQueues&QueueNamePrefix=other")
-    res.data.should.contain(b"otherqueue.fifo")
-    res.data.should_not.contain(b"testqueue")
+    assert b"otherqueue.fifo" in res.data
+    assert b"testqueue" not in res.data
 
 
 def test_messages_polling():
@@ -91,7 +87,8 @@ def test_no_messages_polling_timeout():
     wait_seconds = 5
     start = datetime.datetime.utcnow()
     test_client.get(
-        f"/123/{queue_name}?Action=ReceiveMessage&MaxNumberOfMessages=1&WaitTimeSeconds={wait_seconds}"
+        f"/123/{queue_name}?Action=ReceiveMessage&"
+        f"MaxNumberOfMessages=1&WaitTimeSeconds={wait_seconds}"
     )
     end = datetime.datetime.utcnow()
     duration = end - start
diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py
index 60f8fb480..eec03ecbe 100644
--- a/tests/test_sqs/test_sqs.py
+++ b/tests/test_sqs/test_sqs.py
@@ -1,21 +1,20 @@
+import hashlib
 import json
 import os
-import time
-import uuid
-import hashlib
+import re
 import sys
+import time
+from unittest import SkipTest, mock
+from uuid import uuid4
 
 import boto3
 import botocore.exceptions
-import sure  # noqa # pylint: disable=unused-import
 from botocore.exceptions import ClientError
 from freezegun import freeze_time
+import pytest
+
 from moto import mock_sqs, settings
 from moto.utilities.distutils_version import LooseVersion
-
-from unittest import SkipTest, mock
-
-import pytest
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
 from moto.sqs.models import (
     Queue,
@@ -23,7 +22,6 @@ from moto.sqs.models import (
     MAXIMUM_MESSAGE_SIZE_ATTR_UPPER_BOUND,
     MAXIMUM_MESSAGE_LENGTH,
 )
-from uuid import uuid4
 
 TEST_POLICY = """
 {
@@ -54,7 +52,7 @@ def test_create_fifo_queue_fail():
     try:
         sqs.create_queue(QueueName=str(uuid4())[0:6], Attributes={"FifoQueue": "true"})
     except botocore.exceptions.ClientError as err:
-        err.response["Error"]["Code"].should.equal("InvalidParameterValue")
+        assert err.response["Error"]["Code"] == "InvalidParameterValue"
     else:
         raise RuntimeError("Should have raised InvalidParameterValue Exception")
 
@@ -94,7 +92,7 @@ def test_create_queue_with_different_attributes_fail():
     try:
         sqs.create_queue(QueueName=q_name, Attributes={"VisibilityTimeout": "60"})
     except botocore.exceptions.ClientError as err:
-        err.response["Error"]["Code"].should.equal("QueueAlreadyExists")
+        assert err.response["Error"]["Code"] == "QueueAlreadyExists"
     else:
         raise RuntimeError("Should have raised QueueAlreadyExists Exception")
 
@@ -105,7 +103,7 @@ def test_create_queue_with_different_attributes_fail():
     sqs.set_queue_attributes(QueueUrl=response.get("QueueUrl"), Attributes=attributes)
 
     new_response = sqs.create_queue(QueueName=q_name2, Attributes={"FifoQueue": "tru"})
-    new_response["QueueUrl"].should.equal(response.get("QueueUrl"))
+    assert new_response["QueueUrl"] == response.get("QueueUrl")
 
 
 @mock_sqs
@@ -121,28 +119,28 @@ def test_create_fifo_queue():
     )["QueueUrl"]
 
     # then
-    queue_url.should.contain(queue_name)
+    assert queue_name in queue_url
 
     attributes = sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=["All"])[
         "Attributes"
     ]
-    attributes["ApproximateNumberOfMessages"].should.equal("0")
-    attributes["ApproximateNumberOfMessagesNotVisible"].should.equal("0")
-    attributes["ApproximateNumberOfMessagesDelayed"].should.equal("0")
-    attributes["CreatedTimestamp"].should.be.a(str)
-    attributes["ContentBasedDeduplication"].should.equal("false")
-    attributes["DeduplicationScope"].should.equal("queue")
-    attributes["DelaySeconds"].should.equal("0")
-    attributes["LastModifiedTimestamp"].should.be.a(str)
-    attributes["FifoQueue"].should.equal("true")
-    attributes["FifoThroughputLimit"].should.equal("perQueue")
-    attributes["MaximumMessageSize"].should.equal("262144")
-    attributes["MessageRetentionPeriod"].should.equal("345600")
-    attributes["QueueArn"].should.equal(
+    assert attributes["ApproximateNumberOfMessages"] == "0"
+    assert attributes["ApproximateNumberOfMessagesNotVisible"] == "0"
+    assert attributes["ApproximateNumberOfMessagesDelayed"] == "0"
+    assert isinstance(attributes["CreatedTimestamp"], str)
+    assert attributes["ContentBasedDeduplication"] == "false"
+    assert attributes["DeduplicationScope"] == "queue"
+    assert attributes["DelaySeconds"] == "0"
+    assert isinstance(attributes["LastModifiedTimestamp"], str)
+    assert attributes["FifoQueue"] == "true"
+    assert attributes["FifoThroughputLimit"] == "perQueue"
+    assert attributes["MaximumMessageSize"] == "262144"
+    assert attributes["MessageRetentionPeriod"] == "345600"
+    assert attributes["QueueArn"] == (
         f"arn:aws:sqs:{region_name}:{ACCOUNT_ID}:{queue_name}"
     )
-    attributes["ReceiveMessageWaitTimeSeconds"].should.equal("0")
-    attributes["VisibilityTimeout"].should.equal("30")
+    assert attributes["ReceiveMessageWaitTimeSeconds"] == "0"
+    assert attributes["VisibilityTimeout"] == "30"
 
 
 @mock_sqs
@@ -162,14 +160,14 @@ def test_create_fifo_queue_with_high_throughput():
     )["QueueUrl"]
 
     # then
-    queue_url.should.contain(queue_name)
+    assert queue_name in queue_url
 
     attributes = sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=["All"])[
         "Attributes"
     ]
-    attributes["DeduplicationScope"].should.equal("messageGroup")
-    attributes["FifoQueue"].should.equal("true")
-    attributes["FifoThroughputLimit"].should.equal("perMessageGroupId")
+    assert attributes["DeduplicationScope"] == "messageGroup"
+    assert attributes["FifoQueue"] == "true"
+    assert attributes["FifoThroughputLimit"] == "perMessageGroupId"
 
 
 @mock_sqs
@@ -178,12 +176,12 @@ def test_create_queue():
 
     q_name = str(uuid4())[0:6]
     new_queue = sqs.create_queue(QueueName=q_name)
-    new_queue.should.have.property("url").should.contain(q_name)
+    assert q_name in getattr(new_queue, "url")
 
     queue = sqs.get_queue_by_name(QueueName=q_name)
-    queue.attributes.get("QueueArn").split(":")[-1].should.equal(q_name)
-    queue.attributes.get("QueueArn").split(":")[3].should.equal("us-east-1")
-    queue.attributes.get("VisibilityTimeout").should.equal("30")
+    assert queue.attributes.get("QueueArn").split(":")[-1] == q_name
+    assert queue.attributes.get("QueueArn").split(":")[3] == "us-east-1"
+    assert queue.attributes.get("VisibilityTimeout") == "30"
 
 
 @mock_sqs
@@ -201,8 +199,8 @@ def test_create_queue_kms():
 
     queue = sqs.get_queue_by_name(QueueName=q_name)
 
-    queue.attributes.get("KmsMasterKeyId").should.equal("master-key-id")
-    queue.attributes.get("KmsDataKeyReusePeriodSeconds").should.equal("600")
+    assert queue.attributes.get("KmsMasterKeyId") == "master-key-id"
+    assert queue.attributes.get("KmsDataKeyReusePeriodSeconds") == "600"
 
 
 @mock_sqs
@@ -214,9 +212,10 @@ def test_create_queue_with_tags():
     )
     queue_url = response["QueueUrl"]
 
-    client.list_queue_tags(QueueUrl=queue_url)["Tags"].should.equal(
-        {"tag_key_1": "tag_value_1", "tag_key_2": ""}
-    )
+    assert client.list_queue_tags(QueueUrl=queue_url)["Tags"] == {
+        "tag_key_1": "tag_value_1",
+        "tag_key_2": "",
+    }
 
 
 @mock_sqs
@@ -240,13 +239,11 @@ def test_create_queue_with_policy():
     response = client.get_queue_attributes(
         QueueUrl=queue_url, AttributeNames=["Policy"]
     )
-    json.loads(response["Attributes"]["Policy"]).should.equal(
-        {
-            "Version": "2012-10-17",
-            "Id": "test",
-            "Statement": [{"Effect": "Allow", "Principal": "*", "Action": "*"}],
-        }
-    )
+    assert json.loads(response["Attributes"]["Policy"]) == {
+        "Version": "2012-10-17",
+        "Id": "test",
+        "Statement": [{"Effect": "Allow", "Principal": "*", "Action": "*"}],
+    }
 
 
 @mock_sqs
@@ -272,7 +269,7 @@ def test_set_queue_attribute_empty_policy_removes_attr():
     response = client.get_queue_attributes(QueueUrl=queue_url, AttributeNames=["All"])[
         "Attributes"
     ]
-    response.shouldnt.have.key("Policy")
+    assert "Policy" not in response
 
 
 def test_is_empty_redrive_policy_returns_true_for_empty_and_falsy_values():
@@ -312,7 +309,7 @@ def test_set_queue_attribute_empty_redrive_removes_attr():
     response = client.get_queue_attributes(QueueUrl=queue_url, AttributeNames=["All"])[
         "Attributes"
     ]
-    response.shouldnt.have.key("RedrivePolicy")
+    assert "RedrivePolicy" not in response
 
 
 @mock_sqs
@@ -323,7 +320,7 @@ def test_get_queue_url():
 
     response = client.get_queue_url(QueueName=q_name)
 
-    response.should.have.key("QueueUrl").which.should.contain(q_name)
+    assert q_name in response["QueueUrl"]
 
 
 @mock_sqs
@@ -337,12 +334,10 @@ def test_get_queue_url_error_not_exists():
 
     # then
     ex = e.value
-    ex.operation_name.should.equal("GetQueueUrl")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.response["Error"]["Code"].should.contain(
-        "AWS.SimpleQueueService.NonExistentQueue"
-    )
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "GetQueueUrl"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert "AWS.SimpleQueueService.NonExistentQueue" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
         "The specified queue does not exist for this wsdl version."
     )
 
@@ -354,9 +349,9 @@ def test_get_nonexistent_queue():
     with pytest.raises(ClientError) as err:
         sqs.Queue("http://whatever-incorrect-queue-address").load()
     ex = err.value
-    ex.operation_name.should.equal("GetQueueAttributes")
-    ex.response["Error"]["Code"].should.equal("AWS.SimpleQueueService.NonExistentQueue")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "GetQueueAttributes"
+    assert ex.response["Error"]["Code"] == "AWS.SimpleQueueService.NonExistentQueue"
+    assert ex.response["Error"]["Message"] == (
         "The specified queue does not exist for this wsdl version."
     )
 
@@ -366,12 +361,12 @@ def test_message_send_without_attributes():
     sqs = boto3.resource("sqs", region_name="us-east-1")
     queue = sqs.create_queue(QueueName=str(uuid4())[0:6])
     msg = queue.send_message(MessageBody="derp")
-    msg.get("MD5OfMessageBody").should.equal("58fd9edd83341c29f1aebba81c31e257")
-    msg.shouldnt.have.key("MD5OfMessageAttributes")
-    msg.get("MessageId").should_not.contain(" \n")
+    assert msg.get("MD5OfMessageBody") == "58fd9edd83341c29f1aebba81c31e257"
+    assert "MD5OfMessageAttributes" not in msg
+    assert " \n" not in msg.get("MessageId")
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
 
 @mock_sqs
@@ -387,12 +382,12 @@ def test_message_send_with_attributes():
             }
         },
     )
-    msg.get("MD5OfMessageBody").should.equal("58fd9edd83341c29f1aebba81c31e257")
-    msg.get("MD5OfMessageAttributes").should.equal("36655e7e9d7c0e8479fa3f3f42247ae7")
-    msg.get("MessageId").should_not.contain(" \n")
+    assert msg.get("MD5OfMessageBody") == "58fd9edd83341c29f1aebba81c31e257"
+    assert msg.get("MD5OfMessageAttributes") == "36655e7e9d7c0e8479fa3f3f42247ae7"
+    assert " \n" not in msg.get("MessageId")
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
 
 @mock_sqs
@@ -464,10 +459,11 @@ def test_message_with_invalid_attributes():
             },
         )
     ex = e.value
-    ex.response["Error"]["Code"].should.equal("MessageAttributesInvalid")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.response["Error"]["Code"] == "MessageAttributesInvalid"
+    assert ex.response["Error"]["Message"] == (
         "The message attribute name 'öther_encodings' is invalid. "
-        "Attribute name can contain A-Z, a-z, 0-9, underscore (_), hyphen (-), and period (.) characters."
+        "Attribute name can contain A-Z, a-z, 0-9, underscore (_), "
+        "hyphen (-), and period (.) characters."
     )
 
 
@@ -489,12 +485,12 @@ def test_message_with_string_attributes():
             },
         },
     )
-    msg.get("MD5OfMessageBody").should.equal("58fd9edd83341c29f1aebba81c31e257")
-    msg.get("MD5OfMessageAttributes").should.equal("b12289320bb6e494b18b645ef562b4a9")
-    msg.get("MessageId").should_not.contain(" \n")
+    assert msg.get("MD5OfMessageBody") == "58fd9edd83341c29f1aebba81c31e257"
+    assert msg.get("MD5OfMessageAttributes") == "b12289320bb6e494b18b645ef562b4a9"
+    assert " \n" not in msg.get("MessageId")
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
 
 @mock_sqs
@@ -516,12 +512,12 @@ def test_message_with_binary_attribute():
             "contentType": {"StringValue": "application/json", "DataType": "String"},
         },
     )
-    msg.get("MD5OfMessageBody").should.equal("58fd9edd83341c29f1aebba81c31e257")
-    msg.get("MD5OfMessageAttributes").should.equal("049075255ebc53fb95f7f9f3cedf3c50")
-    msg.get("MessageId").should_not.contain(" \n")
+    assert msg.get("MD5OfMessageBody") == "58fd9edd83341c29f1aebba81c31e257"
+    assert msg.get("MD5OfMessageAttributes") == "049075255ebc53fb95f7f9f3cedf3c50"
+    assert " \n" not in msg.get("MessageId")
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
 
 @mock_sqs
@@ -537,12 +533,12 @@ def test_message_with_attributes_have_labels():
             }
         },
     )
-    msg.get("MD5OfMessageBody").should.equal("58fd9edd83341c29f1aebba81c31e257")
-    msg.get("MD5OfMessageAttributes").should.equal("2e2e4876d8e0bd6b8c2c8f556831c349")
-    msg.get("MessageId").should_not.contain(" \n")
+    assert msg.get("MD5OfMessageBody") == "58fd9edd83341c29f1aebba81c31e257"
+    assert msg.get("MD5OfMessageAttributes") == "2e2e4876d8e0bd6b8c2c8f556831c349"
+    assert " \n" not in msg.get("MessageId")
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
 
 @mock_sqs
@@ -561,9 +557,10 @@ def test_message_with_attributes_invalid_datatype():
             },
         )
     ex = e.value
-    ex.response["Error"]["Code"].should.equal("MessageAttributesInvalid")
-    ex.response["Error"]["Message"].should.equal(
-        "The message attribute 'timestamp' has an invalid message attribute type, the set of supported type "
+    assert ex.response["Error"]["Code"] == "MessageAttributesInvalid"
+    assert ex.response["Error"]["Message"] == (
+        "The message attribute 'timestamp' has an invalid message "
+        "attribute type, the set of supported type "
         "prefixes is Binary, Number, and String."
     )
 
@@ -584,13 +581,13 @@ def test_send_message_with_message_group_id():
     messages = queue.receive_messages(
         AttributeNames=["MessageDeduplicationId", "MessageGroupId"]
     )
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     message_attributes = messages[0].attributes
-    message_attributes.should.contain("MessageGroupId")
-    message_attributes["MessageGroupId"].should.equal("group_id_1")
-    message_attributes.should.contain("MessageDeduplicationId")
-    message_attributes["MessageDeduplicationId"].should.equal("dedupe_id_1")
+    assert "MessageGroupId" in message_attributes
+    assert message_attributes["MessageGroupId"] == "group_id_1"
+    assert "MessageDeduplicationId" in message_attributes
+    assert message_attributes["MessageDeduplicationId"] == "dedupe_id_1"
 
 
 @mock_sqs
@@ -602,8 +599,8 @@ def test_send_message_with_message_group_id_standard_queue():
         queue.send_message(MessageBody="mydata", MessageGroupId="group_id_1")
 
     err = ex.value.response["Error"]
-    err["Code"].should.equal("InvalidParameterValue")
-    err["Message"].should.equal(
+    assert err["Code"] == "InvalidParameterValue"
+    assert err["Message"] == (
         "Value group_id_1 for parameter MessageGroupId is invalid. "
         "Reason: The request include parameter that is not valid for this queue type."
     )
@@ -620,7 +617,7 @@ def test_send_message_with_unicode_characters():
     messages = queue.receive_messages()
     message_body = messages[0].body
 
-    message_body.should.equal(body_one)
+    assert message_body == body_one
 
 
 @mock_sqs
@@ -628,10 +625,10 @@ def test_set_queue_attributes():
     sqs = boto3.resource("sqs", region_name="us-east-1")
     queue = sqs.create_queue(QueueName=str(uuid4())[0:6])
 
-    queue.attributes["VisibilityTimeout"].should.equal("30")
+    assert queue.attributes["VisibilityTimeout"] == "30"
 
     queue.set_attributes(Attributes={"VisibilityTimeout": "45"})
-    queue.attributes["VisibilityTimeout"].should.equal("45")
+    assert queue.attributes["VisibilityTimeout"] == "45"
 
 
 def _get_common_url(region):
@@ -662,13 +659,13 @@ def test_create_queues_in_multiple_region():
 
     boto_common_url = _get_common_url("us-west-1")
     base_url = "http://localhost:5000" if settings.TEST_SERVER_MODE else boto_common_url
-    w1.list_queues()["QueueUrls"].should.contain(f"{base_url}/{ACCOUNT_ID}/{w1_name}")
-    w1.list_queues()["QueueUrls"].shouldnt.contain(f"{base_url}/{ACCOUNT_ID}/{w2_name}")
+    assert f"{base_url}/{ACCOUNT_ID}/{w1_name}" in w1.list_queues()["QueueUrls"]
+    assert f"{base_url}/{ACCOUNT_ID}/{w2_name}" not in w1.list_queues()["QueueUrls"]
 
     boto_common_url = _get_common_url("us-west-2")
     base_url = "http://localhost:5000" if settings.TEST_SERVER_MODE else boto_common_url
-    w2.list_queues()["QueueUrls"].shouldnt.contain(f"{base_url}/{ACCOUNT_ID}/{w1_name}")
-    w2.list_queues()["QueueUrls"].should.contain(f"{base_url}/{ACCOUNT_ID}/{w2_name}")
+    assert f"{base_url}/{ACCOUNT_ID}/{w1_name}" not in w2.list_queues()["QueueUrls"]
+    assert f"{base_url}/{ACCOUNT_ID}/{w2_name}" in w2.list_queues()["QueueUrls"]
 
 
 @mock_sqs
@@ -687,13 +684,13 @@ def test_get_queue_with_prefix():
     expected_url2 = f"{base_url}/{ACCOUNT_ID}/{q_name2}"
 
     all_urls = conn.list_queues()["QueueUrls"]
-    all_urls.should.contain(expected_url1)
-    all_urls.should.contain(expected_url2)
+    assert expected_url1 in all_urls
+    assert expected_url2 in all_urls
 
     queue = conn.list_queues(QueueNamePrefix=prefix)["QueueUrls"]
-    queue.should.have.length_of(1)
+    assert len(queue) == 1
 
-    queue[0].should.equal(expected_url2)
+    assert queue[0] == expected_url2
 
 
 @mock_sqs
@@ -705,12 +702,12 @@ def test_delete_queue():
     queue = sqs.Queue(q_resp["QueueUrl"])
 
     all_urls = conn.list_queues()["QueueUrls"]
-    [u[u.rfind("/") + 1 :] for u in all_urls].should.contain(q_name)
+    assert q_name in [u[u.rfind("/") + 1 :] for u in all_urls]
 
     queue.delete()
 
     all_urls = conn.list_queues().get("QueueUrls", [])
-    [u[u.rfind("/") + 1 :] for u in all_urls].shouldnt.contain(q_name)
+    assert q_name not in [u[u.rfind("/") + 1 :] for u in all_urls]
 
 
 @mock_sqs
@@ -723,12 +720,10 @@ def test_delete_queue_error_not_exists():
         )
 
     ex = e.value
-    ex.operation_name.should.equal("DeleteQueue")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.response["Error"]["Code"].should.contain(
-        "AWS.SimpleQueueService.NonExistentQueue"
-    )
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "DeleteQueue"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert "AWS.SimpleQueueService.NonExistentQueue" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
         "The specified queue does not exist for this wsdl version."
     )
 
@@ -755,19 +750,19 @@ def test_get_queue_attributes():
 
     response = client.get_queue_attributes(QueueUrl=queue_url, AttributeNames=["All"])
 
-    response["Attributes"]["ApproximateNumberOfMessages"].should.equal("0")
-    response["Attributes"]["ApproximateNumberOfMessagesDelayed"].should.equal("0")
-    response["Attributes"]["ApproximateNumberOfMessagesNotVisible"].should.equal("0")
-    response["Attributes"]["CreatedTimestamp"].should.be.a(str)
-    response["Attributes"]["DelaySeconds"].should.equal("0")
-    response["Attributes"]["LastModifiedTimestamp"].should.be.a(str)
-    response["Attributes"]["MaximumMessageSize"].should.equal("262144")
-    response["Attributes"]["MessageRetentionPeriod"].should.equal("345600")
-    response["Attributes"]["QueueArn"].should.equal(
+    assert response["Attributes"]["ApproximateNumberOfMessages"] == "0"
+    assert response["Attributes"]["ApproximateNumberOfMessagesDelayed"] == "0"
+    assert response["Attributes"]["ApproximateNumberOfMessagesNotVisible"] == "0"
+    assert isinstance(response["Attributes"]["CreatedTimestamp"], str)
+    assert response["Attributes"]["DelaySeconds"] == "0"
+    assert isinstance(response["Attributes"]["LastModifiedTimestamp"], str)
+    assert response["Attributes"]["MaximumMessageSize"] == "262144"
+    assert response["Attributes"]["MessageRetentionPeriod"] == "345600"
+    assert response["Attributes"]["QueueArn"] == (
         f"arn:aws:sqs:us-east-1:{ACCOUNT_ID}:{q_name}"
     )
-    response["Attributes"]["ReceiveMessageWaitTimeSeconds"].should.equal("0")
-    response["Attributes"]["VisibilityTimeout"].should.equal("30")
+    assert response["Attributes"]["ReceiveMessageWaitTimeSeconds"] == "0"
+    assert response["Attributes"]["VisibilityTimeout"] == "30"
 
     response = client.get_queue_attributes(
         QueueUrl=queue_url,
@@ -780,24 +775,22 @@ def test_get_queue_attributes():
         ],
     )
 
-    response["Attributes"].should.equal(
-        {
-            "ApproximateNumberOfMessages": "0",
-            "MaximumMessageSize": "262144",
-            "QueueArn": f"arn:aws:sqs:us-east-1:{ACCOUNT_ID}:{q_name}",
-            "VisibilityTimeout": "30",
-            "RedrivePolicy": json.dumps(
-                {"deadLetterTargetArn": dlq_arn1, "maxReceiveCount": 2}
-            ),
-        }
-    )
+    assert response["Attributes"] == {
+        "ApproximateNumberOfMessages": "0",
+        "MaximumMessageSize": "262144",
+        "QueueArn": f"arn:aws:sqs:us-east-1:{ACCOUNT_ID}:{q_name}",
+        "VisibilityTimeout": "30",
+        "RedrivePolicy": json.dumps(
+            {"deadLetterTargetArn": dlq_arn1, "maxReceiveCount": 2}
+        ),
+    }
 
     # should not return any attributes, if it was not set before
     response = client.get_queue_attributes(
         QueueUrl=queue_url, AttributeNames=["KmsMasterKeyId"]
     )
 
-    response.should_not.have.key("Attributes")
+    assert "Attributes" not in response
 
 
 @mock_sqs
@@ -806,18 +799,23 @@ def test_get_queue_attributes_errors():
     response = client.create_queue(QueueName=str(uuid4())[0:6])
     queue_url = response["QueueUrl"]
 
-    client.get_queue_attributes.when.called_with(
-        QueueUrl=queue_url,
-        AttributeNames=["QueueArn", "not-existing", "VisibilityTimeout"],
-    ).should.throw(ClientError, "Unknown Attribute not-existing.")
+    with pytest.raises(ClientError) as client_error:
+        client.get_queue_attributes(
+            QueueUrl=queue_url,
+            AttributeNames=["QueueArn", "not-existing", "VisibilityTimeout"],
+        )
+    assert (
+        client_error.value.response["Error"]["Message"]
+        == "Unknown Attribute not-existing."
+    )
 
-    client.get_queue_attributes.when.called_with(
-        QueueUrl=queue_url, AttributeNames=[""]
-    ).should.throw(ClientError, "Unknown Attribute .")
+    with pytest.raises(ClientError) as client_error:
+        client.get_queue_attributes(QueueUrl=queue_url, AttributeNames=[""])
+    assert client_error.value.response["Error"]["Message"] == "Unknown Attribute ."
 
-    client.get_queue_attributes.when.called_with(
-        QueueUrl=queue_url, AttributeNames=[]
-    ).should.throw(ClientError, "Unknown Attribute .")
+    with pytest.raises(ClientError) as client_error:
+        client.get_queue_attributes(QueueUrl=queue_url, AttributeNames=[])
+    assert client_error.value.response["Error"]["Message"] == "Unknown Attribute ."
 
 
 @mock_sqs
@@ -833,12 +831,10 @@ def test_get_queue_attributes_error_not_exists():
 
     # then
     ex = e.value
-    ex.operation_name.should.equal("GetQueueAttributes")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.response["Error"]["Code"].should.contain(
-        "AWS.SimpleQueueService.NonExistentQueue"
-    )
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "GetQueueAttributes"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert "AWS.SimpleQueueService.NonExistentQueue" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
         "The specified queue does not exist for this wsdl version."
     )
 
@@ -852,11 +848,11 @@ def test_set_queue_attribute():
     )
 
     queue = sqs.Queue(q_resp["QueueUrl"])
-    queue.attributes["VisibilityTimeout"].should.equal("3")
+    assert queue.attributes["VisibilityTimeout"] == "3"
 
     queue.set_attributes(Attributes={"VisibilityTimeout": "45"})
     queue = sqs.Queue(q_resp["QueueUrl"])
-    queue.attributes["VisibilityTimeout"].should.equal("45")
+    assert queue.attributes["VisibilityTimeout"] == "45"
 
 
 @mock_sqs
@@ -879,14 +875,14 @@ def test_send_receive_message_without_attributes():
     message1 = messages[0]
     message2 = messages[1]
 
-    message1["Body"].should.equal(body_one)
-    message2["Body"].should.equal(body_two)
+    assert message1["Body"] == body_one
+    assert message2["Body"] == body_two
 
-    message1.shouldnt.have.key("MD5OfMessageAttributes")
-    message2.shouldnt.have.key("MD5OfMessageAttributes")
+    assert "MD5OfMessageAttributes" not in message1
+    assert "MD5OfMessageAttributes" not in message2
 
-    message1.should_not.have.key("Attributes")
-    message2.should_not.have.key("Attributes")
+    assert "Attributes" not in message1
+    assert "Attributes" not in message2
 
 
 @mock_sqs
@@ -920,15 +916,11 @@ def test_send_receive_message_with_attributes():
     message1 = messages[0]
     message2 = messages[1]
 
-    message1.get("Body").should.equal(body_one)
-    message2.get("Body").should.equal(body_two)
+    assert message1.get("Body") == body_one
+    assert message2.get("Body") == body_two
 
-    message1.get("MD5OfMessageAttributes").should.equal(
-        "235c5c510d26fb653d073faed50ae77c"
-    )
-    message2.get("MD5OfMessageAttributes").should.equal(
-        "994258b45346a2cc3f9cbb611aa7af30"
-    )
+    assert message1.get("MD5OfMessageAttributes") == "235c5c510d26fb653d073faed50ae77c"
+    assert message2.get("MD5OfMessageAttributes") == "994258b45346a2cc3f9cbb611aa7af30"
 
 
 @mock_sqs
@@ -968,15 +960,11 @@ def test_send_receive_message_with_attributes_with_labels():
     message1 = messages[0]
     message2 = messages[1]
 
-    message1.get("Body").should.equal(body_one)
-    message2.get("Body").should.equal(body_two)
+    assert message1.get("Body") == body_one
+    assert message2.get("Body") == body_two
 
-    message1.get("MD5OfMessageAttributes").should.equal(
-        "2e2e4876d8e0bd6b8c2c8f556831c349"
-    )
-    message2.get("MD5OfMessageAttributes").should.equal(
-        "cfa7c73063c6e2dbf9be34232a1978cf"
-    )
+    assert message1.get("MD5OfMessageAttributes") == "2e2e4876d8e0bd6b8c2c8f556831c349"
+    assert message2.get("MD5OfMessageAttributes") == "cfa7c73063c6e2dbf9be34232a1978cf"
 
     response = queue.send_message(
         MessageBody="test message",
@@ -985,16 +973,17 @@ def test_send_receive_message_with_attributes_with_labels():
         },
     )
 
-    response.get("MD5OfMessageAttributes").should.equal(
-        "9e05cca738e70ff6c6041e82d5e77ef1"
-    )
+    assert response.get("MD5OfMessageAttributes") == "9e05cca738e70ff6c6041e82d5e77ef1"
 
 
 @mock_sqs
 def test_receive_message_with_xml_content():
     sqs = boto3.client("sqs", region_name="eu-west-2")
     queue_url = sqs.create_queue(QueueName=str(uuid4())[0:6])["QueueUrl"]
-    original_payload = '<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom"/>'
+    original_payload = (
+        '<?xml version="1.0" encoding="UTF-8"?>'
+        '<feed xmlns="http://www.w3.org/2005/Atom"/>'
+    )
     data = {"Payload": {"DataType": "String", "StringValue": original_payload}}
 
     sqs.send_message(
@@ -1009,7 +998,7 @@ def test_receive_message_with_xml_content():
     )["Messages"]
 
     attr = messages[0]["MessageAttributes"]["Payload"]["StringValue"]
-    attr.should.equal(original_payload)
+    assert attr == original_payload
 
 
 @mock_sqs
@@ -1025,7 +1014,7 @@ def test_change_message_visibility_than_permitted():
         queue = sqs.Queue(q_resp["QueueUrl"])
         queue.send_message(MessageBody="derp")
         messages = conn.receive_message(QueueUrl=queue.url)
-        messages.get("Messages").should.have.length_of(1)
+        assert len(messages.get("Messages")) == 1
 
         conn.change_message_visibility(
             QueueUrl=queue.url,
@@ -1043,8 +1032,8 @@ def test_change_message_visibility_than_permitted():
             )
 
         ex = err.value
-        ex.operation_name.should.equal("ChangeMessageVisibility")
-        ex.response["Error"]["Code"].should.equal("InvalidParameterValue")
+        assert ex.operation_name == "ChangeMessageVisibility"
+        assert ex.response["Error"]["Code"] == "InvalidParameterValue"
 
 
 @mock_sqs
@@ -1069,8 +1058,14 @@ def test_send_receive_message_timestamps():
         "ApproximateFirstReceiveTimestamp"
     )
 
-    int.when.called_with(sent_timestamp).shouldnt.throw(ValueError)
-    int.when.called_with(approximate_first_receive_timestamp).shouldnt.throw(ValueError)
+    try:
+        int(sent_timestamp)
+    except ValueError:
+        assert False, "sent_timestamp not an int"
+    try:
+        int(approximate_first_receive_timestamp)
+    except ValueError:
+        assert False, "aproximate_first_receive_timestamp not an int"
 
 
 @mock_sqs
@@ -1080,61 +1075,61 @@ def test_send_receive_message_timestamps():
         (
             "All",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should_not.be.empty,
-                "ApproximateReceiveCount": lambda x: x.should.equal("1"),
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should_not.be.empty,
-                "SentTimestamp": lambda x: x.should_not.be.empty,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x,
+                "ApproximateReceiveCount": lambda x: x == "1",
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x,
+                "SentTimestamp": lambda x: x,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "ApproximateFirstReceiveTimestamp",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should_not.be.empty,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "ApproximateReceiveCount",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.equal("1"),
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x == "1",
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "SenderId",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should_not.be.empty,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "SentTimestamp",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should_not.be.empty,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
     ],
@@ -1165,39 +1160,39 @@ def test_send_receive_message_with_attribute_name(attribute_name, expected):
     message1 = messages[0]
     message2 = messages[1]
 
-    message1["Body"].should.equal(body_one)
-    message2["Body"].should.equal(body_two)
+    assert message1["Body"] == body_one
+    assert message2["Body"] == body_two
 
-    message1.shouldnt.have.key("MD5OfMessageAttributes")
-    message2.shouldnt.have.key("MD5OfMessageAttributes")
+    assert "MD5OfMessageAttributes" not in message1
+    assert "MD5OfMessageAttributes" not in message2
 
-    expected["ApproximateFirstReceiveTimestamp"](
+    assert expected["ApproximateFirstReceiveTimestamp"](
         message1["Attributes"].get("ApproximateFirstReceiveTimestamp")
     )
-    expected["ApproximateReceiveCount"](
+    assert expected["ApproximateReceiveCount"](
         message1["Attributes"].get("ApproximateReceiveCount")
     )
-    expected["MessageDeduplicationId"](
+    assert expected["MessageDeduplicationId"](
         message1["Attributes"].get("MessageDeduplicationId")
     )
-    expected["MessageGroupId"](message1["Attributes"].get("MessageGroupId"))
-    expected["SenderId"](message1["Attributes"].get("SenderId"))
-    expected["SentTimestamp"](message1["Attributes"].get("SentTimestamp"))
-    expected["SequenceNumber"](message1["Attributes"].get("SequenceNumber"))
+    assert expected["MessageGroupId"](message1["Attributes"].get("MessageGroupId"))
+    assert expected["SenderId"](message1["Attributes"].get("SenderId"))
+    assert expected["SentTimestamp"](message1["Attributes"].get("SentTimestamp"))
+    assert expected["SequenceNumber"](message1["Attributes"].get("SequenceNumber"))
 
-    expected["ApproximateFirstReceiveTimestamp"](
+    assert expected["ApproximateFirstReceiveTimestamp"](
         message2["Attributes"].get("ApproximateFirstReceiveTimestamp")
     )
-    expected["ApproximateReceiveCount"](
+    assert expected["ApproximateReceiveCount"](
         message2["Attributes"].get("ApproximateReceiveCount")
     )
-    expected["MessageDeduplicationId"](
+    assert expected["MessageDeduplicationId"](
         message2["Attributes"].get("MessageDeduplicationId")
     )
-    expected["MessageGroupId"](message2["Attributes"].get("MessageGroupId"))
-    expected["SenderId"](message2["Attributes"].get("SenderId"))
-    expected["SentTimestamp"](message2["Attributes"].get("SentTimestamp"))
-    expected["SequenceNumber"](message2["Attributes"].get("SequenceNumber"))
+    assert expected["MessageGroupId"](message2["Attributes"].get("MessageGroupId"))
+    assert expected["SenderId"](message2["Attributes"].get("SenderId"))
+    assert expected["SentTimestamp"](message2["Attributes"].get("SentTimestamp"))
+    assert expected["SequenceNumber"](message2["Attributes"].get("SequenceNumber"))
 
 
 @mock_sqs
@@ -1207,97 +1202,97 @@ def test_send_receive_message_with_attribute_name(attribute_name, expected):
         (
             "All",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should_not.be.empty,
-                "ApproximateReceiveCount": lambda x: x.should.equal("1"),
-                "MessageDeduplicationId": lambda x: x.should.equal("123"),
-                "MessageGroupId": lambda x: x.should.equal("456"),
-                "SenderId": lambda x: x.should_not.be.empty,
-                "SentTimestamp": lambda x: x.should_not.be.empty,
-                "SequenceNumber": lambda x: x.should_not.be.empty,
+                "ApproximateFirstReceiveTimestamp": lambda x: x,
+                "ApproximateReceiveCount": lambda x: x == "1",
+                "MessageDeduplicationId": lambda x: x == "123",
+                "MessageGroupId": lambda x: x == "456",
+                "SenderId": lambda x: x,
+                "SentTimestamp": lambda x: x,
+                "SequenceNumber": lambda x: x,
             },
         ),
         (
             "ApproximateFirstReceiveTimestamp",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should_not.be.empty,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "ApproximateReceiveCount",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.equal("1"),
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x == "1",
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "MessageDeduplicationId",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.equal("123"),
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x == "123",
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "MessageGroupId",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.equal("456"),
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x == "456",
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "SenderId",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should_not.be.empty,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "SentTimestamp",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should_not.be.empty,
-                "SequenceNumber": lambda x: x.should.be.none,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x,
+                "SequenceNumber": lambda x: x is None,
             },
         ),
         (
             "SequenceNumber",
             {
-                "ApproximateFirstReceiveTimestamp": lambda x: x.should.be.none,
-                "ApproximateReceiveCount": lambda x: x.should.be.none,
-                "MessageDeduplicationId": lambda x: x.should.be.none,
-                "MessageGroupId": lambda x: x.should.be.none,
-                "SenderId": lambda x: x.should.be.none,
-                "SentTimestamp": lambda x: x.should.be.none,
-                "SequenceNumber": lambda x: x.should_not.be.empty,
+                "ApproximateFirstReceiveTimestamp": lambda x: x is None,
+                "ApproximateReceiveCount": lambda x: x is None,
+                "MessageDeduplicationId": lambda x: x is None,
+                "MessageGroupId": lambda x: x is None,
+                "SenderId": lambda x: x is None,
+                "SentTimestamp": lambda x: x is None,
+                "SequenceNumber": lambda x: x,
             },
         ),
     ],
@@ -1331,38 +1326,36 @@ def test_fifo_send_receive_message_with_attribute_name(attribute_name, expected)
         QueueUrl=queue_url, AttributeNames=[attribute_name], MaxNumberOfMessages=2
     )["Messages"][0]
 
-    message["Body"].should.equal(body)
+    assert message["Body"] == body
 
-    message.should_not.have.key("MD5OfMessageAttributes")
+    assert "MD5OfMessageAttributes" not in message
 
-    expected["ApproximateFirstReceiveTimestamp"](
+    assert expected["ApproximateFirstReceiveTimestamp"](
         message["Attributes"].get("ApproximateFirstReceiveTimestamp")
     )
-    expected["ApproximateReceiveCount"](
+    assert expected["ApproximateReceiveCount"](
         message["Attributes"].get("ApproximateReceiveCount")
     )
-    expected["MessageDeduplicationId"](
+    assert expected["MessageDeduplicationId"](
         message["Attributes"].get("MessageDeduplicationId")
     )
-    expected["MessageGroupId"](message["Attributes"].get("MessageGroupId"))
-    expected["SenderId"](message["Attributes"].get("SenderId"))
-    expected["SentTimestamp"](message["Attributes"].get("SentTimestamp"))
-    expected["SequenceNumber"](message["Attributes"].get("SequenceNumber"))
+    assert expected["MessageGroupId"](message["Attributes"].get("MessageGroupId"))
+    assert expected["SenderId"](message["Attributes"].get("SenderId"))
+    assert expected["SentTimestamp"](message["Attributes"].get("SentTimestamp"))
+    assert expected["SequenceNumber"](message["Attributes"].get("SequenceNumber"))
 
 
 @mock_sqs
 def test_get_queue_attributes_no_param():
-    """
-    AWS does not return the Attributes-key when omitting the AttributeNames-parameter
-    """
+    """Test Attributes-key is not returned when no AttributeNames-parameter."""
     sqs = boto3.client("sqs", region_name="ap-northeast-3")
     queue_url = sqs.create_queue(QueueName=str(uuid4())[0:6])["QueueUrl"]
 
     queue_attrs = sqs.get_queue_attributes(QueueUrl=queue_url)
-    queue_attrs.shouldnt.have.key("Attributes")
+    assert "Attributes" not in queue_attrs
 
     queue_attrs = sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=["All"])
-    queue_attrs.should.have.key("Attributes")
+    assert "Attributes" in queue_attrs
 
 
 @mock_sqs
@@ -1407,7 +1400,7 @@ def test_receive_messages_with_wait_seconds_timeout_of_zero():
     queue = sqs.create_queue(QueueName=str(uuid4())[0:6])
 
     messages = queue.receive_messages(WaitTimeSeconds=0)
-    messages.should.equal([])
+    assert messages == []
 
 
 @mock_sqs
@@ -1422,7 +1415,7 @@ def test_send_message_with_xml_characters():
 
     messages = client.receive_message(QueueUrl=queue.url)["Messages"]
 
-    messages[0]["Body"].should.equal(body_one)
+    assert messages[0]["Body"] == body_one
 
 
 @mock_sqs
@@ -1437,12 +1430,12 @@ def test_send_message_with_delay():
     queue.send_message(MessageBody=body_two)
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
-    messages[0].body.should.equal(body_two)
+    assert messages[0].body == body_two
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(0)
+    assert len(messages) == 0
 
 
 @mock_sqs
@@ -1454,8 +1447,8 @@ def test_send_large_message_fails():
     with pytest.raises(ClientError) as ex:
         queue.send_message(MessageBody=body)
     err = ex.value.response["Error"]
-    err["Code"].should.equal("InvalidParameterValue")
-    err["Message"].should.equal(
+    assert err["Code"] == "InvalidParameterValue"
+    assert err["Message"] == (
         "One or more parameters are invalid. Reason: Message must be shorter than 262144 bytes."
     )
 
@@ -1467,25 +1460,25 @@ def test_message_becomes_inflight_when_received():
         QueueName=str(uuid4())[0:6], Attributes={"VisibilityTimeout ": "2"}
     )
 
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
     body = "this is a test message"
     queue.send_message(MessageBody=body)
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
     # Wait
     time.sleep(3)
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
 
 
 @mock_sqs
@@ -1495,19 +1488,19 @@ def test_receive_message_with_explicit_visibility_timeout():
         QueueName=str(uuid4())[0:6], Attributes={"VisibilityTimeout ": "1"}
     )
 
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
     body = "this is a test message"
     queue.send_message(MessageBody=body)
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
 
     messages = queue.receive_messages(VisibilityTimeout=0)
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
 
 
 @mock_sqs
@@ -1521,13 +1514,13 @@ def test_change_message_visibility():
     queue.send_message(MessageBody=body)
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
     messages = queue.receive_messages()
 
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
     messages[0].change_visibility(VisibilityTimeout=2)
 
@@ -1536,18 +1529,18 @@ def test_change_message_visibility():
 
     # Message is not visible
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
     time.sleep(2)
 
     # Message now becomes visible
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
 
     messages = queue.receive_messages()
     messages[0].delete()
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
 
 @mock_sqs
@@ -1563,8 +1556,8 @@ def test_change_message_visibility_on_unknown_receipt_handle():
             QueueUrl=queue.url, ReceiptHandle="unknown-stuff", VisibilityTimeout=432
         )
     err = exc.value.response["Error"]
-    err["Code"].should.equal("ReceiptHandleIsInvalid")
-    err["Message"].should.equal("The input receipt handle is invalid.")
+    assert err["Code"] == "ReceiptHandleIsInvalid"
+    assert err["Message"] == "The input receipt handle is invalid."
 
 
 @mock_sqs
@@ -1578,7 +1571,7 @@ def test_queue_length():
     queue.send_message(MessageBody="this is another test message")
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("2")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "2"
 
 
 @mock_sqs
@@ -1600,7 +1593,7 @@ def test_delete_batch_operation():
     )
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
 
 
 @mock_sqs
@@ -1614,21 +1607,21 @@ def test_change_message_visibility_on_old_message():
 
     messages = queue.receive_messages(MaxNumberOfMessages=1)
 
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     original_message = messages[0]
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
     time.sleep(2)
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
 
     messages = queue.receive_messages(MaxNumberOfMessages=1)
 
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     # Docs indicate this should throw an ReceiptHandleIsInvalid, but this is allowed in AWS
     original_message.change_visibility(VisibilityTimeout=100)
@@ -1639,7 +1632,7 @@ def test_change_message_visibility_on_old_message():
 
     # Message is not yet available, because of the visibility-timeout
     messages = queue.receive_messages(MaxNumberOfMessages=1)
-    messages.should.have.length_of(0)
+    assert len(messages) == 0
 
 
 @mock_sqs
@@ -1651,22 +1644,22 @@ def test_change_message_visibility_on_visible_message():
 
     queue.send_message(MessageBody="test message")
     messages = queue.receive_messages(MaxNumberOfMessages=1)
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
     time.sleep(2)
 
     messages = queue.receive_messages(MaxNumberOfMessages=1)
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     messages[0].change_visibility(VisibilityTimeout=100)
 
     time.sleep(2)
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
 
 @mock_sqs
@@ -1697,8 +1690,8 @@ def test_purge_queue_before_delete_message():
     )
     receive_resp2 = client.receive_message(QueueUrl=queue_url)
 
-    len(receive_resp2.get("Messages", [])).should.equal(1)
-    receive_resp2["Messages"][0]["Body"].should.equal("second_message")
+    assert len(receive_resp2.get("Messages", [])) == 1
+    assert receive_resp2["Messages"][0]["Body"] == "second_message"
 
 
 @mock_sqs
@@ -1713,7 +1706,7 @@ def test_delete_message_after_visibility_timeout():
     queue.send_message(MessageBody="Message 1!")
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("1")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "1"
 
     m1_retrieved = queue.receive_messages()[0]
 
@@ -1722,7 +1715,7 @@ def test_delete_message_after_visibility_timeout():
     m1_retrieved.delete()
 
     queue.reload()
-    queue.attributes["ApproximateNumberOfMessages"].should.equal("0")
+    assert queue.attributes["ApproximateNumberOfMessages"] == "0"
 
 
 @mock_sqs
@@ -1734,15 +1727,20 @@ def test_delete_message_errors():
     response = client.receive_message(QueueUrl=queue_url)
     receipt_handle = response["Messages"][0]["ReceiptHandle"]
 
-    client.delete_message.when.called_with(
-        QueueUrl=queue_url + "-not-existing", ReceiptHandle=receipt_handle
-    ).should.throw(
-        ClientError, "The specified queue does not exist for this wsdl version."
+    with pytest.raises(ClientError) as client_error:
+        client.delete_message(
+            QueueUrl=queue_url + "-not-existing", ReceiptHandle=receipt_handle
+        )
+    assert client_error.value.response["Error"]["Message"] == (
+        "The specified queue does not exist for this wsdl version."
     )
 
-    client.delete_message.when.called_with(
-        QueueUrl=queue_url, ReceiptHandle="not-existing"
-    ).should.throw(ClientError, "The input receipt handle is invalid.")
+    with pytest.raises(ClientError) as client_error:
+        client.delete_message(QueueUrl=queue_url, ReceiptHandle="not-existing")
+    assert (
+        client_error.value.response["Error"]["Message"]
+        == "The input receipt handle is invalid."
+    )
 
 
 @mock_sqs
@@ -1774,16 +1772,17 @@ def test_delete_message_using_old_receipt_handle():
     response = client.receive_message(QueueUrl=queue_url)
     receipt_2 = response["Messages"][0]["ReceiptHandle"]
 
-    receipt_1.shouldnt.equal(receipt_2)
+    assert receipt_1 != receipt_2
 
     # Can use an old receipt_handle to delete a message
     client.delete_message(QueueUrl=queue_url, ReceiptHandle=receipt_1)
     # Sanity check the message really is gone
-    client.receive_message(QueueUrl=queue_url).shouldnt.have.key("Messages")
+    assert "Messages" not in client.receive_message(QueueUrl=queue_url)
     # We can delete it again
     client.delete_message(QueueUrl=queue_url, ReceiptHandle=receipt_1)
 
-    # Can use the second receipt handle to delete it 'again' - succeeds, as it is idempotent against the message
+    # Can use the second receipt handle to delete it 'again' - succeeds,
+    # as it is idempotent against the message
     client.delete_message(QueueUrl=queue_url, ReceiptHandle=receipt_2)
 
 
@@ -1824,9 +1823,7 @@ def test_send_message_batch():
         ],
     )
 
-    sorted([entry["Id"] for entry in response["Successful"]]).should.equal(
-        ["id_1", "id_2"]
-    )
+    assert sorted([entry["Id"] for entry in response["Successful"]]) == ["id_1", "id_2"]
 
     response = client.receive_message(
         QueueUrl=queue_url,
@@ -1835,24 +1832,24 @@ def test_send_message_batch():
         AttributeNames=["MessageDeduplicationId", "MessageGroupId"],
     )
 
-    response["Messages"][0]["Body"].should.equal("body_1")
-    response["Messages"][0]["MessageAttributes"].should.equal(
+    assert response["Messages"][0]["Body"] == "body_1"
+    assert response["Messages"][0]["MessageAttributes"] == (
         {"attribute_name_1": {"StringValue": "attribute_value_1", "DataType": "String"}}
     )
-    response["Messages"][0]["Attributes"]["MessageGroupId"].should.equal(
-        "message_group_id_1"
+    assert (
+        response["Messages"][0]["Attributes"]["MessageGroupId"] == "message_group_id_1"
     )
-    response["Messages"][0]["Attributes"]["MessageDeduplicationId"].should.equal(
+    assert response["Messages"][0]["Attributes"]["MessageDeduplicationId"] == (
         "message_deduplication_id_1"
     )
-    response["Messages"][1]["Body"].should.equal("body_2")
-    response["Messages"][1]["MessageAttributes"].should.equal(
+    assert response["Messages"][1]["Body"] == "body_2"
+    assert response["Messages"][1]["MessageAttributes"] == (
         {"attribute_name_2": {"StringValue": "123", "DataType": "Number"}}
     )
-    response["Messages"][1]["Attributes"]["MessageGroupId"].should.equal(
+    assert response["Messages"][1]["Attributes"]["MessageGroupId"] == (
         "message_group_id_2"
     )
-    response["Messages"][1]["Attributes"]["MessageDeduplicationId"].should.equal(
+    assert response["Messages"][1]["Attributes"]["MessageDeduplicationId"] == (
         "message_deduplication_id_2"
     )
 
@@ -1914,13 +1911,19 @@ def test_delete_message_batch_with_invalid_receipt_id():
             "Id": "fake-receipt-handle-1",
             "SenderFault": True,
             "Code": "ReceiptHandleIsInvalid",
-            "Message": 'The input receipt handle "fake-receipt-handle-1" is not a valid receipt handle.',
+            "Message": (
+                'The input receipt handle "fake-receipt-handle-1" is not '
+                "a valid receipt handle."
+            ),
         },
         {
             "Id": "fake-receipt-handle-2",
             "SenderFault": True,
             "Code": "ReceiptHandleIsInvalid",
-            "Message": 'The input receipt handle "fake-receipt-handle-2" is not a valid receipt handle.',
+            "Message": (
+                'The input receipt handle "fake-receipt-handle-2" is not '
+                "a valid receipt handle."
+            ),
         },
     ]
 
@@ -1947,14 +1950,12 @@ def test_message_attributes_in_receive_message():
         QueueUrl=queue.url, MaxNumberOfMessages=2, MessageAttributeNames=["timestamp"]
     )["Messages"]
 
-    messages[0]["MessageAttributes"].should.equal(
-        {
-            "timestamp": {
-                "StringValue": "1493147359900",
-                "DataType": "Number.java.lang.Long",
-            }
+    assert messages[0]["MessageAttributes"] == {
+        "timestamp": {
+            "StringValue": "1493147359900",
+            "DataType": "Number.java.lang.Long",
         }
-    )
+    }
 
     queue.send_message(
         MessageBody=body_one,
@@ -1969,7 +1970,7 @@ def test_message_attributes_in_receive_message():
         "Messages"
     ]
 
-    messages[0].get("MessageAttributes").should.equal(None)
+    assert messages[0].get("MessageAttributes") is None
 
     queue.send_message(
         MessageBody=body_one,
@@ -1984,14 +1985,12 @@ def test_message_attributes_in_receive_message():
         QueueUrl=queue.url, MaxNumberOfMessages=2, MessageAttributeNames=["All"]
     )["Messages"]
 
-    messages[0]["MessageAttributes"].should.equal(
-        {
-            "timestamp": {
-                "StringValue": "1493147359900",
-                "DataType": "Number.java.lang.Long",
-            }
+    assert messages[0]["MessageAttributes"] == {
+        "timestamp": {
+            "StringValue": "1493147359900",
+            "DataType": "Number.java.lang.Long",
         }
-    )
+    }
 
 
 @mock_sqs
@@ -2001,69 +2000,76 @@ def test_send_message_batch_errors():
     response = client.create_queue(QueueName="test-queue")
     queue_url = response["QueueUrl"]
 
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url + "-not-existing",
-        Entries=[{"Id": "id_1", "MessageBody": "body_1"}],
-    ).should.throw(
-        ClientError, "The specified queue does not exist for this wsdl version."
+    with pytest.raises(ClientError) as client_error:
+        client.send_message_batch(
+            QueueUrl=queue_url + "-not-existing",
+            Entries=[{"Id": "id_1", "MessageBody": "body_1"}],
+        )
+    assert client_error.value.response["Error"]["Message"] == (
+        "The specified queue does not exist for this wsdl version."
     )
 
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url, Entries=[]
-    ).should.throw(
-        ClientError,
-        "There should be at least one SendMessageBatchRequestEntry in the request.",
+    with pytest.raises(ClientError) as client_error:
+        client.send_message_batch(QueueUrl=queue_url, Entries=[])
+    assert client_error.value.response["Error"]["Message"] == (
+        "There should be at least one SendMessageBatchRequestEntry in the request."
     )
 
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url, Entries=[{"Id": "", "MessageBody": "body_1"}]
-    ).should.throw(
-        ClientError,
+    with pytest.raises(ClientError) as client_error:
+        client.send_message_batch(
+            QueueUrl=queue_url, Entries=[{"Id": "", "MessageBody": "body_1"}]
+        )
+    assert client_error.value.response["Error"]["Message"] == (
         "A batch entry id can only contain alphanumeric characters, "
-        "hyphens and underscores. It can be at most 80 letters long.",
+        "hyphens and underscores. It can be at most 80 letters long."
     )
 
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url, Entries=[{"Id": ".!@#$%^&*()+=", "MessageBody": "body_1"}]
-    ).should.throw(
-        ClientError,
+    with pytest.raises(ClientError) as client_error:
+        client.send_message_batch(
+            QueueUrl=queue_url,
+            Entries=[{"Id": ".!@#$%^&*()+=", "MessageBody": "body_1"}],
+        )
+    assert client_error.value.response["Error"]["Message"] == (
         "A batch entry id can only contain alphanumeric characters, "
-        "hyphens and underscores. It can be at most 80 letters long.",
+        "hyphens and underscores. It can be at most 80 letters long."
     )
 
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url, Entries=[{"Id": "i" * 81, "MessageBody": "body_1"}]
-    ).should.throw(
-        ClientError,
+    with pytest.raises(ClientError) as client_error:
+        client.send_message_batch(
+            QueueUrl=queue_url, Entries=[{"Id": "i" * 81, "MessageBody": "body_1"}]
+        )
+    assert client_error.value.response["Error"]["Message"] == (
         "A batch entry id can only contain alphanumeric characters, "
-        "hyphens and underscores. It can be at most 80 letters long.",
+        "hyphens and underscores. It can be at most 80 letters long."
     )
 
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url, Entries=[{"Id": "id_1", "MessageBody": "b" * 262145}]
-    ).should.throw(
-        ClientError,
+    with pytest.raises(ClientError) as client_error:
+        client.send_message_batch(
+            QueueUrl=queue_url, Entries=[{"Id": "id_1", "MessageBody": "b" * 262145}]
+        )
+    assert client_error.value.response["Error"]["Message"] == (
         "Batch requests cannot be longer than 262144 bytes. "
-        "You have sent 262145 bytes.",
+        "You have sent 262145 bytes."
     )
 
-    # only the first duplicated Id is reported
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url,
-        Entries=[
-            {"Id": "id_1", "MessageBody": "body_1"},
-            {"Id": "id_2", "MessageBody": "body_2"},
-            {"Id": "id_2", "MessageBody": "body_2"},
-            {"Id": "id_1", "MessageBody": "body_1"},
-        ],
-    ).should.throw(ClientError, "Id id_2 repeated.")
+    with pytest.raises(ClientError) as client_error:
+        # only the first duplicated Id is reported
+        client.send_message_batch(
+            QueueUrl=queue_url,
+            Entries=[
+                {"Id": "id_1", "MessageBody": "body_1"},
+                {"Id": "id_2", "MessageBody": "body_2"},
+                {"Id": "id_2", "MessageBody": "body_2"},
+                {"Id": "id_1", "MessageBody": "body_1"},
+            ],
+        )
+    assert client_error.value.response["Error"]["Message"] == "Id id_2 repeated."
 
     entries = [{"Id": f"id_{i}", "MessageBody": f"body_{i}"} for i in range(11)]
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url, Entries=entries
-    ).should.throw(
-        ClientError,
-        "Maximum number of entries per request are 10. " "You have sent 11.",
+    with pytest.raises(ClientError) as client_error:
+        assert client.send_message_batch(QueueUrl=queue_url, Entries=entries)
+    assert client_error.value.response["Error"]["Message"] == (
+        "Maximum number of entries per request are 10. You have sent 11."
     )
 
 
@@ -2074,11 +2080,10 @@ def test_send_message_batch_with_empty_list():
     response = client.create_queue(QueueName="test-queue")
     queue_url = response["QueueUrl"]
 
-    client.send_message_batch.when.called_with(
-        QueueUrl=queue_url, Entries=[]
-    ).should.throw(
-        ClientError,
-        "There should be at least one SendMessageBatchRequestEntry in the request.",
+    with pytest.raises(ClientError) as client_error:
+        client.send_message_batch(QueueUrl=queue_url, Entries=[])
+    assert client_error.value.response["Error"]["Message"] == (
+        "There should be at least one SendMessageBatchRequestEntry in the request."
     )
 
 
@@ -2107,12 +2112,12 @@ def test_batch_change_message_visibility():
 
     with freeze_time("2015-01-01 12:01:00"):
         receive_resp = sqs.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=2)
-        len(receive_resp["Messages"]).should.equal(2)
+        assert len(receive_resp["Messages"]) == 2
 
         handles = [item["ReceiptHandle"] for item in receive_resp["Messages"]]
         entries = [
             {
-                "Id": str(uuid.uuid4()),
+                "Id": str(uuid4()),
                 "ReceiptHandle": handle,
                 "VisibilityTimeout": 43000,
             }
@@ -2120,19 +2125,19 @@ def test_batch_change_message_visibility():
         ]
 
         resp = sqs.change_message_visibility_batch(QueueUrl=queue_url, Entries=entries)
-        len(resp["Successful"]).should.equal(2)
+        assert len(resp["Successful"]) == 2
 
     with freeze_time("2015-01-01 14:00:00"):
         resp = sqs.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=3)
-        len(resp["Messages"]).should.equal(1)
+        assert len(resp["Messages"]) == 1
 
     with freeze_time("2015-01-01 16:00:00"):
         resp = sqs.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=3)
-        len(resp["Messages"]).should.equal(1)
+        assert len(resp["Messages"]) == 1
 
     with freeze_time("2015-01-02 12:00:00"):
         resp = sqs.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=3)
-        len(resp["Messages"]).should.equal(3)
+        assert len(resp["Messages"]) == 3
 
 
 @mock_sqs
@@ -2146,25 +2151,25 @@ def test_batch_change_message_visibility_on_old_message():
 
     messages = queue.receive_messages(MaxNumberOfMessages=1)
 
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     original_message = messages[0]
 
     time.sleep(2)
 
     messages = queue.receive_messages(MaxNumberOfMessages=1)
-    messages[0].receipt_handle.shouldnt.equal(original_message.receipt_handle)
+    assert messages[0].receipt_handle != original_message.receipt_handle
 
     entries = [
         {
-            "Id": str(uuid.uuid4()),
+            "Id": str(uuid4()),
             "ReceiptHandle": original_message.receipt_handle,
             "VisibilityTimeout": 4,
         }
     ]
 
     resp = queue.change_message_visibility_batch(Entries=entries)
-    resp["Successful"].should.have.length_of(1)
+    assert len(resp["Successful"]) == 1
 
 
 @mock_sqs
@@ -2192,12 +2197,36 @@ def test_permissions():
         QueueUrl=queue_url, AttributeNames=["Policy"]
     )
     policy = json.loads(response["Attributes"]["Policy"])
-    policy["Version"].should.equal("2012-10-17")
-    policy["Id"].should.equal(
+    assert policy["Version"] == "2012-10-17"
+    assert policy["Id"] == (
         f"arn:aws:sqs:us-east-1:123456789012:{q_name}/SQSDefaultPolicy"
     )
-    sorted(policy["Statement"], key=lambda x: x["Sid"]).should.equal(
-        [
+    assert sorted(policy["Statement"], key=lambda x: x["Sid"]) == [
+        {
+            "Sid": "account1",
+            "Effect": "Allow",
+            "Principal": {"AWS": "arn:aws:iam::111111111111:root"},
+            "Action": "SQS:*",
+            "Resource": f"arn:aws:sqs:us-east-1:123456789012:{q_name}",
+        },
+        {
+            "Sid": "account2",
+            "Effect": "Allow",
+            "Principal": {"AWS": "arn:aws:iam::222211111111:root"},
+            "Action": "SQS:SendMessage",
+            "Resource": f"arn:aws:sqs:us-east-1:123456789012:{q_name}",
+        },
+    ]
+
+    client.remove_permission(QueueUrl=queue_url, Label="account2")
+
+    response = client.get_queue_attributes(
+        QueueUrl=queue_url, AttributeNames=["Policy"]
+    )
+    assert json.loads(response["Attributes"]["Policy"]) == {
+        "Version": "2012-10-17",
+        "Id": f"arn:aws:sqs:us-east-1:123456789012:{q_name}/SQSDefaultPolicy",
+        "Statement": [
             {
                 "Sid": "account1",
                 "Effect": "Allow",
@@ -2205,36 +2234,8 @@ def test_permissions():
                 "Action": "SQS:*",
                 "Resource": f"arn:aws:sqs:us-east-1:123456789012:{q_name}",
             },
-            {
-                "Sid": "account2",
-                "Effect": "Allow",
-                "Principal": {"AWS": "arn:aws:iam::222211111111:root"},
-                "Action": "SQS:SendMessage",
-                "Resource": f"arn:aws:sqs:us-east-1:123456789012:{q_name}",
-            },
-        ]
-    )
-
-    client.remove_permission(QueueUrl=queue_url, Label="account2")
-
-    response = client.get_queue_attributes(
-        QueueUrl=queue_url, AttributeNames=["Policy"]
-    )
-    json.loads(response["Attributes"]["Policy"]).should.equal(
-        {
-            "Version": "2012-10-17",
-            "Id": f"arn:aws:sqs:us-east-1:123456789012:{q_name}/SQSDefaultPolicy",
-            "Statement": [
-                {
-                    "Sid": "account1",
-                    "Effect": "Allow",
-                    "Principal": {"AWS": "arn:aws:iam::111111111111:root"},
-                    "Action": "SQS:*",
-                    "Resource": f"arn:aws:sqs:us-east-1:123456789012:{q_name}",
-                },
-            ],
-        }
-    )
+        ],
+    }
 
 
 @mock_sqs
@@ -2287,11 +2288,11 @@ def test_add_permission_errors():
             Actions=["ReceiveMessage", "SendMessage"],
         )
     ex = e.value
-    ex.operation_name.should.equal("AddPermission")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.response["Error"]["Code"].should.contain("InvalidParameterValue")
-    ex.response["Error"]["Message"].should.equal(
-        "Value test for parameter Label is invalid. " "Reason: Already exists."
+    assert ex.operation_name == "AddPermission"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert "InvalidParameterValue" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
+        "Value test for parameter Label is invalid. Reason: Already exists."
     )
 
     with pytest.raises(ClientError) as e:
@@ -2302,10 +2303,10 @@ def test_add_permission_errors():
             Actions=["RemovePermission"],
         )
     ex = e.value
-    ex.operation_name.should.equal("AddPermission")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.response["Error"]["Code"].should.contain("InvalidParameterValue")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "AddPermission"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert "InvalidParameterValue" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
         "Value SQS:RemovePermission for parameter ActionName is invalid. "
         "Reason: Only the queue owner is allowed to invoke this action."
     )
@@ -2318,10 +2319,10 @@ def test_add_permission_errors():
             Actions=[],
         )
     ex = e.value
-    ex.operation_name.should.equal("AddPermission")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.response["Error"]["Code"].should.contain("MissingParameter")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "AddPermission"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert "MissingParameter" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
         "The request must contain the parameter Actions."
     )
 
@@ -2333,10 +2334,10 @@ def test_add_permission_errors():
             Actions=["ReceiveMessage"],
         )
     ex = e.value
-    ex.operation_name.should.equal("AddPermission")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.response["Error"]["Code"].should.contain("InvalidParameterValue")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "AddPermission"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert "InvalidParameterValue" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
         "Value [] for parameter PrincipalId is invalid. Reason: Unable to verify."
     )
 
@@ -2357,10 +2358,10 @@ def test_add_permission_errors():
             ],
         )
     ex = e.value
-    ex.operation_name.should.equal("AddPermission")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(403)
-    ex.response["Error"]["Code"].should.contain("OverLimit")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "AddPermission"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 403
+    assert "OverLimit" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
         "8 Actions were found, maximum allowed is 7."
     )
 
@@ -2374,10 +2375,10 @@ def test_remove_permission_errors():
     with pytest.raises(ClientError) as e:
         client.remove_permission(QueueUrl=queue_url, Label="test")
     ex = e.value
-    ex.operation_name.should.equal("RemovePermission")
-    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.response["Error"]["Code"].should.contain("InvalidParameterValue")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.operation_name == "RemovePermission"
+    assert ex.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+    assert "InvalidParameterValue" in ex.response["Error"]["Code"]
+    assert ex.response["Error"]["Message"] == (
         "Value test for parameter Label is invalid. "
         "Reason: can't find label on existing policy."
     )
@@ -2395,18 +2396,18 @@ def test_tags():
     client.tag_queue(QueueUrl=queue_url, Tags={"test1": "value1", "test2": "value2"})
 
     resp = client.list_queue_tags(QueueUrl=queue_url)
-    resp["Tags"].should.contain("test1")
-    resp["Tags"].should.contain("test2")
+    assert "test1" in resp["Tags"]
+    assert "test2" in resp["Tags"]
 
     client.untag_queue(QueueUrl=queue_url, TagKeys=["test2"])
 
     resp = client.list_queue_tags(QueueUrl=queue_url)
-    resp["Tags"].should.contain("test1")
-    resp["Tags"].should_not.contain("test2")
+    assert "test1" in resp["Tags"]
+    assert "test2" not in resp["Tags"]
 
     # removing a non existing tag should not raise any error
     client.untag_queue(QueueUrl=queue_url, TagKeys=["not-existing-tag"])
-    client.list_queue_tags(QueueUrl=queue_url)["Tags"].should.equal({"test1": "value1"})
+    assert client.list_queue_tags(QueueUrl=queue_url)["Tags"] == {"test1": "value1"}
 
 
 @mock_sqs
@@ -2418,10 +2419,10 @@ def test_list_queue_tags_errors():
     )
     queue_url = response["QueueUrl"]
 
-    client.list_queue_tags.when.called_with(
-        QueueUrl=queue_url + "-not-existing"
-    ).should.throw(
-        ClientError, "The specified queue does not exist for this wsdl version."
+    with pytest.raises(ClientError) as client_error:
+        client.list_queue_tags(QueueUrl=queue_url + "-not-existing")
+    assert client_error.value.response["Error"]["Message"] == (
+        "The specified queue does not exist for this wsdl version."
     )
 
 
@@ -2433,23 +2434,29 @@ def test_tag_queue_errors():
     response = client.create_queue(QueueName=q_name, tags={"tag_key_1": "tag_value_X"})
     queue_url = response["QueueUrl"]
 
-    client.tag_queue.when.called_with(
-        QueueUrl=queue_url + "-not-existing", Tags={"tag_key_1": "tag_value_1"}
-    ).should.throw(
-        ClientError, "The specified queue does not exist for this wsdl version."
+    with pytest.raises(ClientError) as client_error:
+        client.tag_queue(
+            QueueUrl=queue_url + "-not-existing", Tags={"tag_key_1": "tag_value_1"}
+        )
+    assert client_error.value.response["Error"]["Message"] == (
+        "The specified queue does not exist for this wsdl version."
     )
 
-    client.tag_queue.when.called_with(QueueUrl=queue_url, Tags={}).should.throw(
-        ClientError, "The request must contain the parameter Tags."
+    with pytest.raises(ClientError) as client_error:
+        client.tag_queue(QueueUrl=queue_url, Tags={})
+    assert client_error.value.response["Error"]["Message"] == (
+        "The request must contain the parameter Tags."
     )
 
     too_many_tags = {f"tag_key_{i}": f"tag_value_{i}" for i in range(51)}
-    client.tag_queue.when.called_with(
-        QueueUrl=queue_url, Tags=too_many_tags
-    ).should.throw(ClientError, f"Too many tags added for queue {q_name}.")
+    with pytest.raises(ClientError) as client_error:
+        client.tag_queue(QueueUrl=queue_url, Tags=too_many_tags)
+    assert client_error.value.response["Error"]["Message"] == (
+        f"Too many tags added for queue {q_name}."
+    )
 
     # when the request fails, the tags should not be updated
-    client.list_queue_tags(QueueUrl=queue_url)["Tags"].should.equal(
+    assert client.list_queue_tags(QueueUrl=queue_url)["Tags"] == (
         {"tag_key_1": "tag_value_X"}
     )
 
@@ -2463,14 +2470,16 @@ def test_untag_queue_errors():
     )
     queue_url = response["QueueUrl"]
 
-    client.untag_queue.when.called_with(
-        QueueUrl=queue_url + "-not-existing", TagKeys=["tag_key_1"]
-    ).should.throw(
-        ClientError, "The specified queue does not exist for this wsdl version."
+    with pytest.raises(ClientError) as client_error:
+        client.untag_queue(QueueUrl=queue_url + "-not-existing", TagKeys=["tag_key_1"])
+    assert client_error.value.response["Error"]["Message"] == (
+        "The specified queue does not exist for this wsdl version."
     )
 
-    client.untag_queue.when.called_with(QueueUrl=queue_url, TagKeys=[]).should.throw(
-        ClientError, "Tag keys must be between 1 and 128 characters in length."
+    with pytest.raises(ClientError) as client_error:
+        client.untag_queue(QueueUrl=queue_url, TagKeys=[])
+    assert client_error.value.response["Error"]["Message"] == (
+        "Tag keys must be between 1 and 128 characters in length."
     )
 
 
@@ -2696,20 +2705,20 @@ def test_receive_messages_with_message_group_id():
     messages = queue.receive_messages(
         MaxNumberOfMessages=2, AttributeNames=["MessageGroupId"]
     )
-    messages.should.have.length_of(2)
-    messages[0].attributes["MessageGroupId"].should.equal("group")
+    assert len(messages) == 2
+    assert messages[0].attributes["MessageGroupId"] == "group"
 
     # Different client can not 'see' messages from the group until they are processed
     messages_for_client_2 = queue.receive_messages(WaitTimeSeconds=0)
-    messages_for_client_2.should.have.length_of(1)
-    messages_for_client_2[0].body.should.equal("separate-message")
+    assert len(messages_for_client_2) == 1
+    assert messages_for_client_2[0].body == "separate-message"
 
     # message is now processed, next one should be available
     for message in messages:
         message.delete()
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
-    messages[0].body.should.equal("message-3")
+    assert len(messages) == 1
+    assert messages[0].body == "message-3"
 
 
 @mock_sqs
@@ -2724,19 +2733,19 @@ def test_receive_messages_with_message_group_id_on_requeue():
     queue.send_message(MessageBody="message-2", MessageGroupId="group")
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
     message = messages[0]
 
     # received message is not deleted!
 
     messages = queue.receive_messages(WaitTimeSeconds=0)
-    messages.should.have.length_of(0)
+    assert len(messages) == 0
 
     # message is now available again, next one should be available
     message.change_visibility(VisibilityTimeout=0)
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
-    messages[0].message_id.should.equal(message.message_id)
+    assert len(messages) == 1
+    assert messages[0].message_id == message.message_id
 
 
 @mock_sqs
@@ -2755,12 +2764,12 @@ def test_receive_messages_with_message_group_id_on_visibility_timeout():
         queue.send_message(MessageBody="message-2", MessageGroupId="group")
 
         messages = queue.receive_messages()
-        messages.should.have.length_of(1)
+        assert len(messages) == 1
         message = messages[0]
 
         # received message is not processed yet
         messages_for_second_client = queue.receive_messages(WaitTimeSeconds=0)
-        messages_for_second_client.should.have.length_of(0)
+        assert len(messages_for_second_client) == 0
 
         for message in messages:
             message.change_visibility(VisibilityTimeout=10)
@@ -2768,13 +2777,13 @@ def test_receive_messages_with_message_group_id_on_visibility_timeout():
     with freeze_time("2015-01-01 12:00:05"):
         # no timeout yet
         messages = queue.receive_messages(WaitTimeSeconds=0)
-        messages.should.have.length_of(0)
+        assert len(messages) == 0
 
     with freeze_time("2015-01-01 12:00:15"):
         # message is now available again, next one should be available
         messages = queue.receive_messages()
-        messages.should.have.length_of(1)
-        messages[0].message_id.should.equal(message.message_id)
+        assert len(messages) == 1
+        assert messages[0].message_id == message.message_id
 
 
 @mock_sqs
@@ -2801,17 +2810,13 @@ def test_list_queues_limits_to_1000_queues():
         queue = client.create_queue(QueueName=f"{prefix_name}-{i}")
         queue_urls.append(queue["QueueUrl"])
 
-    client.list_queues()["QueueUrls"].should.have.length_of(1000)
-    client.list_queues(QueueNamePrefix=prefix_name)["QueueUrls"].should.have.length_of(
-        1000
-    )
+    assert len(client.list_queues()["QueueUrls"]) == 1000
+    assert len(client.list_queues(QueueNamePrefix=prefix_name)["QueueUrls"]) == 1000
 
     resource = boto3.resource("sqs", region_name="us-east-1")
 
-    list(resource.queues.all()).should.have.length_of(1000)
-    list(resource.queues.filter(QueueNamePrefix=prefix_name)).should.have.length_of(
-        1000
-    )
+    assert len(list(resource.queues.all())) == 1000
+    assert len(list(resource.queues.filter(QueueNamePrefix=prefix_name))) == 1000
 
     # Delete this again, to not hog all the resources
     for url in queue_urls:
@@ -2829,8 +2834,8 @@ def test_send_message_to_fifo_without_message_group_id():
     with pytest.raises(Exception) as e:
         queue.send_message(MessageBody="message-1")
     ex = e.value
-    ex.response["Error"]["Code"].should.equal("MissingParameter")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.response["Error"]["Code"] == "MissingParameter"
+    assert ex.response["Error"]["Message"] == (
         "The request must contain the parameter MessageGroupId."
     )
 
@@ -2846,8 +2851,8 @@ def test_send_messages_to_fifo_without_message_group_id():
     with pytest.raises(Exception) as e:
         queue.send_messages(Entries=[{"Id": "id_1", "MessageBody": "body_1"}])
     ex = e.value
-    ex.response["Error"]["Code"].should.equal("MissingParameter")
-    ex.response["Error"]["Message"].should.equal(
+    assert ex.response["Error"]["Code"] == "MissingParameter"
+    assert ex.response["Error"]["Message"] == (
         "The request must contain the parameter MessageGroupId."
     )
 
@@ -2856,11 +2861,11 @@ def test_send_messages_to_fifo_without_message_group_id():
 def test_maximum_message_size_attribute_default():
     sqs = boto3.resource("sqs", region_name="eu-west-3")
     queue = sqs.create_queue(QueueName=str(uuid4()))
-    int(queue.attributes["MaximumMessageSize"]).should.equal(MAXIMUM_MESSAGE_LENGTH)
+    assert int(queue.attributes["MaximumMessageSize"]) == MAXIMUM_MESSAGE_LENGTH
     with pytest.raises(Exception) as e:
         queue.send_message(MessageBody="a" * (MAXIMUM_MESSAGE_LENGTH + 1))
     ex = e.value
-    ex.response["Error"]["Code"].should.equal("InvalidParameterValue")
+    assert ex.response["Error"]["Code"] == "InvalidParameterValue"
 
 
 @mock_sqs
@@ -2877,7 +2882,7 @@ def test_maximum_message_size_attribute_fails_for_invalid_values():
                 Attributes={"MaximumMessageSize": str(message_size)},
             )
         ex = e.value
-        ex.response["Error"]["Code"].should.equal("InvalidAttributeValue")
+        assert ex.response["Error"]["Code"] == "InvalidAttributeValue"
 
 
 @mock_sqs
@@ -2888,12 +2893,12 @@ def test_send_message_fails_when_message_size_greater_than_max_message_size():
         QueueName=str(uuid4()),
         Attributes={"MaximumMessageSize": str(message_size_limit)},
     )
-    int(queue.attributes["MaximumMessageSize"]).should.equal(message_size_limit)
+    assert int(queue.attributes["MaximumMessageSize"]) == message_size_limit
     with pytest.raises(ClientError) as e:
         queue.send_message(MessageBody="a" * (message_size_limit + 1))
     ex = e.value
-    ex.response["Error"]["Code"].should.equal("InvalidParameterValue")
-    ex.response["Error"]["Message"].should.contain(f"{message_size_limit} bytes")
+    assert ex.response["Error"]["Code"] == "InvalidParameterValue"
+    assert f"{message_size_limit} bytes" in ex.response["Error"]["Message"]
 
 
 @mock_sqs
@@ -2924,7 +2929,7 @@ def test_fifo_queue_deduplication_with_id(
         MessageBody=msg_2, MessageDeduplicationId=dedupid_2, MessageGroupId="2"
     )
     messages = msg_queue.receive_messages(MaxNumberOfMessages=2)
-    messages.should.have.length_of(expected_count)
+    assert len(messages) == expected_count
 
 
 @mock_sqs
@@ -2943,7 +2948,7 @@ def test_fifo_queue_deduplication_withoutid(msg_1, msg_2, expected_count):
     msg_queue.send_message(MessageBody=msg_1, MessageGroupId="1")
     msg_queue.send_message(MessageBody=msg_2, MessageGroupId="2")
     messages = msg_queue.receive_messages(MaxNumberOfMessages=2)
-    messages.should.have.length_of(expected_count)
+    assert len(messages) == expected_count
 
 
 @mock.patch(
@@ -2964,7 +2969,7 @@ def test_fifo_queue_send_duplicate_messages_after_deduplication_time_limit():
     time.sleep(MOCK_DEDUPLICATION_TIME_IN_SECONDS + 5)
     msg_queue.send_message(MessageBody="first", MessageGroupId="2")
     messages = msg_queue.receive_messages(MaxNumberOfMessages=2)
-    messages.should.have.length_of(2)
+    assert len(messages) == 2
 
 
 @mock_sqs
@@ -2987,7 +2992,7 @@ def test_fifo_queue_send_deduplicationid_same_as_sha256_of_old_message():
         MessageBody="second", MessageGroupId="2", MessageDeduplicationId=deduplicationid
     )
     messages = msg_queue.receive_messages(MaxNumberOfMessages=2)
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
 
 @mock_sqs
@@ -3017,23 +3022,23 @@ def test_fifo_send_message_when_same_group_id_is_in_dlq():
 
     msg_queue.send_message(MessageBody="first", MessageGroupId="1")
     messages = msg_queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     time.sleep(1.1)
 
     messages = msg_queue.receive_messages()
-    messages.should.have.length_of(0)
+    assert len(messages) == 0
 
     messages = dlq.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
     msg_queue.send_message(MessageBody="second", MessageGroupId="1")
     messages = msg_queue.receive_messages()
-    messages.should.have.length_of(1)
+    assert len(messages) == 1
 
 
 @mock_sqs
-def test_receive_message_using_name__should_return_name_as_url():
+def test_receive_message_using_name_should_return_name_as_url():
     sqs = boto3.resource("sqs", region_name="us-east-1")
     conn = boto3.client("sqs", region_name="us-east-1")
     name = str(uuid4())[0:6]
@@ -3041,13 +3046,13 @@ def test_receive_message_using_name__should_return_name_as_url():
     working_url = q_response["QueueUrl"]
     # https://queue.amazonaws.com/012341234/test-queue
     # http://localhost:5000/012341234/test-queue in ServerMode
-    working_url.should.match(f"/{ACCOUNT_ID}/{name}")
+    assert working_url.endswith(f"/{ACCOUNT_ID}/{name}")
 
     queue = sqs.Queue(name)
     queue.send_message(MessageBody="this is a test message")
 
     resp = queue.receive_messages()
-    resp[0].queue_url.should.equal(name)
+    assert resp[0].queue_url == name
 
 
 @mock_sqs
@@ -3063,7 +3068,10 @@ def test_message_attributes_contains_trace_header():
         MessageBody=body_one,
         MessageSystemAttributes={
             "AWSTraceHeader": {
-                "StringValue": "Root=1-3152b799-8954dae64eda91bc9a23a7e8;Parent=7fa8c0f79203be72;Sampled=1",
+                "StringValue": (
+                    "Root=1-3152b799-8954dae64eda91bc9a23a7e8;"
+                    "Parent=7fa8c0f79203be72;Sampled=1"
+                ),
                 "DataType": "String",
             }
         },
@@ -3123,8 +3131,8 @@ def test_message_has_windows_return():
     queue.send_message(MessageBody=message)
 
     messages = queue.receive_messages()
-    messages.should.have.length_of(1)
-    messages[0].body.should.match(message)
+    assert len(messages) == 1
+    assert re.match(message, messages[0].body)
 
 
 @mock_sqs
@@ -3164,9 +3172,9 @@ def test_message_delay_is_more_than_15_minutes():
         ],
     )
 
-    sorted([entry["Id"] for entry in response["Successful"]]).should.equal(["id_1"])
+    assert sorted([entry["Id"] for entry in response["Successful"]]) == ["id_1"]
 
-    sorted([entry["Id"] for entry in response["Failed"]]).should.equal(["id_2"])
+    assert sorted([entry["Id"] for entry in response["Failed"]]) == ["id_2"]
 
     # print(response)
 
@@ -3179,15 +3187,15 @@ def test_message_delay_is_more_than_15_minutes():
         AttributeNames=["MessageDeduplicationId", "MessageGroupId"],
     )
 
-    response["Messages"].should.have.length_of(1)
-    response["Messages"][0]["Body"].should.equal("body_1")
-    response["Messages"][0]["MessageAttributes"].should.equal(
+    assert len(response["Messages"]) == 1
+    assert response["Messages"][0]["Body"] == "body_1"
+    assert response["Messages"][0]["MessageAttributes"] == (
         {"attribute_name_1": {"StringValue": "attribute_value_1", "DataType": "String"}}
     )
-    response["Messages"][0]["Attributes"]["MessageGroupId"].should.equal(
-        "message_group_id_1"
+    assert (
+        response["Messages"][0]["Attributes"]["MessageGroupId"] == "message_group_id_1"
     )
-    response["Messages"][0]["Attributes"]["MessageDeduplicationId"].should.equal(
+    assert response["Messages"][0]["Attributes"]["MessageDeduplicationId"] == (
         "message_deduplication_id_1"
     )
 
@@ -3241,8 +3249,8 @@ def test_fifo_dedupe_error_no_message_group_id():
             MessageDeduplicationId="1",
         )
 
-    exc.value.response["Error"]["Code"].should.equal("MissingParameter")
-    exc.value.response["Error"]["Message"].should.equal(
+    assert exc.value.response["Error"]["Code"] == "MissingParameter"
+    assert exc.value.response["Error"]["Message"] == (
         "The request must contain the parameter MessageGroupId."
     )
 
@@ -3260,7 +3268,8 @@ def test_fifo_dedupe_error_no_message_dedupe_id():
             MessageGroupId="1",
         )
 
-    exc.value.response["Error"]["Code"].should.equal("InvalidParameterValue")
-    exc.value.response["Error"]["Message"].should.equal(
-        "The queue should either have ContentBasedDeduplication enabled or MessageDeduplicationId provided explicitly"
+    assert exc.value.response["Error"]["Code"] == "InvalidParameterValue"
+    assert exc.value.response["Error"]["Message"] == (
+        "The queue should either have ContentBasedDeduplication enabled "
+        "or MessageDeduplicationId provided explicitly"
     )
diff --git a/tests/test_sqs/test_sqs_cloudformation.py b/tests/test_sqs/test_sqs_cloudformation.py
index 983c896d0..5e35cd8ae 100644
--- a/tests/test_sqs/test_sqs_cloudformation.py
+++ b/tests/test_sqs/test_sqs_cloudformation.py
@@ -1,13 +1,13 @@
-import boto3
-
 import json
-import sure  # noqa # pylint: disable=unused-import
-from moto import mock_sqs, mock_cloudformation
-from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
 from string import Template
 from random import randint
 from uuid import uuid4
 
+import boto3
+
+from moto import mock_sqs, mock_cloudformation
+from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+
 simple_queue = Template(
     """{
     "AWSTemplateFormatVersion": "2010-09-09",
@@ -56,13 +56,13 @@ def test_describe_stack_subresources():
     cf.create_stack(StackName=stack_name, TemplateBody=template_body)
 
     queue_urls = client.list_queues(QueueNamePrefix=q_name)["QueueUrls"]
-    assert any([f"{ACCOUNT_ID}/{q_name}" in url for url in queue_urls])
+    assert any({f"{ACCOUNT_ID}/{q_name}" in url for url in queue_urls})
 
     stack = res.Stack(stack_name)
     for s in stack.resource_summaries.all():
-        s.resource_type.should.equal("AWS::SQS::Queue")
-        s.logical_id.should.equal("QueueGroup")
-        s.physical_resource_id.should.contain(f"/{q_name}")
+        assert s.resource_type == "AWS::SQS::Queue"
+        assert s.logical_id == "QueueGroup"
+        assert f"/{q_name}" in s.physical_resource_id
 
 
 @mock_sqs
@@ -77,14 +77,14 @@ def test_list_stack_resources():
     cf.create_stack(StackName=stack_name, TemplateBody=template_body)
 
     queue_urls = client.list_queues(QueueNamePrefix=q_name)["QueueUrls"]
-    assert any([f"{ACCOUNT_ID}/{q_name}" in url for url in queue_urls])
+    assert any({f"{ACCOUNT_ID}/{q_name}" in url for url in queue_urls})
 
     queue = cf.list_stack_resources(StackName=stack_name)["StackResourceSummaries"][0]
 
-    queue.should.have.key("ResourceType").equal("AWS::SQS::Queue")
-    queue.should.have.key("LogicalResourceId").should.equal("QueueGroup")
+    assert queue["ResourceType"] == "AWS::SQS::Queue"
+    assert queue["LogicalResourceId"] == "QueueGroup"
     expected_url = f"https://sqs.us-east-1.amazonaws.com/{ACCOUNT_ID}/{q_name}"
-    queue.should.have.key("PhysicalResourceId").should.equal(expected_url)
+    assert queue["PhysicalResourceId"] == expected_url
 
 
 @mock_sqs
@@ -103,7 +103,7 @@ def test_create_from_cloudformation_json_with_tags():
     queue_url = [url for url in all_urls if url.endswith(q_name)][0]
 
     queue_tags = client.list_queue_tags(QueueUrl=queue_url)["Tags"]
-    queue_tags.should.equal({"keyname1": "value1", "keyname2": "value2"})
+    assert queue_tags == {"keyname1": "value1", "keyname2": "value2"}
 
 
 @mock_cloudformation
@@ -127,11 +127,11 @@ def test_update_stack():
 
     client = boto3.client("sqs", region_name="us-west-1")
     queues = client.list_queues(QueueNamePrefix=q_name)["QueueUrls"]
-    queues.should.have.length_of(1)
+    assert len(queues) == 1
     attrs = client.get_queue_attributes(QueueUrl=queues[0], AttributeNames=["All"])[
         "Attributes"
     ]
-    attrs["VisibilityTimeout"].should.equal("60")
+    assert attrs["VisibilityTimeout"] == "60"
 
     # when updating
     sqs_template["Resources"]["QueueGroup"]["Properties"]["VisibilityTimeout"] = 100
@@ -140,11 +140,11 @@ def test_update_stack():
 
     # then the attribute should be updated
     queues = client.list_queues(QueueNamePrefix=q_name)["QueueUrls"]
-    queues.should.have.length_of(1)
+    assert len(queues) == 1
     attrs = client.get_queue_attributes(QueueUrl=queues[0], AttributeNames=["All"])[
         "Attributes"
     ]
-    attrs["VisibilityTimeout"].should.equal("100")
+    assert attrs["VisibilityTimeout"] == "100"
 
 
 @mock_cloudformation
@@ -167,14 +167,14 @@ def test_update_stack_and_remove_resource():
     cf.create_stack(StackName=stack_name, TemplateBody=sqs_template_json)
 
     client = boto3.client("sqs", region_name="us-west-1")
-    client.list_queues(QueueNamePrefix=q_name)["QueueUrls"].should.have.length_of(1)
+    assert len(client.list_queues(QueueNamePrefix=q_name)["QueueUrls"]) == 1
 
     sqs_template["Resources"].pop("QueueGroup")
     sqs_template_json = json.dumps(sqs_template)
     cf.update_stack(StackName=stack_name, TemplateBody=sqs_template_json)
 
     # No queues exist, so the key is not passed through
-    client.list_queues(QueueNamePrefix=q_name).shouldnt.have.key("QueueUrls")
+    assert "QueueUrls" not in client.list_queues(QueueNamePrefix=q_name)
 
 
 @mock_cloudformation
@@ -190,7 +190,7 @@ def test_update_stack_and_add_resource():
     cf.create_stack(StackName=stack_name, TemplateBody=sqs_template_json)
 
     client = boto3.client("sqs", region_name="us-west-1")
-    client.list_queues(QueueNamePrefix=q_name).shouldnt.have.key("QueueUrls")
+    assert "QueueUrls" not in client.list_queues(QueueNamePrefix=q_name)
 
     sqs_template = {
         "AWSTemplateFormatVersion": "2010-09-09",
@@ -204,7 +204,7 @@ def test_update_stack_and_add_resource():
     sqs_template_json = json.dumps(sqs_template)
     cf.update_stack(StackName=stack_name, TemplateBody=sqs_template_json)
 
-    client.list_queues(QueueNamePrefix=q_name)["QueueUrls"].should.have.length_of(1)
+    assert len(client.list_queues(QueueNamePrefix=q_name)["QueueUrls"]) == 1
 
 
 @mock_sqs
@@ -223,4 +223,4 @@ def test_create_queue_passing_integer_as_name():
     cf.create_stack(StackName=stack_name, TemplateBody=template_body)
 
     queue_urls = client.list_queues(QueueNamePrefix=q_name[0:6])["QueueUrls"]
-    queue_urls.should.have.length_of(1)
+    assert len(queue_urls) == 1
diff --git a/tests/test_sqs/test_sqs_integration.py b/tests/test_sqs/test_sqs_integration.py
index 15e2c1698..316c6eed2 100644
--- a/tests/test_sqs/test_sqs_integration.py
+++ b/tests/test_sqs/test_sqs_integration.py
@@ -1,8 +1,9 @@
-import boto3
 import json
 import time
 import uuid
 
+import boto3
+
 from moto import mock_lambda, mock_sqs, mock_logs
 from tests.markers import requires_docker
 from tests.test_awslambda.test_lambda import get_test_zip_file1, get_role_name
@@ -132,8 +133,8 @@ def test_invoke_function_from_sqs_fifo_queue():
             try:
                 body = json.loads(event.get("message"))
                 atts = body["Records"][0]["attributes"]
-                atts.should.have.key("MessageGroupId").equals("mg1")
-                atts.should.have.key("MessageDeduplicationId")
+                assert atts["MessageGroupId"] == "mg1"
+                assert "MessageDeduplicationId" in atts
                 return
             except:  # noqa: E722 Do not use bare except
                 pass
diff --git a/tests/test_sqs/test_sqs_multiaccount.py b/tests/test_sqs/test_sqs_multiaccount.py
index 4ae03eee6..e569bf769 100644
--- a/tests/test_sqs/test_sqs_multiaccount.py
+++ b/tests/test_sqs/test_sqs_multiaccount.py
@@ -1,7 +1,9 @@
 import unittest
+from uuid import uuid4
+
 import boto3
+
 from moto import mock_sts, mock_sqs
-from uuid import uuid4
 
 
 class TestStsAssumeRole(unittest.TestCase):
@@ -14,7 +16,7 @@ class TestStsAssumeRole(unittest.TestCase):
 
         # verify function exists
         all_urls = sqs.list_queues()["QueueUrls"]
-        all_urls.should.contain(queue_url)
+        assert queue_url in all_urls
 
         # assume role to another aws account
         account_b = "111111111111"
@@ -33,4 +35,4 @@ class TestStsAssumeRole(unittest.TestCase):
         )
 
         # client2 belongs to another account, where there are no queues
-        client2.list_queues().shouldnt.have.key("QueueUrls")
+        assert "QueueUrls" not in client2.list_queues()