diff options
| author | 2022-04-20 22:13:45 +0100 | |
|---|---|---|
| committer | 2022-04-20 22:13:45 +0100 | |
| commit | 18c3bdfe31599445102b2922b28029939f62cff7 (patch) | |
| tree | 35d2339cc710b31ff90acf4d020f61c1aa629f33 /tests | |
| parent | Parse infraction search reason as regex before calling site (#2126) (diff) | |
| parent | Refactor a try/except that will never raise (diff) | |
Merge pull request #2118 from python-discord/bump-d.py-version
Bump Discord.py to latest alpha and use BotBase from bot-core
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/bot/exts/backend/sync/test_base.py | 3 | ||||
| -rw-r--r-- | tests/bot/exts/backend/sync/test_cog.py | 30 | ||||
| -rw-r--r-- | tests/bot/exts/backend/test_error_handler.py | 10 | ||||
| -rw-r--r-- | tests/bot/exts/events/test_code_jams.py | 8 | ||||
| -rw-r--r-- | tests/bot/exts/filters/test_antimalware.py | 8 | ||||
| -rw-r--r-- | tests/bot/exts/filters/test_filtering.py | 2 | ||||
| -rw-r--r-- | tests/bot/exts/filters/test_security.py | 11 | ||||
| -rw-r--r-- | tests/bot/exts/filters/test_token_remover.py | 8 | ||||
| -rw-r--r-- | tests/bot/exts/info/test_help.py | 1 | ||||
| -rw-r--r-- | tests/bot/exts/moderation/infraction/test_utils.py | 2 | ||||
| -rw-r--r-- | tests/bot/exts/moderation/test_silence.py | 40 | ||||
| -rw-r--r-- | tests/bot/exts/utils/test_snekbox.py | 8 | ||||
| -rw-r--r-- | tests/bot/test_api.py | 66 | ||||
| -rw-r--r-- | tests/helpers.py | 12 | ||||
| -rw-r--r-- | tests/test_helpers.py | 2 | 
15 files changed, 70 insertions, 141 deletions
| diff --git a/tests/bot/exts/backend/sync/test_base.py b/tests/bot/exts/backend/sync/test_base.py index 9dc46005b..a17c1fa10 100644 --- a/tests/bot/exts/backend/sync/test_base.py +++ b/tests/bot/exts/backend/sync/test_base.py @@ -1,7 +1,8 @@  import unittest  from unittest import mock -from bot.api import ResponseCodeError +from botcore.site_api import ResponseCodeError +  from bot.exts.backend.sync._syncers import Syncer  from tests import helpers diff --git a/tests/bot/exts/backend/sync/test_cog.py b/tests/bot/exts/backend/sync/test_cog.py index fdd0ab74a..87b76c6b4 100644 --- a/tests/bot/exts/backend/sync/test_cog.py +++ b/tests/bot/exts/backend/sync/test_cog.py @@ -2,9 +2,9 @@ import unittest  from unittest import mock  import discord +from botcore.site_api import ResponseCodeError  from bot import constants -from bot.api import ResponseCodeError  from bot.exts.backend import sync  from bot.exts.backend.sync._cog import Sync  from bot.exts.backend.sync._syncers import Syncer @@ -16,11 +16,11 @@ class SyncExtensionTests(unittest.IsolatedAsyncioTestCase):      """Tests for the sync extension."""      @staticmethod -    def test_extension_setup(): +    async def test_extension_setup():          """The Sync cog should be added."""          bot = helpers.MockBot() -        sync.setup(bot) -        bot.add_cog.assert_called_once() +        await sync.setup(bot) +        bot.add_cog.assert_awaited_once()  class SyncCogTestCase(unittest.IsolatedAsyncioTestCase): @@ -60,22 +60,18 @@ class SyncCogTestCase(unittest.IsolatedAsyncioTestCase):  class SyncCogTests(SyncCogTestCase):      """Tests for the Sync cog.""" -    @mock.patch("bot.utils.scheduling.create_task") -    @mock.patch.object(Sync, "sync_guild", new_callable=mock.MagicMock) -    def test_sync_cog_init(self, sync_guild, create_task): -        """Should instantiate syncers and run a sync for the guild.""" -        # Reset because a Sync cog was already instantiated in setUp. +    async def test_sync_cog_sync_on_load(self): +        """Roles and users should be synced on cog load.""" +        guild = helpers.MockGuild() +        self.bot.get_guild = mock.MagicMock(return_value=guild) +          self.RoleSyncer.reset_mock()          self.UserSyncer.reset_mock() -        mock_sync_guild_coro = mock.MagicMock() -        sync_guild.return_value = mock_sync_guild_coro - -        Sync(self.bot) +        await self.cog.cog_load() -        sync_guild.assert_called_once_with() -        create_task.assert_called_once() -        self.assertEqual(create_task.call_args.args[0], mock_sync_guild_coro) +        self.RoleSyncer.sync.assert_called_once_with(guild) +        self.UserSyncer.sync.assert_called_once_with(guild)      async def test_sync_cog_sync_guild(self):          """Roles and users should be synced only if a guild is successfully retrieved.""" @@ -87,7 +83,7 @@ class SyncCogTests(SyncCogTestCase):                  self.bot.get_guild = mock.MagicMock(return_value=guild) -                await self.cog.sync_guild() +                await self.cog.cog_load()                  self.bot.wait_until_guild_available.assert_called_once()                  self.bot.get_guild.assert_called_once_with(constants.Guild.id) diff --git a/tests/bot/exts/backend/test_error_handler.py b/tests/bot/exts/backend/test_error_handler.py index 35fa0ee59..193f1d822 100644 --- a/tests/bot/exts/backend/test_error_handler.py +++ b/tests/bot/exts/backend/test_error_handler.py @@ -1,9 +1,9 @@  import unittest  from unittest.mock import AsyncMock, MagicMock, call, patch +from botcore.site_api import ResponseCodeError  from discord.ext.commands import errors -from bot.api import ResponseCodeError  from bot.errors import InvalidInfractedUserError, LockedResourceError  from bot.exts.backend.error_handler import ErrorHandler, setup  from bot.exts.info.tags import Tags @@ -544,11 +544,11 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase):                  push_scope_mock.set_extra.has_calls(set_extra_calls) -class ErrorHandlerSetupTests(unittest.TestCase): +class ErrorHandlerSetupTests(unittest.IsolatedAsyncioTestCase):      """Tests for `ErrorHandler` `setup` function.""" -    def test_setup(self): +    async def test_setup(self):          """Should call `bot.add_cog` with `ErrorHandler`."""          bot = MockBot() -        setup(bot) -        bot.add_cog.assert_called_once() +        await setup(bot) +        bot.add_cog.assert_awaited_once() diff --git a/tests/bot/exts/events/test_code_jams.py b/tests/bot/exts/events/test_code_jams.py index 0856546af..684f7abcd 100644 --- a/tests/bot/exts/events/test_code_jams.py +++ b/tests/bot/exts/events/test_code_jams.py @@ -160,11 +160,11 @@ class JamCodejamCreateTests(unittest.IsolatedAsyncioTestCase):                  member.add_roles.assert_not_awaited() -class CodeJamSetup(unittest.TestCase): +class CodeJamSetup(unittest.IsolatedAsyncioTestCase):      """Test for `setup` function of `CodeJam` cog.""" -    def test_setup(self): +    async def test_setup(self):          """Should call `bot.add_cog`."""          bot = MockBot() -        code_jams.setup(bot) -        bot.add_cog.assert_called_once() +        await code_jams.setup(bot) +        bot.add_cog.assert_awaited_once() diff --git a/tests/bot/exts/filters/test_antimalware.py b/tests/bot/exts/filters/test_antimalware.py index 06d78de9d..7282334e2 100644 --- a/tests/bot/exts/filters/test_antimalware.py +++ b/tests/bot/exts/filters/test_antimalware.py @@ -192,11 +192,11 @@ class AntiMalwareCogTests(unittest.IsolatedAsyncioTestCase):                  self.assertCountEqual(disallowed_extensions, expected_disallowed_extensions) -class AntiMalwareSetupTests(unittest.TestCase): +class AntiMalwareSetupTests(unittest.IsolatedAsyncioTestCase):      """Tests setup of the `AntiMalware` cog.""" -    def test_setup(self): +    async def test_setup(self):          """Setup of the extension should call add_cog."""          bot = MockBot() -        antimalware.setup(bot) -        bot.add_cog.assert_called_once() +        await antimalware.setup(bot) +        bot.add_cog.assert_awaited_once() diff --git a/tests/bot/exts/filters/test_filtering.py b/tests/bot/exts/filters/test_filtering.py index 8ae59c1f1..bd26532f1 100644 --- a/tests/bot/exts/filters/test_filtering.py +++ b/tests/bot/exts/filters/test_filtering.py @@ -11,7 +11,7 @@ class FilteringCogTests(unittest.IsolatedAsyncioTestCase):      def setUp(self):          """Instantiate the bot and cog."""          self.bot = MockBot() -        with patch("bot.utils.scheduling.create_task", new=lambda task, **_: task.close()): +        with patch("botcore.utils.scheduling.create_task", new=lambda task, **_: task.close()):              self.cog = filtering.Filtering(self.bot)      @autospec(filtering.Filtering, "_get_filterlist_items", pass_mocks=False, return_value=["TOKEN"]) diff --git a/tests/bot/exts/filters/test_security.py b/tests/bot/exts/filters/test_security.py index c0c3baa42..007b7b1eb 100644 --- a/tests/bot/exts/filters/test_security.py +++ b/tests/bot/exts/filters/test_security.py @@ -1,5 +1,4 @@  import unittest -from unittest.mock import MagicMock  from discord.ext.commands import NoPrivateMessage @@ -44,11 +43,11 @@ class SecurityCogTests(unittest.TestCase):          self.assertTrue(self.cog.check_on_guild(self.ctx)) -class SecurityCogLoadTests(unittest.TestCase): +class SecurityCogLoadTests(unittest.IsolatedAsyncioTestCase):      """Tests loading the `Security` cog.""" -    def test_security_cog_load(self): +    async def test_security_cog_load(self):          """Setup of the extension should call add_cog.""" -        bot = MagicMock() -        security.setup(bot) -        bot.add_cog.assert_called_once() +        bot = MockBot() +        await security.setup(bot) +        bot.add_cog.assert_awaited_once() diff --git a/tests/bot/exts/filters/test_token_remover.py b/tests/bot/exts/filters/test_token_remover.py index 4db27269a..c1f3762ac 100644 --- a/tests/bot/exts/filters/test_token_remover.py +++ b/tests/bot/exts/filters/test_token_remover.py @@ -395,15 +395,15 @@ class TokenRemoverTests(unittest.IsolatedAsyncioTestCase):          self.msg.channel.send.assert_not_awaited() -class TokenRemoverExtensionTests(unittest.TestCase): +class TokenRemoverExtensionTests(unittest.IsolatedAsyncioTestCase):      """Tests for the token_remover extension."""      @autospec("bot.exts.filters.token_remover", "TokenRemover") -    def test_extension_setup(self, cog): +    async def test_extension_setup(self, cog):          """The TokenRemover cog should be added."""          bot = MockBot() -        token_remover.setup(bot) +        await token_remover.setup(bot)          cog.assert_called_once_with(bot) -        bot.add_cog.assert_called_once() +        bot.add_cog.assert_awaited_once()          self.assertTrue(isinstance(bot.add_cog.call_args.args[0], TokenRemover)) diff --git a/tests/bot/exts/info/test_help.py b/tests/bot/exts/info/test_help.py index 604c69671..2644ae40d 100644 --- a/tests/bot/exts/info/test_help.py +++ b/tests/bot/exts/info/test_help.py @@ -12,7 +12,6 @@ class HelpCogTests(unittest.IsolatedAsyncioTestCase):          self.bot = MockBot()          self.cog = help.Help(self.bot)          self.ctx = MockContext(bot=self.bot) -        self.bot.help_command.context = self.ctx      @autospec(help.CustomHelpCommand, "get_all_help_choices", return_value={"help"}, pass_mocks=False)      async def test_help_fuzzy_matching(self): diff --git a/tests/bot/exts/moderation/infraction/test_utils.py b/tests/bot/exts/moderation/infraction/test_utils.py index ff81ddd65..5cf02033d 100644 --- a/tests/bot/exts/moderation/infraction/test_utils.py +++ b/tests/bot/exts/moderation/infraction/test_utils.py @@ -3,9 +3,9 @@ from collections import namedtuple  from datetime import datetime  from unittest.mock import AsyncMock, MagicMock, call, patch +from botcore.site_api import ResponseCodeError  from discord import Embed, Forbidden, HTTPException, NotFound -from bot.api import ResponseCodeError  from bot.constants import Colours, Icons  from bot.exts.moderation.infraction import _utils as utils  from tests.helpers import MockBot, MockContext, MockMember, MockUser diff --git a/tests/bot/exts/moderation/test_silence.py b/tests/bot/exts/moderation/test_silence.py index 92ce3418a..65aecad28 100644 --- a/tests/bot/exts/moderation/test_silence.py +++ b/tests/bot/exts/moderation/test_silence.py @@ -114,44 +114,36 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):          self.cog = silence.Silence(self.bot)      @autospec(silence, "SilenceNotifier", pass_mocks=False) -    async def test_async_init_got_guild(self): +    async def test_cog_load_got_guild(self):          """Bot got guild after it became available.""" -        await self.cog._async_init() +        await self.cog.cog_load()          self.bot.wait_until_guild_available.assert_awaited_once()          self.bot.get_guild.assert_called_once_with(Guild.id)      @autospec(silence, "SilenceNotifier", pass_mocks=False) -    async def test_async_init_got_channels(self): +    async def test_cog_load_got_channels(self):          """Got channels from bot."""          self.bot.get_channel.side_effect = lambda id_: MockTextChannel(id=id_) -        await self.cog._async_init() +        await self.cog.cog_load()          self.assertEqual(self.cog._mod_alerts_channel.id, Channels.mod_alerts)      @autospec(silence, "SilenceNotifier") -    async def test_async_init_got_notifier(self, notifier): +    async def test_cog_load_got_notifier(self, notifier):          """Notifier was started with channel."""          self.bot.get_channel.side_effect = lambda id_: MockTextChannel(id=id_) -        await self.cog._async_init() +        await self.cog.cog_load()          notifier.assert_called_once_with(MockTextChannel(id=Channels.mod_log))          self.assertEqual(self.cog.notifier, notifier.return_value)      @autospec(silence, "SilenceNotifier", pass_mocks=False) -    async def test_async_init_rescheduled(self): +    async def testcog_load_rescheduled(self):          """`_reschedule_` coroutine was awaited."""          self.cog._reschedule = mock.create_autospec(self.cog._reschedule) -        await self.cog._async_init() +        await self.cog.cog_load()          self.cog._reschedule.assert_awaited_once_with() -    def test_cog_unload_cancelled_tasks(self): -        """The init task was cancelled.""" -        self.cog._init_task = asyncio.Future() -        self.cog.cog_unload() - -        # It's too annoying to test cancel_all since it's a done callback and wrapped in a lambda. -        self.assertTrue(self.cog._init_task.cancelled()) -      @autospec("discord.ext.commands", "has_any_role")      @mock.patch.object(silence.constants, "MODERATION_ROLES", new=(1, 2, 3))      async def test_cog_check(self, role_check): @@ -165,7 +157,7 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):      async def test_force_voice_sync(self):          """Tests the _force_voice_sync helper function.""" -        await self.cog._async_init() +        await self.cog.cog_load()          # Create a regular member, and one member for each of the moderation roles          moderation_members = [MockMember(roles=[MockRole(id=role)]) for role in MODERATION_ROLES] @@ -187,7 +179,7 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):      async def test_force_voice_sync_no_channel(self):          """Test to ensure _force_voice_sync can create its own voice channel if one is not available.""" -        await self.cog._async_init() +        await self.cog.cog_load()          channel = MockVoiceChannel(guild=MockGuild(afk_channel=None))          new_channel = MockVoiceChannel(delete=AsyncMock()) @@ -206,7 +198,7 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):      async def test_voice_kick(self):          """Test to ensure kick function can remove all members from a voice channel.""" -        await self.cog._async_init() +        await self.cog.cog_load()          # Create a regular member, and one member for each of the moderation roles          moderation_members = [MockMember(roles=[MockRole(id=role)]) for role in MODERATION_ROLES] @@ -236,7 +228,7 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):      async def test_kick_move_to_error(self):          """Test to ensure move_to gets called on all members during kick, even if some fail.""" -        await self.cog._async_init() +        await self.cog.cog_load()          _, members = self.create_erroneous_members()          await self.cog._kick_voice_members(MockVoiceChannel(members=members)) @@ -245,7 +237,7 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):      async def test_sync_move_to_error(self):          """Test to ensure move_to gets called on all members during sync, even if some fail.""" -        await self.cog._async_init() +        await self.cog.cog_load()          failing_member, members = self.create_erroneous_members()          await self.cog._force_voice_sync(MockVoiceChannel(members=members)) @@ -339,7 +331,7 @@ class RescheduleTests(unittest.IsolatedAsyncioTestCase):          self.cog._unsilence_wrapper = mock.create_autospec(self.cog._unsilence_wrapper)          with mock.patch.object(self.cog, "_reschedule", autospec=True): -            asyncio.run(self.cog._async_init())  # Populate instance attributes. +            asyncio.run(self.cog.cog_load())  # Populate instance attributes.      async def test_skipped_missing_channel(self):          """Did nothing because the channel couldn't be retrieved.""" @@ -428,7 +420,7 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):          # Avoid unawaited coroutine warnings.          self.cog.scheduler.schedule_later.side_effect = lambda delay, task_id, coro: coro.close() -        asyncio.run(self.cog._async_init())  # Populate instance attributes. +        asyncio.run(self.cog.cog_load())  # Populate instance attributes.          self.text_channel = MockTextChannel()          self.text_overwrite = PermissionOverwrite( @@ -701,7 +693,7 @@ class UnsilenceTests(unittest.IsolatedAsyncioTestCase):          overwrites_cache = mock.create_autospec(self.cog.previous_overwrites, spec_set=True)          self.cog.previous_overwrites = overwrites_cache -        asyncio.run(self.cog._async_init())  # Populate instance attributes. +        asyncio.run(self.cog.cog_load())  # Populate instance attributes.          self.cog.scheduler.__contains__.return_value = True          overwrites_cache.get.return_value = '{"send_messages": true, "add_reactions": false}' diff --git a/tests/bot/exts/utils/test_snekbox.py b/tests/bot/exts/utils/test_snekbox.py index f68a20089..3c555c051 100644 --- a/tests/bot/exts/utils/test_snekbox.py +++ b/tests/bot/exts/utils/test_snekbox.py @@ -403,11 +403,11 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):                  self.assertEqual(actual, expected) -class SnekboxSetupTests(unittest.TestCase): +class SnekboxSetupTests(unittest.IsolatedAsyncioTestCase):      """Tests setup of the `Snekbox` cog.""" -    def test_setup(self): +    async def test_setup(self):          """Setup of the extension should call add_cog."""          bot = MockBot() -        snekbox.setup(bot) -        bot.add_cog.assert_called_once() +        await snekbox.setup(bot) +        bot.add_cog.assert_awaited_once() diff --git a/tests/bot/test_api.py b/tests/bot/test_api.py deleted file mode 100644 index 76bcb481d..000000000 --- a/tests/bot/test_api.py +++ /dev/null @@ -1,66 +0,0 @@ -import unittest -from unittest.mock import MagicMock - -from bot import api - - -class APIClientTests(unittest.IsolatedAsyncioTestCase): -    """Tests for the bot's API client.""" - -    @classmethod -    def setUpClass(cls): -        """Sets up the shared fixtures for the tests.""" -        cls.error_api_response = MagicMock() -        cls.error_api_response.status = 999 - -    def test_response_code_error_default_initialization(self): -        """Test the default initialization of `ResponseCodeError` without `text` or `json`""" -        error = api.ResponseCodeError(response=self.error_api_response) - -        self.assertIs(error.status, self.error_api_response.status) -        self.assertEqual(error.response_json, {}) -        self.assertEqual(error.response_text, "") -        self.assertIs(error.response, self.error_api_response) - -    def test_response_code_error_string_representation_default_initialization(self): -        """Test the string representation of `ResponseCodeError` initialized without text or json.""" -        error = api.ResponseCodeError(response=self.error_api_response) -        self.assertEqual(str(error), f"Status: {self.error_api_response.status} Response: ") - -    def test_response_code_error_initialization_with_json(self): -        """Test the initialization of `ResponseCodeError` with json.""" -        json_data = {'hello': 'world'} -        error = api.ResponseCodeError( -            response=self.error_api_response, -            response_json=json_data, -        ) -        self.assertEqual(error.response_json, json_data) -        self.assertEqual(error.response_text, "") - -    def test_response_code_error_string_representation_with_nonempty_response_json(self): -        """Test the string representation of `ResponseCodeError` initialized with json.""" -        json_data = {'hello': 'world'} -        error = api.ResponseCodeError( -            response=self.error_api_response, -            response_json=json_data -        ) -        self.assertEqual(str(error), f"Status: {self.error_api_response.status} Response: {json_data}") - -    def test_response_code_error_initialization_with_text(self): -        """Test the initialization of `ResponseCodeError` with text.""" -        text_data = 'Lemon will eat your soul' -        error = api.ResponseCodeError( -            response=self.error_api_response, -            response_text=text_data, -        ) -        self.assertEqual(error.response_text, text_data) -        self.assertEqual(error.response_json, {}) - -    def test_response_code_error_string_representation_with_nonempty_response_text(self): -        """Test the string representation of `ResponseCodeError` initialized with text.""" -        text_data = 'Lemon will eat your soul' -        error = api.ResponseCodeError( -            response=self.error_api_response, -            response_text=text_data -        ) -        self.assertEqual(str(error), f"Status: {self.error_api_response.status} Response: {text_data}") diff --git a/tests/helpers.py b/tests/helpers.py index 9d4988d23..a6e4bdd66 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -9,10 +9,10 @@ from typing import Iterable, Optional  import discord  from aiohttp import ClientSession +from botcore.async_stats import AsyncStatsClient +from botcore.site_api import APIClient  from discord.ext.commands import Context -from bot.api import APIClient -from bot.async_stats import AsyncStatsClient  from bot.bot import Bot  from tests._autospec import autospec  # noqa: F401 other modules import it via this module @@ -312,6 +312,10 @@ class MockBot(CustomMockMixin, unittest.mock.MagicMock):          command_prefix=unittest.mock.MagicMock(),          loop=_get_mock_loop(),          redis_session=unittest.mock.MagicMock(), +        http_session=unittest.mock.MagicMock(), +        allowed_roles=[1], +        guild_id=1, +        intents=discord.Intents.all(),      )      additional_spec_asyncs = ("wait_for", "redis_ready") @@ -322,6 +326,7 @@ class MockBot(CustomMockMixin, unittest.mock.MagicMock):          self.api_client = MockAPIClient(loop=self.loop)          self.http_session = unittest.mock.create_autospec(spec=ClientSession, spec_set=True)          self.stats = unittest.mock.create_autospec(spec=AsyncStatsClient, spec_set=True) +        self.add_cog = unittest.mock.AsyncMock()  # Create a TextChannel instance to get a realistic MagicMock of `discord.TextChannel` @@ -334,6 +339,8 @@ channel_data = {      'position': 1,      'nsfw': False,      'last_message_id': 1, +    'bitrate': 1337, +    'user_limit': 25,  }  state = unittest.mock.MagicMock()  guild = unittest.mock.MagicMock() @@ -438,6 +445,7 @@ message_data = {  }  state = unittest.mock.MagicMock()  channel = unittest.mock.MagicMock() +channel.type = discord.ChannelType.text  message_instance = discord.Message(state=state, channel=channel, data=message_data) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 81285e009..f3040b305 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -327,7 +327,7 @@ class MockObjectTests(unittest.TestCase):      def test_spec_propagation_of_mock_subclasses(self):          """Test if the `spec` does not propagate to attributes of the mock object."""          test_values = ( -            (helpers.MockGuild, "region"), +            (helpers.MockGuild, "features"),              (helpers.MockRole, "mentionable"),              (helpers.MockMember, "display_name"),              (helpers.MockBot, "owner_id"), | 
