diff options
| author | 2019-11-13 21:37:19 +0100 | |
|---|---|---|
| committer | 2019-11-13 21:37:19 +0100 | |
| commit | 359b0079e3e11f8a80fa6c4df59ff1abde8e8943 (patch) | |
| tree | 148c57ef8839bce47d3334a47b5a5032e59fc153 /tests | |
| parent | Adjust incorrect type hint (diff) | |
| parent | Enhancements for `tests.helpers` and our test suite (#660) (diff) | |
Merge branch 'master' into unittest-mentions
Testing methodology was adjusted in upstream repo. Merging the relevant changes.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/bot/cogs/test_information.py | 58 | ||||
| -rw-r--r-- | tests/bot/cogs/test_token_remover.py | 2 | ||||
| -rw-r--r-- | tests/bot/test_api.py | 4 | ||||
| -rw-r--r-- | tests/bot/utils/test_checks.py | 6 | ||||
| -rw-r--r-- | tests/helpers.py | 116 | ||||
| -rw-r--r-- | tests/test_helpers.py | 105 | 
6 files changed, 134 insertions, 157 deletions
| diff --git a/tests/bot/cogs/test_information.py b/tests/bot/cogs/test_information.py index 5c34541d8..4496a2ae0 100644 --- a/tests/bot/cogs/test_information.py +++ b/tests/bot/cogs/test_information.py @@ -19,7 +19,7 @@ class InformationCogTests(unittest.TestCase):      @classmethod      def setUpClass(cls): -        cls.moderator_role = helpers.MockRole(name="Moderator", role_id=constants.Roles.moderator) +        cls.moderator_role = helpers.MockRole(name="Moderator", id=constants.Roles.moderator)      def setUp(self):          """Sets up fresh objects for each test.""" @@ -54,7 +54,7 @@ class InformationCogTests(unittest.TestCase):          """Tests the `role info` command."""          dummy_role = helpers.MockRole(              name="Dummy", -            role_id=112233445566778899, +            id=112233445566778899,              colour=discord.Colour.blurple(),              position=10,              members=[self.ctx.author], @@ -63,7 +63,7 @@ class InformationCogTests(unittest.TestCase):          admin_role = helpers.MockRole(              name="Admins", -            role_id=998877665544332211, +            id=998877665544332211,              colour=discord.Colour.red(),              position=3,              members=[self.ctx.author], @@ -176,7 +176,7 @@ class UserInfractionHelperMethodTests(unittest.TestCase):          self.bot = helpers.MockBot()          self.bot.api_client.get = helpers.AsyncMock()          self.cog = information.Information(self.bot) -        self.member = helpers.MockMember(user_id=1234) +        self.member = helpers.MockMember(id=1234)      def test_user_command_helper_method_get_requests(self):          """The helper methods should form the correct get requests.""" @@ -351,7 +351,7 @@ class UserEmbedTests(unittest.TestCase):      @unittest.mock.patch(f"{COG_PATH}.basic_user_infraction_counts", new=helpers.AsyncMock(return_value=""))      def test_create_user_embed_uses_string_representation_of_user_in_title_if_nick_is_not_available(self):          """The embed should use the string representation of the user if they don't have a nick.""" -        ctx = helpers.MockContext(channel=helpers.MockTextChannel(channel_id=1)) +        ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=1))          user = helpers.MockMember()          user.nick = None          user.__str__ = unittest.mock.Mock(return_value="Mr. Hemlock") @@ -363,7 +363,7 @@ class UserEmbedTests(unittest.TestCase):      @unittest.mock.patch(f"{COG_PATH}.basic_user_infraction_counts", new=helpers.AsyncMock(return_value=""))      def test_create_user_embed_uses_nick_in_title_if_available(self):          """The embed should use the nick if it's available.""" -        ctx = helpers.MockContext(channel=helpers.MockTextChannel(channel_id=1)) +        ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=1))          user = helpers.MockMember()          user.nick = "Cat lover"          user.__str__ = unittest.mock.Mock(return_value="Mr. Hemlock") @@ -375,8 +375,8 @@ class UserEmbedTests(unittest.TestCase):      @unittest.mock.patch(f"{COG_PATH}.basic_user_infraction_counts", new=helpers.AsyncMock(return_value=""))      def test_create_user_embed_ignores_everyone_role(self):          """Created `!user` embeds should not contain mention of the @everyone-role.""" -        ctx = helpers.MockContext(channel=helpers.MockTextChannel(channel_id=1)) -        admins_role = helpers.MockRole('Admins') +        ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=1)) +        admins_role = helpers.MockRole(name='Admins')          admins_role.colour = 100          # A `MockMember` has the @Everyone role by default; we add the Admins to that. @@ -391,15 +391,15 @@ class UserEmbedTests(unittest.TestCase):      @unittest.mock.patch(f"{COG_PATH}.user_nomination_counts", new_callable=helpers.AsyncMock)      def test_create_user_embed_expanded_information_in_moderation_channels(self, nomination_counts, infraction_counts):          """The embed should contain expanded infractions and nomination info in mod channels.""" -        ctx = helpers.MockContext(channel=helpers.MockTextChannel(channel_id=50)) +        ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=50)) -        moderators_role = helpers.MockRole('Moderators') +        moderators_role = helpers.MockRole(name='Moderators')          moderators_role.colour = 100          infraction_counts.return_value = "expanded infractions info"          nomination_counts.return_value = "nomination info" -        user = helpers.MockMember(user_id=314, roles=[moderators_role], top_role=moderators_role) +        user = helpers.MockMember(id=314, roles=[moderators_role], top_role=moderators_role)          embed = asyncio.run(self.cog.create_user_embed(ctx, user))          infraction_counts.assert_called_once_with(user) @@ -426,14 +426,14 @@ class UserEmbedTests(unittest.TestCase):      @unittest.mock.patch(f"{COG_PATH}.basic_user_infraction_counts", new_callable=helpers.AsyncMock)      def test_create_user_embed_basic_information_outside_of_moderation_channels(self, infraction_counts):          """The embed should contain only basic infraction data outside of mod channels.""" -        ctx = helpers.MockContext(channel=helpers.MockTextChannel(channel_id=100)) +        ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=100)) -        moderators_role = helpers.MockRole('Moderators') +        moderators_role = helpers.MockRole(name='Moderators')          moderators_role.colour = 100          infraction_counts.return_value = "basic infractions info" -        user = helpers.MockMember(user_id=314, roles=[moderators_role], top_role=moderators_role) +        user = helpers.MockMember(id=314, roles=[moderators_role], top_role=moderators_role)          embed = asyncio.run(self.cog.create_user_embed(ctx, user))          infraction_counts.assert_called_once_with(user) @@ -459,10 +459,10 @@ class UserEmbedTests(unittest.TestCase):          """The embed should be created with the colour of the top role, if a top role is available."""          ctx = helpers.MockContext() -        moderators_role = helpers.MockRole('Moderators') +        moderators_role = helpers.MockRole(name='Moderators')          moderators_role.colour = 100 -        user = helpers.MockMember(user_id=314, roles=[moderators_role], top_role=moderators_role) +        user = helpers.MockMember(id=314, roles=[moderators_role], top_role=moderators_role)          embed = asyncio.run(self.cog.create_user_embed(ctx, user))          self.assertEqual(embed.colour, discord.Colour(moderators_role.colour)) @@ -472,7 +472,7 @@ class UserEmbedTests(unittest.TestCase):          """The embed should be created with a blurple colour if the user has no assigned roles."""          ctx = helpers.MockContext() -        user = helpers.MockMember(user_id=217) +        user = helpers.MockMember(id=217)          embed = asyncio.run(self.cog.create_user_embed(ctx, user))          self.assertEqual(embed.colour, discord.Colour.blurple()) @@ -482,7 +482,7 @@ class UserEmbedTests(unittest.TestCase):          """The embed thumbnail should be set to the user's avatar in `png` format."""          ctx = helpers.MockContext() -        user = helpers.MockMember(user_id=217) +        user = helpers.MockMember(id=217)          user.avatar_url_as.return_value = "avatar url"          embed = asyncio.run(self.cog.create_user_embed(ctx, user)) @@ -499,13 +499,13 @@ class UserCommandTests(unittest.TestCase):          self.bot = helpers.MockBot()          self.cog = information.Information(self.bot) -        self.moderator_role = helpers.MockRole("Moderators", role_id=2, position=10) -        self.flautist_role = helpers.MockRole("Flautists", role_id=3, position=2) -        self.bassist_role = helpers.MockRole("Bassists", role_id=4, position=3) +        self.moderator_role = helpers.MockRole(name="Moderators", id=2, position=10) +        self.flautist_role = helpers.MockRole(name="Flautists", id=3, position=2) +        self.bassist_role = helpers.MockRole(name="Bassists", id=4, position=3) -        self.author = helpers.MockMember(user_id=1, name="syntaxaire") -        self.moderator = helpers.MockMember(user_id=2, name="riffautae", roles=[self.moderator_role]) -        self.target = helpers.MockMember(user_id=3, name="__fluzz__") +        self.author = helpers.MockMember(id=1, name="syntaxaire") +        self.moderator = helpers.MockMember(id=2, name="riffautae", roles=[self.moderator_role]) +        self.target = helpers.MockMember(id=3, name="__fluzz__")      def test_regular_member_cannot_target_another_member(self, constants):          """A regular user should not be able to use `!user` targeting another user.""" @@ -523,7 +523,7 @@ class UserCommandTests(unittest.TestCase):          constants.STAFF_ROLES = [self.moderator_role.id]          constants.Channels.bot = 50 -        ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(channel_id=100)) +        ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(id=100))          msg = "Sorry, but you may only use this command within <#50>."          with self.assertRaises(InChannelCheckFailure, msg=msg): @@ -535,7 +535,7 @@ class UserCommandTests(unittest.TestCase):          constants.STAFF_ROLES = [self.moderator_role.id]          constants.Channels.bot = 50 -        ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(channel_id=50)) +        ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(id=50))          asyncio.run(self.cog.user_info.callback(self.cog, ctx)) @@ -548,7 +548,7 @@ class UserCommandTests(unittest.TestCase):          constants.STAFF_ROLES = [self.moderator_role.id]          constants.Channels.bot = 50 -        ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(channel_id=50)) +        ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(id=50))          asyncio.run(self.cog.user_info.callback(self.cog, ctx, self.author)) @@ -561,7 +561,7 @@ class UserCommandTests(unittest.TestCase):          constants.STAFF_ROLES = [self.moderator_role.id]          constants.Channels.bot = 50 -        ctx = helpers.MockContext(author=self.moderator, channel=helpers.MockTextChannel(channel_id=200)) +        ctx = helpers.MockContext(author=self.moderator, channel=helpers.MockTextChannel(id=200))          asyncio.run(self.cog.user_info.callback(self.cog, ctx)) @@ -574,7 +574,7 @@ class UserCommandTests(unittest.TestCase):          constants.MODERATION_ROLES = [self.moderator_role.id]          constants.STAFF_ROLES = [self.moderator_role.id] -        ctx = helpers.MockContext(author=self.moderator, channel=helpers.MockTextChannel(channel_id=50)) +        ctx = helpers.MockContext(author=self.moderator, channel=helpers.MockTextChannel(id=50))          asyncio.run(self.cog.user_info.callback(self.cog, ctx, self.target)) diff --git a/tests/bot/cogs/test_token_remover.py b/tests/bot/cogs/test_token_remover.py index dfb1bafc9..3276cf5a5 100644 --- a/tests/bot/cogs/test_token_remover.py +++ b/tests/bot/cogs/test_token_remover.py @@ -24,7 +24,7 @@ class TokenRemoverTests(unittest.TestCase):          self.bot.get_cog.return_value.send_log_message = AsyncMock()          self.cog = TokenRemover(bot=self.bot) -        self.msg = MockMessage(message_id=555, content='') +        self.msg = MockMessage(id=555, content='')          self.msg.author.__str__ = MagicMock()          self.msg.author.__str__.return_value = 'lemon'          self.msg.author.bot = False diff --git a/tests/bot/test_api.py b/tests/bot/test_api.py index e0ede0eb1..5a88adc5c 100644 --- a/tests/bot/test_api.py +++ b/tests/bot/test_api.py @@ -121,7 +121,9 @@ class LoggingHandlerTests(LoggingTestCase):      def test_schedule_queued_tasks_for_nonempty_queue(self):          """`APILoggingHandler` should schedule logs when the queue is not empty.""" -        with self.assertLogs(level=logging.DEBUG) as logs, patch('asyncio.create_task') as create_task: +        log = logging.getLogger("bot.api") + +        with self.assertLogs(logger=log, level=logging.DEBUG) as logs, patch('asyncio.create_task') as create_task:              self.log_handler.queue = [555]              self.log_handler.schedule_queued_tasks()              self.assertListEqual(self.log_handler.queue, []) diff --git a/tests/bot/utils/test_checks.py b/tests/bot/utils/test_checks.py index 19b758336..9610771e5 100644 --- a/tests/bot/utils/test_checks.py +++ b/tests/bot/utils/test_checks.py @@ -22,7 +22,7 @@ class ChecksTests(unittest.TestCase):      def test_with_role_check_with_guild_and_required_role(self):          """`with_role_check` returns `True` if `Context.author` has the required role.""" -        self.ctx.author.roles.append(MockRole(role_id=10)) +        self.ctx.author.roles.append(MockRole(id=10))          self.assertTrue(checks.with_role_check(self.ctx, 10))      def test_without_role_check_without_guild(self): @@ -33,13 +33,13 @@ class ChecksTests(unittest.TestCase):      def test_without_role_check_returns_false_with_unwanted_role(self):          """`without_role_check` returns `False` if `Context.author` has unwanted role."""          role_id = 42 -        self.ctx.author.roles.append(MockRole(role_id=role_id)) +        self.ctx.author.roles.append(MockRole(id=role_id))          self.assertFalse(checks.without_role_check(self.ctx, role_id))      def test_without_role_check_returns_true_without_unwanted_role(self):          """`without_role_check` returns `True` if `Context.author` does not have unwanted role."""          role_id = 42 -        self.ctx.author.roles.append(MockRole(role_id=role_id)) +        self.ctx.author.roles.append(MockRole(id=role_id))          self.assertTrue(checks.without_role_check(self.ctx, role_id + 10))      def test_in_channel_check_for_correct_channel(self): diff --git a/tests/helpers.py b/tests/helpers.py index 8496ba031..8a14aeef4 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,8 +1,11 @@  from __future__ import annotations  import asyncio +import collections  import functools  import inspect +import itertools +import logging  import unittest.mock  from typing import Any, Iterable, Optional @@ -10,6 +13,16 @@ import discord  from discord.ext.commands import Bot, Context +for logger in logging.Logger.manager.loggerDict.values(): +    # Set all loggers to CRITICAL by default to prevent screen clutter during testing + +    if not isinstance(logger, logging.Logger): +        # There might be some logging.PlaceHolder objects in there +        continue + +    logger.setLevel(logging.CRITICAL) + +  def async_test(wrapped):      """      Run a test case via asyncio. @@ -61,11 +74,16 @@ class CustomMockMixin:      """      child_mock_type = unittest.mock.MagicMock +    discord_id = itertools.count(0) -    def __init__(self, spec: Any = None, **kwargs): -        super().__init__(spec=spec, **kwargs) -        if spec: -            self._extract_coroutine_methods_from_spec_instance(spec) +    def __init__(self, spec_set: Any = None, **kwargs): +        name = kwargs.pop('name', None)  # `name` has special meaning for Mock classes, so we need to set it manually. +        super().__init__(spec_set=spec_set, **kwargs) + +        if name: +            self.name = name +        if spec_set: +            self._extract_coroutine_methods_from_spec_instance(spec_set)      def _get_child_mock(self, **kw):          """ @@ -155,25 +173,14 @@ class MockGuild(CustomMockMixin, unittest.mock.Mock, HashableMixin):      For more info, see the `Mocking` section in `tests/README.md`.      """ -    def __init__( -        self, -        guild_id: int = 1, -        roles: Optional[Iterable[MockRole]] = None, -        members: Optional[Iterable[MockMember]] = None, -        **kwargs, -    ) -> None: -        super().__init__(spec=guild_instance, **kwargs) - -        self.id = guild_id - -        self.roles = [MockRole("@everyone", 1)] +    def __init__(self, roles: Optional[Iterable[MockRole]] = None, **kwargs) -> None: +        default_kwargs = {'id': next(self.discord_id), 'members': []} +        super().__init__(spec_set=guild_instance, **collections.ChainMap(kwargs, default_kwargs)) + +        self.roles = [MockRole(name="@everyone", position=1, id=0)]          if roles:              self.roles.extend(roles) -        self.members = [] -        if members: -            self.members.extend(members) -  # Create a Role instance to get a realistic Mock of `discord.Role`  role_data = {'name': 'role', 'id': 1} @@ -187,13 +194,12 @@ class MockRole(CustomMockMixin, unittest.mock.Mock, ColourMixin, HashableMixin):      Instances of this class will follow the specifications of `discord.Role` instances. For more      information, see the `MockGuild` docstring.      """ -    def __init__(self, name: str = "role", role_id: int = 1, position: int = 1, **kwargs) -> None: -        super().__init__(spec=role_instance, **kwargs) +    def __init__(self, **kwargs) -> None: +        default_kwargs = {'id': next(self.discord_id), 'name': 'role', 'position': 1} +        super().__init__(spec_set=role_instance, **collections.ChainMap(kwargs, default_kwargs)) -        self.name = name -        self.id = role_id -        self.position = position -        self.mention = f'&{self.name}' +        if 'mention' not in kwargs: +            self.mention = f'&{self.name}'      def __lt__(self, other):          """Simplified position-based comparisons similar to those of `discord.Role`.""" @@ -213,27 +219,22 @@ class MockMember(CustomMockMixin, unittest.mock.Mock, ColourMixin, HashableMixin      Instances of this class will follow the specifications of `discord.Member` instances. For more      information, see the `MockGuild` docstring.      """ -    def __init__( -        self, -        name: str = "member", -        user_id: int = 1, -        roles: Optional[Iterable[MockRole]] = None, -        **kwargs, -    ) -> None: -        super().__init__(spec=member_instance, **kwargs) - -        self.name = name -        self.id = user_id - -        self.roles = [MockRole("@everyone", 1)] +    def __init__(self, roles: Optional[Iterable[MockRole]] = None, **kwargs) -> None: +        default_kwargs = {'name': 'member', 'id': next(self.discord_id)} +        super().__init__(spec_set=member_instance, **collections.ChainMap(kwargs, default_kwargs)) + +        self.roles = [MockRole(name="@everyone", position=1, id=0)]          if roles:              self.roles.extend(roles) -        self.mention = f"@{self.name}" +        if 'mention' not in kwargs: +            self.mention = f"@{self.name}"  # Create a Bot instance to get a realistic MagicMock of `discord.ext.commands.Bot`  bot_instance = Bot(command_prefix=unittest.mock.MagicMock()) +bot_instance.http_session = None +bot_instance.api_client = None  class MockBot(CustomMockMixin, unittest.mock.MagicMock): @@ -244,17 +245,18 @@ class MockBot(CustomMockMixin, unittest.mock.MagicMock):      For more information, see the `MockGuild` docstring.      """      def __init__(self, **kwargs) -> None: -        super().__init__(spec=bot_instance, **kwargs) - -        # Our custom attributes and methods -        self.http_session = unittest.mock.MagicMock() -        self.api_client = unittest.mock.MagicMock() +        super().__init__(spec_set=bot_instance, **kwargs)          # self.wait_for is *not* a coroutine function, but returns a coroutine nonetheless and          # and should therefore be awaited. (The documentation calls it a coroutine as well, which          # is technically incorrect, since it's a regular def.)          self.wait_for = AsyncMock() +        # Since calling `create_task` on our MockBot does not actually schedule the coroutine object +        # as a task in the asyncio loop, this `side_effect` calls `close()` on the coroutine object +        # to prevent "has not been awaited"-warnings. +        self.loop.create_task.side_effect = lambda coroutine: coroutine.close() +  # Create a TextChannel instance to get a realistic MagicMock of `discord.TextChannel`  channel_data = { @@ -280,11 +282,11 @@ class MockTextChannel(CustomMockMixin, unittest.mock.Mock, HashableMixin):      more information, see the `MockGuild` docstring.      """      def __init__(self, name: str = 'channel', channel_id: int = 1, **kwargs) -> None: -        super().__init__(spec=channel_instance, **kwargs) -        self.id = channel_id -        self.name = name -        self.guild = kwargs.get('guild', MockGuild()) -        self.mention = f"#{self.name}" +        default_kwargs = {'id': next(self.discord_id), 'name': 'channel', 'guild': MockGuild()} +        super().__init__(spec_set=channel_instance, **collections.ChainMap(kwargs, default_kwargs)) + +        if 'mention' not in kwargs: +            self.mention = f"#{self.name}"  # Create a Message instance to get a realistic MagicMock of `discord.Message` @@ -321,12 +323,11 @@ class MockContext(CustomMockMixin, unittest.mock.MagicMock):      instances. For more information, see the `MockGuild` docstring.      """      def __init__(self, **kwargs) -> None: -        super().__init__(spec=context_instance, **kwargs) +        super().__init__(spec_set=context_instance, **kwargs)          self.bot = kwargs.get('bot', MockBot())          self.guild = kwargs.get('guild', MockGuild())          self.author = kwargs.get('author', MockMember())          self.channel = kwargs.get('channel', MockTextChannel()) -        self.command = kwargs.get('command', unittest.mock.MagicMock())  class MockMessage(CustomMockMixin, unittest.mock.MagicMock): @@ -337,7 +338,7 @@ class MockMessage(CustomMockMixin, unittest.mock.MagicMock):      information, see the `MockGuild` docstring.      """      def __init__(self, **kwargs) -> None: -        super().__init__(spec=message_instance, **kwargs) +        super().__init__(spec_set=message_instance, **kwargs)          self.author = kwargs.get('author', MockMember())          self.channel = kwargs.get('channel', MockTextChannel()) @@ -354,12 +355,9 @@ class MockEmoji(CustomMockMixin, unittest.mock.MagicMock):      information, see the `MockGuild` docstring.      """      def __init__(self, **kwargs) -> None: -        super().__init__(spec=emoji_instance, **kwargs) +        super().__init__(spec_set=emoji_instance, **kwargs)          self.guild = kwargs.get('guild', MockGuild()) -        # Get all coroutine functions and set them as AsyncMock attributes -        self._extract_coroutine_methods_from_spec_instance(emoji_instance) -  partial_emoji_instance = discord.PartialEmoji(animated=False, name='guido') @@ -372,7 +370,7 @@ class MockPartialEmoji(CustomMockMixin, unittest.mock.MagicMock):      more information, see the `MockGuild` docstring.      """      def __init__(self, **kwargs) -> None: -        super().__init__(spec=partial_emoji_instance, **kwargs) +        super().__init__(spec_set=partial_emoji_instance, **kwargs)  reaction_instance = discord.Reaction(message=MockMessage(), data={'me': True}, emoji=MockEmoji()) @@ -386,6 +384,6 @@ class MockReaction(CustomMockMixin, unittest.mock.MagicMock):      more information, see the `MockGuild` docstring.      """      def __init__(self, **kwargs) -> None: -        super().__init__(spec=reaction_instance, **kwargs) +        super().__init__(spec_set=reaction_instance, **kwargs)          self.emoji = kwargs.get('emoji', MockEmoji())          self.message = kwargs.get('message', MockMessage()) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 2b58634dd..7894e104a 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -19,7 +19,6 @@ class DiscordMocksTests(unittest.TestCase):          self.assertIsInstance(role, discord.Role)          self.assertEqual(role.name, "role") -        self.assertEqual(role.id, 1)          self.assertEqual(role.position, 1)          self.assertEqual(role.mention, "&role") @@ -27,7 +26,7 @@ class DiscordMocksTests(unittest.TestCase):          """Test if MockRole initializes with the arguments provided."""          role = helpers.MockRole(              name="Admins", -            role_id=90210, +            id=90210,              position=10,          ) @@ -67,22 +66,21 @@ class DiscordMocksTests(unittest.TestCase):          self.assertIsInstance(member, discord.Member)          self.assertEqual(member.name, "member") -        self.assertEqual(member.id, 1) -        self.assertListEqual(member.roles, [helpers.MockRole("@everyone", 1)]) +        self.assertListEqual(member.roles, [helpers.MockRole(name="@everyone", position=1, id=0)])          self.assertEqual(member.mention, "@member")      def test_mock_member_alternative_arguments(self):          """Test if MockMember initializes with the arguments provided.""" -        core_developer = helpers.MockRole("Core Developer", 2) +        core_developer = helpers.MockRole(name="Core Developer", position=2)          member = helpers.MockMember(              name="Mark", -            user_id=12345, +            id=12345,              roles=[core_developer]          )          self.assertEqual(member.name, "Mark")          self.assertEqual(member.id, 12345) -        self.assertListEqual(member.roles, [helpers.MockRole("@everyone", 1), core_developer]) +        self.assertListEqual(member.roles, [helpers.MockRole(name="@everyone", position=1, id=0), core_developer])          self.assertEqual(member.mention, "@Mark")      def test_mock_member_accepts_dynamic_arguments(self): @@ -102,19 +100,19 @@ class DiscordMocksTests(unittest.TestCase):          # The `spec` argument makes sure `isistance` checks with `discord.Guild` pass          self.assertIsInstance(guild, discord.Guild) -        self.assertListEqual(guild.roles, [helpers.MockRole("@everyone", 1)]) +        self.assertListEqual(guild.roles, [helpers.MockRole(name="@everyone", position=1, id=0)])          self.assertListEqual(guild.members, [])      def test_mock_guild_alternative_arguments(self):          """Test if MockGuild initializes with the arguments provided.""" -        core_developer = helpers.MockRole("Core Developer", 2) +        core_developer = helpers.MockRole(name="Core Developer", position=2)          guild = helpers.MockGuild(              roles=[core_developer], -            members=[helpers.MockMember(user_id=54321)], +            members=[helpers.MockMember(id=54321)],          ) -        self.assertListEqual(guild.roles, [helpers.MockRole("@everyone", 1), core_developer]) -        self.assertListEqual(guild.members, [helpers.MockMember(user_id=54321)]) +        self.assertListEqual(guild.roles, [helpers.MockRole(name="@everyone", position=1, id=0), core_developer]) +        self.assertListEqual(guild.members, [helpers.MockMember(id=54321)])      def test_mock_guild_accepts_dynamic_arguments(self):          """Test if MockGuild accepts and sets abitrary keyword arguments.""" @@ -191,51 +189,30 @@ class DiscordMocksTests(unittest.TestCase):                  with self.assertRaises(AttributeError):                      mock.the_cake_is_a_lie -    def test_custom_mock_methods_are_valid_discord_object_methods(self): -        """The `AsyncMock` attributes of the mocks should be valid for the class they're mocking.""" -        mocks = ( -            (helpers.MockGuild, helpers.guild_instance), -            (helpers.MockRole, helpers.role_instance), -            (helpers.MockMember, helpers.member_instance), -            (helpers.MockBot, helpers.bot_instance), -            (helpers.MockContext, helpers.context_instance), -            (helpers.MockTextChannel, helpers.channel_instance), -            (helpers.MockMessage, helpers.message_instance), +    def test_mocks_use_mention_when_provided_as_kwarg(self): +        """The mock should use the passed `mention` instead of the default one if present.""" +        test_cases = ( +            (helpers.MockRole, "role mention"), +            (helpers.MockMember, "member mention"), +            (helpers.MockTextChannel, "channel mention"),          ) -        for mock_class, instance in mocks: -            mock = mock_class() -            async_methods = ( -                attr for attr in dir(mock) if isinstance(getattr(mock, attr), helpers.AsyncMock) -            ) - -            # spec_mock = unittest.mock.MagicMock(spec=instance) -            for method in async_methods: -                with self.subTest(mock_class=mock_class, method=method): -                    try: -                        getattr(instance, method) -                    except AttributeError: -                        msg = f"method {method} is not a method attribute of {instance.__class__}" -                        self.fail(msg) - -    @unittest.mock.patch(f'{__name__}.DiscordMocksTests.subTest') -    def test_the_custom_mock_methods_test(self, subtest_mock): -        """The custom method test should raise AssertionError for invalid methods.""" -        class FakeMockBot(helpers.CustomMockMixin, unittest.mock.MagicMock): -            """Fake MockBot class with invalid attribute/method `release_the_walrus`.""" - -            child_mock_type = unittest.mock.MagicMock +        for mock_type, mention in test_cases: +            with self.subTest(mock_type=mock_type, mention=mention): +                mock = mock_type(mention=mention) +                self.assertEqual(mock.mention, mention) -            def __init__(self, **kwargs): -                super().__init__(spec=helpers.bot_instance, **kwargs) +    def test_create_test_on_mock_bot_closes_passed_coroutine(self): +        """`bot.loop.create_task` should close the passed coroutine object to prevent warnings.""" +        async def dementati(): +            """Dummy coroutine for testing purposes.""" -                # Fake attribute -                self.release_the_walrus = helpers.AsyncMock() +        coroutine_object = dementati() -        with unittest.mock.patch("tests.helpers.MockBot", new=FakeMockBot): -            msg = "method release_the_walrus is not a valid method of <class 'discord.ext.commands.bot.Bot'>" -            with self.assertRaises(AssertionError, msg=msg): -                self.test_custom_mock_methods_are_valid_discord_object_methods() +        bot = helpers.MockBot() +        bot.loop.create_task(coroutine_object) +        with self.assertRaises(RuntimeError, msg="cannot reuse already awaited coroutine"): +            asyncio.run(coroutine_object)  class MockObjectTests(unittest.TestCase): @@ -266,14 +243,14 @@ class MockObjectTests(unittest.TestCase):      def test_hashable_mixin_uses_id_for_equality_comparison(self):          """Test if the HashableMixing uses the id attribute for hashing.""" -        class MockScragly(unittest.mock.Mock, helpers.HashableMixin): +        class MockScragly(helpers.HashableMixin):              pass -        scragly = MockScragly(spec=object) +        scragly = MockScragly()          scragly.id = 10 -        eevee = MockScragly(spec=object) +        eevee = MockScragly()          eevee.id = 10 -        python = MockScragly(spec=object) +        python = MockScragly()          python.id = 20          self.assertTrue(scragly == eevee) @@ -281,14 +258,14 @@ class MockObjectTests(unittest.TestCase):      def test_hashable_mixin_uses_id_for_nonequality_comparison(self):          """Test if the HashableMixing uses the id attribute for hashing.""" -        class MockScragly(unittest.mock.Mock, helpers.HashableMixin): +        class MockScragly(helpers.HashableMixin):              pass -        scragly = MockScragly(spec=object) +        scragly = MockScragly()          scragly.id = 10 -        eevee = MockScragly(spec=object) +        eevee = MockScragly()          eevee.id = 10 -        python = MockScragly(spec=object) +        python = MockScragly()          python.id = 20          self.assertTrue(scragly != python) @@ -298,7 +275,7 @@ class MockObjectTests(unittest.TestCase):          """Test if the MagicMock subclasses that implement the HashableMixin use id for hash."""          for mock in self.hashable_mocks:              with self.subTest(mock_class=mock): -                instance = helpers.MockRole(role_id=100) +                instance = helpers.MockRole(id=100)                  self.assertEqual(hash(instance), instance.id)      def test_mock_class_with_hashable_mixin_uses_id_for_equality(self): @@ -396,11 +373,11 @@ class MockObjectTests(unittest.TestCase):      @unittest.mock.patch("tests.helpers.CustomMockMixin._extract_coroutine_methods_from_spec_instance")      def test_custom_mock_mixin_init_with_spec(self, extract_method_mock):          """Test if CustomMockMixin correctly passes on spec/kwargs and calls the extraction method.""" -        spec = "pydis" +        spec_set = "pydis" -        helpers.CustomMockMixin(spec=spec) +        helpers.CustomMockMixin(spec_set=spec_set) -        extract_method_mock.assert_called_once_with(spec) +        extract_method_mock.assert_called_once_with(spec_set)      @unittest.mock.patch("builtins.super", new=unittest.mock.MagicMock())      @unittest.mock.patch("tests.helpers.CustomMockMixin._extract_coroutine_methods_from_spec_instance") | 
