From 7b9e71fbb1364a416e5239b45434874fed9eb857 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Mon, 10 Feb 2020 16:31:07 -0800 Subject: Tests: create TestCase subclass with a permissions check assertion The subclass will contain assertions that are useful for testing Discord commands. The currently included assertion tests that a command will raise a MissingPermissions exception if the author lacks permissions. --- tests/base.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'tests/base.py') diff --git a/tests/base.py b/tests/base.py index 029a249ed..88693f382 100644 --- a/tests/base.py +++ b/tests/base.py @@ -1,6 +1,12 @@ import logging import unittest from contextlib import contextmanager +from typing import Dict + +import discord +from discord.ext import commands + +from tests import helpers class _CaptureLogHandler(logging.Handler): @@ -65,3 +71,31 @@ class LoggingTestCase(unittest.TestCase): standard_message = self._truncateMessage(base_message, record_message) msg = self._formatMessage(msg, standard_message) self.fail(msg) + + +class CommandTestCase(unittest.TestCase): + """TestCase with additional assertions that are useful for testing Discord commands.""" + + @helpers.async_test + async def assertHasPermissionsCheck( + self, + cmd: commands.Command, + permissions: Dict[str, bool], + ) -> None: + """ + Test that `cmd` raises a `MissingPermissions` exception if author lacks `permissions`. + + Every permission in `permissions` is expected to be reported as missing. In other words, do + not include permissions which should not raise an exception along with those which should. + """ + # Invert permission values because it's more intuitive to pass to this assertion the same + # permissions as those given to the check decorator. + permissions = {k: not v for k, v in permissions.items()} + + ctx = helpers.MockContext() + ctx.channel.permissions_for.return_value = discord.Permissions(**permissions) + + with self.assertRaises(commands.MissingPermissions) as cm: + await cmd.can_run(ctx) + + self.assertCountEqual(permissions.keys(), cm.exception.missing_perms) -- cgit v1.2.3 From d3f4673c1a1c3f5213840e756c5f35f7c70d46f6 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Sun, 23 Feb 2020 12:39:25 +0100 Subject: Use mixin-composition not inheritance for LoggingTestCase We used inheritence to add additional logging assertion methods to unittest's TestCase class. However, with the introduction of the new IsolatedAsyncioTestCase this extension strategy means we'd have to create multiple child classes to be able to use the extended functionality in all of the TestCase variants. Since that leads to undesirable code reuse and an inheritance relationship is not at all needed, I've switched to a mixin-composition based approach that allows the user to extend the functionality of any TestCase variant with a mixin where needed. --- tests/base.py | 10 +++++++--- tests/bot/cogs/test_duck_pond.py | 2 +- tests/test_base.py | 18 ++++++------------ 3 files changed, 14 insertions(+), 16 deletions(-) (limited to 'tests/base.py') diff --git a/tests/base.py b/tests/base.py index 029a249ed..21a57716a 100644 --- a/tests/base.py +++ b/tests/base.py @@ -1,5 +1,4 @@ import logging -import unittest from contextlib import contextmanager @@ -16,8 +15,13 @@ class _CaptureLogHandler(logging.Handler): self.records.append(record) -class LoggingTestCase(unittest.TestCase): - """TestCase subclass that adds more logging assertion tools.""" +class LoggingTestsMixin: + """ + A mixin that defines additional test methods for logging behavior. + + This mixin relies on the availability of the `fail` attribute defined by the + test classes included in Python's unittest method to signal test failure. + """ @contextmanager def assertNotLogs(self, logger=None, level=None, msg=None): diff --git a/tests/bot/cogs/test_duck_pond.py b/tests/bot/cogs/test_duck_pond.py index d07b2bce1..320cbd5c5 100644 --- a/tests/bot/cogs/test_duck_pond.py +++ b/tests/bot/cogs/test_duck_pond.py @@ -14,7 +14,7 @@ from tests import helpers MODULE_PATH = "bot.cogs.duck_pond" -class DuckPondTests(base.LoggingTestCase): +class DuckPondTests(base.LoggingTestsMixin, unittest.TestCase): """Tests for DuckPond functionality.""" @classmethod diff --git a/tests/test_base.py b/tests/test_base.py index a16e2af8f..23abb1dfd 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -3,7 +3,11 @@ import unittest import unittest.mock -from tests.base import LoggingTestCase, _CaptureLogHandler +from tests.base import LoggingTestsMixin, _CaptureLogHandler + + +class LoggingTestCase(LoggingTestsMixin): + pass class LoggingTestCaseTests(unittest.TestCase): @@ -18,19 +22,9 @@ class LoggingTestCaseTests(unittest.TestCase): try: with LoggingTestCase.assertNotLogs(self, level=logging.DEBUG): pass - except AssertionError: + except AssertionError: # pragma: no cover self.fail("`self.assertNotLogs` raised an AssertionError when it should not!") - @unittest.mock.patch("tests.base.LoggingTestCase.assertNotLogs") - def test_the_test_function_assert_not_logs_does_not_raise_with_no_logs(self, assertNotLogs): - """Test if test_assert_not_logs_does_not_raise_with_no_logs captures exception correctly.""" - assertNotLogs.return_value = iter([None]) - assertNotLogs.side_effect = AssertionError - - message = "`self.assertNotLogs` raised an AssertionError when it should not!" - with self.assertRaises(AssertionError, msg=message): - self.test_assert_not_logs_does_not_raise_with_no_logs() - def test_assert_not_logs_raises_correct_assertion_error_when_logs_are_emitted(self): """Test if LoggingTestCase.assertNotLogs raises AssertionError when logs were emitted.""" msg_regex = ( -- cgit v1.2.3 From b8bd18bd743608ddff47064d0b459edff3da65e3 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Mon, 24 Feb 2020 02:12:02 +0100 Subject: Migrate syncers test suite to Python 3.8 The test suite for the new role/member syncers used the "old"-style test suite with the helpers implemented for Python 3.7. I have migrated it to use the new Python 3.8 asyncio test helpers. --- tests/base.py | 4 ++-- tests/bot/cogs/sync/test_base.py | 45 ++++++++++++++++----------------------- tests/bot/cogs/sync/test_cog.py | 31 ++++++++------------------- tests/bot/cogs/sync/test_roles.py | 12 ++--------- tests/bot/cogs/sync/test_users.py | 13 ++--------- tests/helpers.py | 4 +--- 6 files changed, 34 insertions(+), 75 deletions(-) (limited to 'tests/base.py') diff --git a/tests/base.py b/tests/base.py index 21613110e..42174e911 100644 --- a/tests/base.py +++ b/tests/base.py @@ -1,4 +1,5 @@ import logging +import unittest from contextlib import contextmanager from typing import Dict @@ -77,10 +78,9 @@ class LoggingTestsMixin: self.fail(msg) -class CommandTestCase(unittest.TestCase): +class CommandTestCase(unittest.IsolatedAsyncioTestCase): """TestCase with additional assertions that are useful for testing Discord commands.""" - @helpers.async_test async def assertHasPermissionsCheck( self, cmd: commands.Command, diff --git a/tests/bot/cogs/sync/test_base.py b/tests/bot/cogs/sync/test_base.py index e6a6f9688..17aa4198b 100644 --- a/tests/bot/cogs/sync/test_base.py +++ b/tests/bot/cogs/sync/test_base.py @@ -13,8 +13,8 @@ class TestSyncer(Syncer): """Syncer subclass with mocks for abstract methods for testing purposes.""" name = "test" - _get_diff = helpers.AsyncMock() - _sync = helpers.AsyncMock() + _get_diff = mock.AsyncMock() + _sync = mock.AsyncMock() class SyncerBaseTests(unittest.TestCase): @@ -29,7 +29,7 @@ class SyncerBaseTests(unittest.TestCase): Syncer(self.bot) -class SyncerSendPromptTests(unittest.TestCase): +class SyncerSendPromptTests(unittest.IsolatedAsyncioTestCase): """Tests for sending the sync confirmation prompt.""" def setUp(self): @@ -61,7 +61,6 @@ class SyncerSendPromptTests(unittest.TestCase): return mock_channel, mock_message - @helpers.async_test async def test_send_prompt_edits_and_returns_message(self): """The given message should be edited to display the prompt and then should be returned.""" msg = helpers.MockMessage() @@ -71,7 +70,6 @@ class SyncerSendPromptTests(unittest.TestCase): self.assertIn("content", msg.edit.call_args[1]) self.assertEqual(ret_val, msg) - @helpers.async_test async def test_send_prompt_gets_dev_core_channel(self): """The dev-core channel should be retrieved if an extant message isn't given.""" subtests = ( @@ -86,7 +84,6 @@ class SyncerSendPromptTests(unittest.TestCase): method.assert_called_once_with(constants.Channels.devcore) - @helpers.async_test async def test_send_prompt_returns_None_if_channel_fetch_fails(self): """None should be returned if there's an HTTPException when fetching the channel.""" self.bot.get_channel.return_value = None @@ -96,7 +93,6 @@ class SyncerSendPromptTests(unittest.TestCase): self.assertIsNone(ret_val) - @helpers.async_test async def test_send_prompt_sends_and_returns_new_message_if_not_given(self): """A new message mentioning core devs should be sent and returned if message isn't given.""" for mock_ in (self.mock_get_channel, self.mock_fetch_channel): @@ -108,7 +104,6 @@ class SyncerSendPromptTests(unittest.TestCase): self.assertIn(self.syncer._CORE_DEV_MENTION, mock_channel.send.call_args[0][0]) self.assertEqual(ret_val, mock_message) - @helpers.async_test async def test_send_prompt_adds_reactions(self): """The message should have reactions for confirmation added.""" extant_message = helpers.MockMessage() @@ -129,7 +124,7 @@ class SyncerSendPromptTests(unittest.TestCase): mock_message.add_reaction.assert_has_calls(calls) -class SyncerConfirmationTests(unittest.TestCase): +class SyncerConfirmationTests(unittest.IsolatedAsyncioTestCase): """Tests for waiting for a sync confirmation reaction on the prompt.""" def setUp(self): @@ -211,7 +206,6 @@ class SyncerConfirmationTests(unittest.TestCase): ret_val = self.syncer._reaction_check(*args) self.assertFalse(ret_val) - @helpers.async_test async def test_wait_for_confirmation(self): """The message should always be edited and only return True if the emoji is a check mark.""" subtests = ( @@ -251,14 +245,13 @@ class SyncerConfirmationTests(unittest.TestCase): self.assertIs(actual_return, ret_val) -class SyncerSyncTests(unittest.TestCase): +class SyncerSyncTests(unittest.IsolatedAsyncioTestCase): """Tests for main function orchestrating the sync.""" def setUp(self): self.bot = helpers.MockBot(user=helpers.MockMember(bot=True)) self.syncer = TestSyncer(self.bot) - @helpers.async_test async def test_sync_respects_confirmation_result(self): """The sync should abort if confirmation fails and continue if confirmed.""" mock_message = helpers.MockMessage() @@ -274,7 +267,7 @@ class SyncerSyncTests(unittest.TestCase): diff = _Diff({1, 2, 3}, {4, 5}, None) self.syncer._get_diff.return_value = diff - self.syncer._get_confirmation_result = helpers.AsyncMock( + self.syncer._get_confirmation_result = mock.AsyncMock( return_value=(confirmed, message) ) @@ -289,7 +282,6 @@ class SyncerSyncTests(unittest.TestCase): else: self.syncer._sync.assert_not_called() - @helpers.async_test async def test_sync_diff_size(self): """The diff size should be correctly calculated.""" subtests = ( @@ -303,7 +295,7 @@ class SyncerSyncTests(unittest.TestCase): with self.subTest(size=size, diff=diff): self.syncer._get_diff.reset_mock() self.syncer._get_diff.return_value = diff - self.syncer._get_confirmation_result = helpers.AsyncMock(return_value=(False, None)) + self.syncer._get_confirmation_result = mock.AsyncMock(return_value=(False, None)) guild = helpers.MockGuild() await self.syncer.sync(guild) @@ -312,7 +304,6 @@ class SyncerSyncTests(unittest.TestCase): self.syncer._get_confirmation_result.assert_called_once() self.assertEqual(self.syncer._get_confirmation_result.call_args[0][0], size) - @helpers.async_test async def test_sync_message_edited(self): """The message should be edited if one was sent, even if the sync has an API error.""" subtests = ( @@ -324,7 +315,7 @@ class SyncerSyncTests(unittest.TestCase): for message, side_effect, should_edit in subtests: with self.subTest(message=message, side_effect=side_effect, should_edit=should_edit): self.syncer._sync.side_effect = side_effect - self.syncer._get_confirmation_result = helpers.AsyncMock( + self.syncer._get_confirmation_result = mock.AsyncMock( return_value=(True, message) ) @@ -335,7 +326,6 @@ class SyncerSyncTests(unittest.TestCase): message.edit.assert_called_once() self.assertIn("content", message.edit.call_args[1]) - @helpers.async_test async def test_sync_confirmation_context_redirect(self): """If ctx is given, a new message should be sent and author should be ctx's author.""" mock_member = helpers.MockMember() @@ -349,7 +339,10 @@ class SyncerSyncTests(unittest.TestCase): if ctx is not None: ctx.send.return_value = message - self.syncer._get_confirmation_result = helpers.AsyncMock(return_value=(False, None)) + diff = _Diff({1, 2, 3}, {4, 5}, None) + self.syncer._get_diff.return_value = diff + + self.syncer._get_confirmation_result = mock.AsyncMock(return_value=(False, None)) guild = helpers.MockGuild() await self.syncer.sync(guild, ctx) @@ -362,16 +355,15 @@ class SyncerSyncTests(unittest.TestCase): self.assertEqual(self.syncer._get_confirmation_result.call_args[0][2], message) @mock.patch.object(constants.Sync, "max_diff", new=3) - @helpers.async_test async def test_confirmation_result_small_diff(self): """Should always return True and the given message if the diff size is too small.""" author = helpers.MockMember() expected_message = helpers.MockMessage() - for size in (3, 2): + for size in (3, 2): # pragma: no cover with self.subTest(size=size): - self.syncer._send_prompt = helpers.AsyncMock() - self.syncer._wait_for_confirmation = helpers.AsyncMock() + self.syncer._send_prompt = mock.AsyncMock() + self.syncer._wait_for_confirmation = mock.AsyncMock() coro = self.syncer._get_confirmation_result(size, author, expected_message) result, actual_message = await coro @@ -382,7 +374,6 @@ class SyncerSyncTests(unittest.TestCase): self.syncer._wait_for_confirmation.assert_not_called() @mock.patch.object(constants.Sync, "max_diff", new=3) - @helpers.async_test async def test_confirmation_result_large_diff(self): """Should return True if confirmed and False if _send_prompt fails or aborted.""" author = helpers.MockMember() @@ -394,10 +385,10 @@ class SyncerSyncTests(unittest.TestCase): (False, mock_message, False, "aborted"), ) - for expected_result, expected_message, confirmed, msg in subtests: + for expected_result, expected_message, confirmed, msg in subtests: # pragma: no cover with self.subTest(msg=msg): - self.syncer._send_prompt = helpers.AsyncMock(return_value=expected_message) - self.syncer._wait_for_confirmation = helpers.AsyncMock(return_value=confirmed) + self.syncer._send_prompt = mock.AsyncMock(return_value=expected_message) + self.syncer._wait_for_confirmation = mock.AsyncMock(return_value=confirmed) coro = self.syncer._get_confirmation_result(4, author) actual_result, actual_message = await coro diff --git a/tests/bot/cogs/sync/test_cog.py b/tests/bot/cogs/sync/test_cog.py index 98c9afc0d..8c87c0d6b 100644 --- a/tests/bot/cogs/sync/test_cog.py +++ b/tests/bot/cogs/sync/test_cog.py @@ -18,12 +18,13 @@ class MockSyncer(helpers.CustomMockMixin, mock.MagicMock): Instances of this class will follow the specifications of `bot.cogs.sync.syncers.Syncer` instances. For more information, see the `MockGuild` docstring. """ + spec_set = Syncer def __init__(self, **kwargs) -> None: - super().__init__(spec_set=Syncer, **kwargs) + super().__init__(**kwargs) -class SyncExtensionTests(unittest.TestCase): +class SyncExtensionTests(unittest.IsolatedAsyncioTestCase): """Tests for the sync extension.""" @staticmethod @@ -34,7 +35,7 @@ class SyncExtensionTests(unittest.TestCase): bot.add_cog.assert_called_once() -class SyncCogTestCase(unittest.TestCase): +class SyncCogTestCase(unittest.IsolatedAsyncioTestCase): """Base class for Sync cog tests. Sets up patches for syncers.""" def setUp(self): @@ -72,13 +73,13 @@ class SyncCogTestCase(unittest.TestCase): class SyncCogTests(SyncCogTestCase): """Tests for the Sync cog.""" - @mock.patch.object(sync.Sync, "sync_guild") + @mock.patch.object(sync.Sync, "sync_guild", new_callable=mock.MagicMock) def test_sync_cog_init(self, sync_guild): """Should instantiate syncers and run a sync for the guild.""" # Reset because a Sync cog was already instantiated in setUp. self.RoleSyncer.reset_mock() self.UserSyncer.reset_mock() - self.bot.loop.create_task.reset_mock() + self.bot.loop.create_task = mock.MagicMock() mock_sync_guild_coro = mock.MagicMock() sync_guild.return_value = mock_sync_guild_coro @@ -90,7 +91,6 @@ class SyncCogTests(SyncCogTestCase): sync_guild.assert_called_once_with() self.bot.loop.create_task.assert_called_once_with(mock_sync_guild_coro) - @helpers.async_test async def test_sync_cog_sync_guild(self): """Roles and users should be synced only if a guild is successfully retrieved.""" for guild in (helpers.MockGuild(), None): @@ -126,14 +126,12 @@ class SyncCogTests(SyncCogTestCase): json=updated_information, ) - @helpers.async_test async def test_sync_cog_patch_user(self): """A PATCH request should be sent and 404 errors ignored.""" for side_effect in (None, self.response_error(404)): with self.subTest(side_effect=side_effect): await self.patch_user_helper(side_effect) - @helpers.async_test async def test_sync_cog_patch_user_non_404(self): """A PATCH request should be sent and the error raised if it's not a 404.""" with self.assertRaises(ResponseCodeError): @@ -145,9 +143,8 @@ class SyncCogListenerTests(SyncCogTestCase): def setUp(self): super().setUp() - self.cog.patch_user = helpers.AsyncMock(spec_set=self.cog.patch_user) + self.cog.patch_user = mock.AsyncMock(spec_set=self.cog.patch_user) - @helpers.async_test async def test_sync_cog_on_guild_role_create(self): """A POST request should be sent with the new role's data.""" self.assertTrue(self.cog.on_guild_role_create.__cog_listener__) @@ -164,7 +161,6 @@ class SyncCogListenerTests(SyncCogTestCase): self.bot.api_client.post.assert_called_once_with("bot/roles", json=role_data) - @helpers.async_test async def test_sync_cog_on_guild_role_delete(self): """A DELETE request should be sent.""" self.assertTrue(self.cog.on_guild_role_delete.__cog_listener__) @@ -174,7 +170,6 @@ class SyncCogListenerTests(SyncCogTestCase): self.bot.api_client.delete.assert_called_once_with("bot/roles/99") - @helpers.async_test async def test_sync_cog_on_guild_role_update(self): """A PUT request should be sent if the colour, name, permissions, or position changes.""" self.assertTrue(self.cog.on_guild_role_update.__cog_listener__) @@ -212,7 +207,6 @@ class SyncCogListenerTests(SyncCogTestCase): else: self.bot.api_client.put.assert_not_called() - @helpers.async_test async def test_sync_cog_on_member_remove(self): """Member should patched to set in_guild as False.""" self.assertTrue(self.cog.on_member_remove.__cog_listener__) @@ -225,7 +219,6 @@ class SyncCogListenerTests(SyncCogTestCase): updated_information={"in_guild": False} ) - @helpers.async_test async def test_sync_cog_on_member_update_roles(self): """Members should be patched if their roles have changed.""" self.assertTrue(self.cog.on_member_update.__cog_listener__) @@ -240,7 +233,6 @@ class SyncCogListenerTests(SyncCogTestCase): data = {"roles": sorted(role.id for role in after_member.roles)} self.cog.patch_user.assert_called_once_with(after_member.id, updated_information=data) - @helpers.async_test async def test_sync_cog_on_member_update_other(self): """Members should not be patched if other attributes have changed.""" self.assertTrue(self.cog.on_member_update.__cog_listener__) @@ -262,7 +254,6 @@ class SyncCogListenerTests(SyncCogTestCase): self.cog.patch_user.assert_not_called() - @helpers.async_test async def test_sync_cog_on_user_update(self): """A user should be patched only if the name, discriminator, or avatar changes.""" self.assertTrue(self.cog.on_user_update.__cog_listener__) @@ -341,7 +332,6 @@ class SyncCogListenerTests(SyncCogTestCase): return data - @helpers.async_test async def test_sync_cog_on_member_join(self): """Should PUT user's data or POST it if the user doesn't exist.""" for side_effect in (None, self.response_error(404)): @@ -354,7 +344,6 @@ class SyncCogListenerTests(SyncCogTestCase): else: self.bot.api_client.post.assert_not_called() - @helpers.async_test async def test_sync_cog_on_member_join_non_404(self): """ResponseCodeError should be re-raised if status code isn't a 404.""" with self.assertRaises(ResponseCodeError): @@ -366,7 +355,6 @@ class SyncCogListenerTests(SyncCogTestCase): class SyncCogCommandTests(SyncCogTestCase, CommandTestCase): """Tests for the commands in the Sync cog.""" - @helpers.async_test async def test_sync_roles_command(self): """sync() should be called on the RoleSyncer.""" ctx = helpers.MockContext() @@ -374,7 +362,6 @@ class SyncCogCommandTests(SyncCogTestCase, CommandTestCase): self.cog.role_syncer.sync.assert_called_once_with(ctx.guild, ctx) - @helpers.async_test async def test_sync_users_command(self): """sync() should be called on the UserSyncer.""" ctx = helpers.MockContext() @@ -382,7 +369,7 @@ class SyncCogCommandTests(SyncCogTestCase, CommandTestCase): self.cog.user_syncer.sync.assert_called_once_with(ctx.guild, ctx) - def test_commands_require_admin(self): + async def test_commands_require_admin(self): """The sync commands should only run if the author has the administrator permission.""" cmds = ( self.cog.sync_group, @@ -392,4 +379,4 @@ class SyncCogCommandTests(SyncCogTestCase, CommandTestCase): for cmd in cmds: with self.subTest(cmd=cmd): - self.assertHasPermissionsCheck(cmd, {"administrator": True}) + await self.assertHasPermissionsCheck(cmd, {"administrator": True}) diff --git a/tests/bot/cogs/sync/test_roles.py b/tests/bot/cogs/sync/test_roles.py index 14fb2577a..79eee98f4 100644 --- a/tests/bot/cogs/sync/test_roles.py +++ b/tests/bot/cogs/sync/test_roles.py @@ -18,7 +18,7 @@ def fake_role(**kwargs): return kwargs -class RoleSyncerDiffTests(unittest.TestCase): +class RoleSyncerDiffTests(unittest.IsolatedAsyncioTestCase): """Tests for determining differences between roles in the DB and roles in the Guild cache.""" def setUp(self): @@ -39,7 +39,6 @@ class RoleSyncerDiffTests(unittest.TestCase): return guild - @helpers.async_test async def test_empty_diff_for_identical_roles(self): """No differences should be found if the roles in the guild and DB are identical.""" self.bot.api_client.get.return_value = [fake_role()] @@ -50,7 +49,6 @@ class RoleSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_diff_for_updated_roles(self): """Only updated roles should be added to the 'updated' set of the diff.""" updated_role = fake_role(id=41, name="new") @@ -63,7 +61,6 @@ class RoleSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_diff_for_new_roles(self): """Only new roles should be added to the 'created' set of the diff.""" new_role = fake_role(id=41, name="new") @@ -76,7 +73,6 @@ class RoleSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_diff_for_deleted_roles(self): """Only deleted roles should be added to the 'deleted' set of the diff.""" deleted_role = fake_role(id=61, name="deleted") @@ -89,7 +85,6 @@ class RoleSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_diff_for_new_updated_and_deleted_roles(self): """When roles are added, updated, and removed, all of them are returned properly.""" new = fake_role(id=41, name="new") @@ -109,14 +104,13 @@ class RoleSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) -class RoleSyncerSyncTests(unittest.TestCase): +class RoleSyncerSyncTests(unittest.IsolatedAsyncioTestCase): """Tests for the API requests that sync roles.""" def setUp(self): self.bot = helpers.MockBot() self.syncer = RoleSyncer(self.bot) - @helpers.async_test async def test_sync_created_roles(self): """Only POST requests should be made with the correct payload.""" roles = [fake_role(id=111), fake_role(id=222)] @@ -132,7 +126,6 @@ class RoleSyncerSyncTests(unittest.TestCase): self.bot.api_client.put.assert_not_called() self.bot.api_client.delete.assert_not_called() - @helpers.async_test async def test_sync_updated_roles(self): """Only PUT requests should be made with the correct payload.""" roles = [fake_role(id=111), fake_role(id=222)] @@ -148,7 +141,6 @@ class RoleSyncerSyncTests(unittest.TestCase): self.bot.api_client.post.assert_not_called() self.bot.api_client.delete.assert_not_called() - @helpers.async_test async def test_sync_deleted_roles(self): """Only DELETE requests should be made with the correct payload.""" roles = [fake_role(id=111), fake_role(id=222)] diff --git a/tests/bot/cogs/sync/test_users.py b/tests/bot/cogs/sync/test_users.py index 421bf6bb6..818883012 100644 --- a/tests/bot/cogs/sync/test_users.py +++ b/tests/bot/cogs/sync/test_users.py @@ -17,7 +17,7 @@ def fake_user(**kwargs): return kwargs -class UserSyncerDiffTests(unittest.TestCase): +class UserSyncerDiffTests(unittest.IsolatedAsyncioTestCase): """Tests for determining differences between users in the DB and users in the Guild cache.""" def setUp(self): @@ -42,7 +42,6 @@ class UserSyncerDiffTests(unittest.TestCase): return guild - @helpers.async_test async def test_empty_diff_for_no_users(self): """When no users are given, an empty diff should be returned.""" guild = self.get_guild() @@ -52,7 +51,6 @@ class UserSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_empty_diff_for_identical_users(self): """No differences should be found if the users in the guild and DB are identical.""" self.bot.api_client.get.return_value = [fake_user()] @@ -63,7 +61,6 @@ class UserSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_diff_for_updated_users(self): """Only updated users should be added to the 'updated' set of the diff.""" updated_user = fake_user(id=99, name="new") @@ -76,7 +73,6 @@ class UserSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_diff_for_new_users(self): """Only new users should be added to the 'created' set of the diff.""" new_user = fake_user(id=99, name="new") @@ -89,7 +85,6 @@ class UserSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_diff_sets_in_guild_false_for_leaving_users(self): """When a user leaves the guild, the `in_guild` flag is updated to `False`.""" leaving_user = fake_user(id=63, in_guild=False) @@ -102,7 +97,6 @@ class UserSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_diff_for_new_updated_and_leaving_users(self): """When users are added, updated, and removed, all of them are returned properly.""" new_user = fake_user(id=99, name="new") @@ -117,7 +111,6 @@ class UserSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) - @helpers.async_test async def test_empty_diff_for_db_users_not_in_guild(self): """When the DB knows a user the guild doesn't, no difference is found.""" self.bot.api_client.get.return_value = [fake_user(), fake_user(id=63, in_guild=False)] @@ -129,14 +122,13 @@ class UserSyncerDiffTests(unittest.TestCase): self.assertEqual(actual_diff, expected_diff) -class UserSyncerSyncTests(unittest.TestCase): +class UserSyncerSyncTests(unittest.IsolatedAsyncioTestCase): """Tests for the API requests that sync users.""" def setUp(self): self.bot = helpers.MockBot() self.syncer = UserSyncer(self.bot) - @helpers.async_test async def test_sync_created_users(self): """Only POST requests should be made with the correct payload.""" users = [fake_user(id=111), fake_user(id=222)] @@ -152,7 +144,6 @@ class UserSyncerSyncTests(unittest.TestCase): self.bot.api_client.put.assert_not_called() self.bot.api_client.delete.assert_not_called() - @helpers.async_test async def test_sync_updated_users(self): """Only PUT requests should be made with the correct payload.""" users = [fake_user(id=111), fake_user(id=222)] diff --git a/tests/helpers.py b/tests/helpers.py index 7ae7ed621..8e13f0f28 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -261,9 +261,7 @@ class MockAPIClient(CustomMockMixin, unittest.mock.MagicMock): Instances of this class will follow the specifications of `bot.api.APIClient` instances. For more information, see the `MockGuild` docstring. """ - - def __init__(self, **kwargs) -> None: - super().__init__(spec_set=APIClient, **kwargs) + spec_set = APIClient # Create a Bot instance to get a realistic MagicMock of `discord.ext.commands.Bot` -- cgit v1.2.3 From 2c85b2241bd8a1e7ca8290cd385cded97c54f9bb Mon Sep 17 00:00:00 2001 From: "S. Co1" Date: Tue, 3 Mar 2020 22:59:07 -0500 Subject: Update code for pep8-naming compliance --- tests/base.py | 4 ++-- tests/bot/cogs/sync/test_base.py | 2 +- tests/bot/cogs/test_snekbox.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests/base.py') diff --git a/tests/base.py b/tests/base.py index 42174e911..d99b9ac31 100644 --- a/tests/base.py +++ b/tests/base.py @@ -31,7 +31,7 @@ class LoggingTestsMixin: """ @contextmanager - def assertNotLogs(self, logger=None, level=None, msg=None): + def assertNotLogs(self, logger=None, level=None, msg=None): # noqa: N802 """ Asserts that no logs of `level` and higher were emitted by `logger`. @@ -81,7 +81,7 @@ class LoggingTestsMixin: class CommandTestCase(unittest.IsolatedAsyncioTestCase): """TestCase with additional assertions that are useful for testing Discord commands.""" - async def assertHasPermissionsCheck( + async def assertHasPermissionsCheck( # noqa: N802 self, cmd: commands.Command, permissions: Dict[str, bool], diff --git a/tests/bot/cogs/sync/test_base.py b/tests/bot/cogs/sync/test_base.py index fe0594efe..6ee9dfda6 100644 --- a/tests/bot/cogs/sync/test_base.py +++ b/tests/bot/cogs/sync/test_base.py @@ -84,7 +84,7 @@ class SyncerSendPromptTests(unittest.IsolatedAsyncioTestCase): method.assert_called_once_with(constants.Channels.dev_core) - async def test_send_prompt_returns_None_if_channel_fetch_fails(self): + async def test_send_prompt_returns_none_if_channel_fetch_fails(self): """None should be returned if there's an HTTPException when fetching the channel.""" self.bot.get_channel.return_value = None self.bot.fetch_channel.side_effect = discord.HTTPException(mock.MagicMock(), "test error!") diff --git a/tests/bot/cogs/test_snekbox.py b/tests/bot/cogs/test_snekbox.py index 9cd7f0154..fd9468829 100644 --- a/tests/bot/cogs/test_snekbox.py +++ b/tests/bot/cogs/test_snekbox.py @@ -89,15 +89,15 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): self.assertEqual(actual, expected) @patch('bot.cogs.snekbox.Signals', side_effect=ValueError) - def test_get_results_message_invalid_signal(self, mock_Signals: Mock): + def test_get_results_message_invalid_signal(self, mock_signals: Mock): self.assertEqual( self.cog.get_results_message({'stdout': '', 'returncode': 127}), ('Your eval job has completed with return code 127', '') ) @patch('bot.cogs.snekbox.Signals') - def test_get_results_message_valid_signal(self, mock_Signals: Mock): - mock_Signals.return_value.name = 'SIGTEST' + def test_get_results_message_valid_signal(self, mock_signals: Mock): + mock_signals.return_value.name = 'SIGTEST' self.assertEqual( self.cog.get_results_message({'stdout': '', 'returncode': 127}), ('Your eval job has completed with return code 127 (SIGTEST)', '') -- cgit v1.2.3