From 75c316f2d2c27845bcc7c6f1a7f2072c4f93c9fa Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 27 Nov 2022 00:42:05 +0100 Subject: [PATCH] Improved test architecture #139 --- kdb-bot/test/ui_tests/startup.py | 2 ++ kdb-bot/test/ui_tests/test_application.py | 30 +++++++++++++++++++ kdb-bot/test/ui_tests/test_on_ready_event.py | 8 +++-- .../command_test_case_with_app.py | 4 +-- kdb-bot/test/ui_tests_shared/declarations.py | 6 ++-- kdb-bot/test/ui_tests_shared/decorators.py | 16 ++++++---- .../ui_tests_shared/test_case_with_app.py | 14 ++++++++- kdb-bot/test/ui_tests_shared/ui.py | 1 + .../ui_tests_tests/cases/help_test_case.py | 19 ++++++++++++ .../ui_tests_tests/cases/ping_test_case.py | 6 ++-- 10 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 kdb-bot/test/ui_tests_tests/cases/help_test_case.py diff --git a/kdb-bot/test/ui_tests/startup.py b/kdb-bot/test/ui_tests/startup.py index 74330cff..4abb1cb1 100644 --- a/kdb-bot/test/ui_tests/startup.py +++ b/kdb-bot/test/ui_tests/startup.py @@ -9,6 +9,7 @@ from cpl_core.dependency_injection import ServiceProviderABC, ServiceCollectionA from cpl_core.environment import ApplicationEnvironment from cpl_discord import get_discord_collection from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum +from dotenv import load_dotenv from ui_tests.test_on_ready_event import TestOnReadyEvent @@ -21,6 +22,7 @@ class Startup(StartupABC): self._config: Optional[ConfigurationABC] = None def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC: + load_dotenv() configuration.add_environment_variables('KDB_TEST_') configuration.add_environment_variables('DISCORD_') diff --git a/kdb-bot/test/ui_tests/test_application.py b/kdb-bot/test/ui_tests/test_application.py index a0201a3a..b2c47037 100644 --- a/kdb-bot/test/ui_tests/test_application.py +++ b/kdb-bot/test/ui_tests/test_application.py @@ -1,9 +1,12 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceProviderABC from cpl_discord.application import DiscordBotApplicationABC +from cpl_discord.configuration import DiscordBotSettings from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslationSettings, TranslationServiceABC +from bot_core.configuration.bot_settings import BotSettings + class TestApplication(DiscordBotApplicationABC): @@ -16,6 +19,28 @@ class TestApplication(DiscordBotApplicationABC): self._bot: DiscordBotServiceABC = services.get_service(DiscordBotServiceABC) self._translation: TranslationServiceABC = services.get_service(TranslationServiceABC) + discord_bot_settings: DiscordBotSettings = config.get_configuration(DiscordBotSettings) + self._discord_settings = self._get_settings(discord_bot_settings) + + def _get_settings(self, settings_from_config: DiscordBotSettings) -> DiscordBotSettings: + new_settings = DiscordBotSettings() + token = None if settings_from_config is None else settings_from_config.token + prefix = None if settings_from_config is None else settings_from_config.prefix + env_token = self._config.get_configuration('TOKEN') + env_prefix = self._config.get_configuration('PREFIX') + + new_settings.from_dict({ + 'Token': env_token if token is None or token == '' else token, + 'Prefix': ('! ' if self._is_string_invalid(env_prefix) else env_prefix) if self._is_string_invalid(prefix) else prefix + }) + if new_settings.token is None or new_settings.token == '': + raise Exception('You have to configure discord token by appsettings or environment variables') + return new_settings + + @staticmethod + def _is_string_invalid(x): + return x is None or x == '' + @property def config(self) -> ConfigurationABC: return self._config @@ -28,6 +53,11 @@ class TestApplication(DiscordBotApplicationABC): self._translation.load_by_settings(self._configuration.get_configuration(TranslationSettings)) async def main(self): + if self._config.get_configuration('IS_UNITTEST'): + await self._bot.login(self._discord_settings.token) + self._bot.loop.create_task(self._bot.connect()) + return + await self._bot.start_async() async def stop_async(self): diff --git a/kdb-bot/test/ui_tests/test_on_ready_event.py b/kdb-bot/test/ui_tests/test_on_ready_event.py index d2c5080d..13881912 100644 --- a/kdb-bot/test/ui_tests/test_on_ready_event.py +++ b/kdb-bot/test/ui_tests/test_on_ready_event.py @@ -1,6 +1,7 @@ import os import unittest +from cpl_core.configuration import ConfigurationABC from cpl_core.console import Console, ForegroundColorEnum from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.logging import LoggerABC @@ -15,7 +16,7 @@ class TestOnReadyEvent(OnReadyABC): def __init__( self, - logger: LoggerABC, + config: ConfigurationABC, bot: DiscordBotServiceABC, services: ServiceProviderABC, client_utils: ClientUtilsServiceABC, @@ -23,13 +24,16 @@ class TestOnReadyEvent(OnReadyABC): ): OnReadyABC.__init__(self) - self._logger = logger + self._config = config self._bot = bot self._services = services self._client_utils = client_utils self._t = t async def on_ready(self): + if self._config.get_configuration('IS_UNITTEST'): + return + Console.write_line('\nStarting tests:\n') loader = unittest.TestLoader() path = f'{os.path.dirname(os.path.realpath(__file__))}/../' diff --git a/kdb-bot/test/ui_tests_shared/command_test_case_with_app.py b/kdb-bot/test/ui_tests_shared/command_test_case_with_app.py index 576a1c69..bf5789a7 100644 --- a/kdb-bot/test/ui_tests_shared/command_test_case_with_app.py +++ b/kdb-bot/test/ui_tests_shared/command_test_case_with_app.py @@ -20,7 +20,6 @@ class CommandTestCaseWithApp(TestCaseWithApp): _bot: Optional[DiscordBotServiceABC] = None _t: Optional[TranslatePipe] = None - @classmethod def setUpClass(cls): TestCaseWithApp.setUpClass() @@ -45,4 +44,5 @@ class CommandTestCaseWithApp(TestCaseWithApp): )) ).click() time.sleep(2) - UI.driver.find_element(By.XPATH, '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div/main/form/div/div[2]/div/div[2]/div/div').send_keys(Keys.ENTER) + UI.driver.find_element(By.XPATH, '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div/main/form/div/div[2]/div/div[2]/div/div').send_keys( + Keys.ENTER) diff --git a/kdb-bot/test/ui_tests_shared/declarations.py b/kdb-bot/test/ui_tests_shared/declarations.py index c176d10a..3e8cec03 100644 --- a/kdb-bot/test/ui_tests_shared/declarations.py +++ b/kdb-bot/test/ui_tests_shared/declarations.py @@ -7,6 +7,6 @@ from ui_tests_shared.configuration.test_settings import TestSettings class Declarations: - app: Optional[TestApplication] - env: Optional[ApplicationEnvironmentABC] - test_settings: Optional[TestSettings] + app: Optional[TestApplication] = None + env: Optional[ApplicationEnvironmentABC] = None + test_settings: Optional[TestSettings] = None diff --git a/kdb-bot/test/ui_tests_shared/decorators.py b/kdb-bot/test/ui_tests_shared/decorators.py index f4530baa..b0359204 100644 --- a/kdb-bot/test/ui_tests_shared/decorators.py +++ b/kdb-bot/test/ui_tests_shared/decorators.py @@ -1,9 +1,15 @@ import asyncio -def async_test(coro): - def wrapper(*args, **kwargs): - loop = asyncio.get_running_loop() - return loop.run_until_complete(coro(*args, **kwargs)) +class Async: + _loop = None - return wrapper + @classmethod + def test(cls, coro): + def wrapper(*args, **kwargs): + if cls._loop is None: + cls._loop = asyncio.get_event_loop() + + return cls._loop.run_until_complete(coro(*args, **kwargs)) + + return wrapper diff --git a/kdb-bot/test/ui_tests_shared/test_case_with_app.py b/kdb-bot/test/ui_tests_shared/test_case_with_app.py index 96f92903..b2fdc67a 100644 --- a/kdb-bot/test/ui_tests_shared/test_case_with_app.py +++ b/kdb-bot/test/ui_tests_shared/test_case_with_app.py @@ -1,3 +1,5 @@ +import asyncio +import time import unittest from typing import Optional @@ -18,10 +20,20 @@ class TestCaseWithApp(unittest.TestCase): @classmethod def setUpClass(cls): if Declarations.app is None: - get_app() + app = get_app() + import nest_asyncio + + nest_asyncio.apply() + asyncio.run(app.run_async()) cls._config = Declarations.app.config cls._services = Declarations.app.services cls._test_settings: TestSettings = cls._config.get_configuration(TestSettings) UI.set_settings(cls._test_settings) UI.login(cls._config.get_configuration('DISCORD_MAIL'), cls._config.get_configuration('DISCORD_PASSWORD')) + + @classmethod + def tearDownClass(cls): + if cls._config.get_configuration('IS_UNITTEST'): + loop = asyncio.get_event_loop() + loop.run_until_complete(Declarations.app.stop_async()) diff --git a/kdb-bot/test/ui_tests_shared/ui.py b/kdb-bot/test/ui_tests_shared/ui.py index 3d4f2bb3..3dd3a383 100644 --- a/kdb-bot/test/ui_tests_shared/ui.py +++ b/kdb-bot/test/ui_tests_shared/ui.py @@ -54,3 +54,4 @@ class UI: WebDriverWait(cls.driver, 20).until(expected_conditions.url_matches(cls._test_settings.me_page_url)) time.sleep(4) + cls._is_logged_in = True diff --git a/kdb-bot/test/ui_tests_tests/cases/help_test_case.py b/kdb-bot/test/ui_tests_tests/cases/help_test_case.py new file mode 100644 index 00000000..9a6f302b --- /dev/null +++ b/kdb-bot/test/ui_tests_tests/cases/help_test_case.py @@ -0,0 +1,19 @@ +import discord + +from ui_tests_shared.command_test_case_with_app import CommandTestCaseWithApp +from ui_tests_shared.decorators import Async + + +class HelpTestCase(CommandTestCaseWithApp): + + @Async.test + async def test_help(self): + correct_response = 'https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle' + self.send_command('help') + + def check(m: discord.Message): + return m.content == correct_response and m.author.id == 998159802393964594 + + response = await self._bot.wait_for('message', check=check) + self.assertEqual(response.content, correct_response) + await self._bot.close() diff --git a/kdb-bot/test/ui_tests_tests/cases/ping_test_case.py b/kdb-bot/test/ui_tests_tests/cases/ping_test_case.py index 444db530..72575253 100644 --- a/kdb-bot/test/ui_tests_tests/cases/ping_test_case.py +++ b/kdb-bot/test/ui_tests_tests/cases/ping_test_case.py @@ -1,14 +1,12 @@ -import unittest - import discord from ui_tests_shared.command_test_case_with_app import CommandTestCaseWithApp -from ui_tests_shared.decorators import async_test +from ui_tests_shared.decorators import Async class PingTestCase(CommandTestCaseWithApp): - @async_test + @Async.test async def test_ping(self): correct_response = self._t.transform('modules.base.pong') self.assertIsNotNone(correct_response)