From 781e885ffbc53fc459fb5a5c0c198655008a3fdb Mon Sep 17 00:00:00 2001 From: "Nick V. Anokhin" Date: Mon, 20 Jul 2026 12:05:57 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20CoreCommand?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- net/xeaf/rack/core/core_command.py | 13 +- .../rack/tests/core/_core_command_mocks.py | 25 ++ net/xeaf/rack/tests/core/_core_enum_mocks.py | 2 +- .../core/core_command_clear_database_tests.py | 40 +++ .../core_command_is_debug_or_test_tests.py | 177 ++++++++++ .../core_command_report_int_value_tests.py | 317 ++++++++++++++++++ .../core_command_report_separator_tests.py | 229 +++++++++++++ testing_2_llm.md | 16 +- testing_llm.md | 11 + 9 files changed, 823 insertions(+), 7 deletions(-) create mode 100644 net/xeaf/rack/tests/core/_core_command_mocks.py create mode 100644 net/xeaf/rack/tests/core/core_command_clear_database_tests.py create mode 100644 net/xeaf/rack/tests/core/core_command_is_debug_or_test_tests.py create mode 100644 net/xeaf/rack/tests/core/core_command_report_int_value_tests.py create mode 100644 net/xeaf/rack/tests/core/core_command_report_separator_tests.py diff --git a/net/xeaf/rack/core/core_command.py b/net/xeaf/rack/core/core_command.py index b6d148d..eeefc51 100644 --- a/net/xeaf/rack/core/core_command.py +++ b/net/xeaf/rack/core/core_command.py @@ -68,7 +68,14 @@ class CoreCommand(BaseCommand): :param length Общая длина """ - p_len = len(str(value)) + len(name) + 2 - points = "." * (length - p_len) + if not name: + p_len = length - len(str(value)) - 1 + else: + p_len = length - len(str(value)) - len(name) - 2 - print(f"{name} {points} {value}") + if p_len <= 0: + points = "" + else: + points = " " + ("." * p_len) + " " + + print(f"{name}{points}{value}") diff --git a/net/xeaf/rack/tests/core/_core_command_mocks.py b/net/xeaf/rack/tests/core/_core_command_mocks.py new file mode 100644 index 0000000..668f6dc --- /dev/null +++ b/net/xeaf/rack/tests/core/_core_command_mocks.py @@ -0,0 +1,25 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание mock-классов для тестов CoreCommand +""" + +from net.xeaf.rack.core import CoreCommand + + +class TestCommand(CoreCommand): + """ + Тестовая реализация команды для проверки базовых методов вывода отчетов + """ + + def handle(self, *args, **options): + """ + Обязательный метод для Django BaseCommand + """ + + pass + diff --git a/net/xeaf/rack/tests/core/_core_enum_mocks.py b/net/xeaf/rack/tests/core/_core_enum_mocks.py index bd97aec..489c6ef 100644 --- a/net/xeaf/rack/tests/core/_core_enum_mocks.py +++ b/net/xeaf/rack/tests/core/_core_enum_mocks.py @@ -5,7 +5,7 @@ # Все права защищены. Лицензия: MIT """ -Описание mock-классов для CoreEnumChoicesTests +Описание mock-классов для тестов CoreEnum """ from net.xeaf.rack.core import CoreEnum diff --git a/net/xeaf/rack/tests/core/core_command_clear_database_tests.py b/net/xeaf/rack/tests/core/core_command_clear_database_tests.py new file mode 100644 index 0000000..ec55069 --- /dev/null +++ b/net/xeaf/rack/tests/core/core_command_clear_database_tests.py @@ -0,0 +1,40 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CoreCommandClearDatabaseTests +""" + +from net.xeaf.rack.core.testing import CoreUserStoryTestCase +from net.xeaf.rack.models.user_story import AccountTestData +from ._core_command_mocks import TestCommand + + +class CoreCommandClearDatabaseTests(CoreUserStoryTestCase): + """ + Тестовый класс для CoreCommand._clear_database() + """ + + def test_clear_database_removes_all_user_stories_from_db(self): + """ + Тест проверяет, что после вызова _clear_database известные объекты User Story удаляются из БД + """ + + # Arrange + command_instance = TestCommand() + + # Проверяем, что перед тестом база наполнена (Иванов существует в БД) + ivanov_id = AccountTestData.IVANOV_ID + account_model = AccountTestData.get_ivanov_account().__class__ + self.assertTrue(account_model.objects.filter(id=ivanov_id).exists()) + + # Act + command_instance._clear_database() + + # Assert + # Проверяем реальное состояние БД: объектов больше быть не должно + self.assertFalse(account_model.objects.filter(id=ivanov_id).exists()) + self.assertEqual(account_model.objects.count(), 0) diff --git a/net/xeaf/rack/tests/core/core_command_is_debug_or_test_tests.py b/net/xeaf/rack/tests/core/core_command_is_debug_or_test_tests.py new file mode 100644 index 0000000..581a0db --- /dev/null +++ b/net/xeaf/rack/tests/core/core_command_is_debug_or_test_tests.py @@ -0,0 +1,177 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CoreCommandIsDebugOrTestTests +""" + +import sys + +from django.conf import settings + +from net.xeaf.rack.core import CoreCommand +from net.xeaf.rack.core.testing import CoreSimpleTestCase + + +class CoreCommandIsDebugOrTestTests(CoreSimpleTestCase): + """ + Тестовый класс для CoreCommand._is_debug_or_test() + """ + + def test_is_debug_or_test_returns_true_when_debug_enabled(self): + """ + Тест проверяет, что метод возвращает True при включенном DEBUG + """ + + # Arrange + original_debug = settings.DEBUG + settings.DEBUG = True + original_argv = sys.argv.copy() + + try: + # Act + result = CoreCommand._is_debug_or_test() + + # Assert + self.assertTrue(result) + finally: + # Cleanup + settings.DEBUG = original_debug + sys.argv = original_argv + + def test_is_debug_or_test_returns_true_when_test_in_argv(self): + """ + Тест проверяет, что метод возвращает True, когда в аргументах командной строки присутствует 'test' + """ + + # Arrange + original_debug = settings.DEBUG + settings.DEBUG = False + original_argv = sys.argv.copy() + sys.argv = ['manage.py', 'test'] + + try: + # Act + result = CoreCommand._is_debug_or_test() + + # Assert + self.assertTrue(result) + finally: + # Cleanup + settings.DEBUG = original_debug + sys.argv = original_argv + + def test_is_debug_or_test_returns_false_when_debug_disabled_and_test_not_in_argv(self): + """ + Тест проверяет, что метод возвращает False, когда DEBUG выключен и 'test' отсутствует в аргументах + """ + + # Arrange + original_debug = settings.DEBUG + settings.DEBUG = False + original_argv = sys.argv.copy() + sys.argv = ['manage.py', 'runserver'] + + try: + # Act + result = CoreCommand._is_debug_or_test() + + # Assert + self.assertFalse(result) + finally: + # Cleanup + settings.DEBUG = original_debug + sys.argv = original_argv + + def test_is_debug_or_test_returns_true_when_debug_enabled_and_test_in_argv(self): + """ + Тест проверяет, что метод возвращает True, когда и DEBUG включен, и 'test' присутствует в аргументах + """ + + # Arrange + original_debug = settings.DEBUG + settings.DEBUG = True + original_argv = sys.argv.copy() + sys.argv = ['manage.py', 'test', '--verbosity=2'] + + try: + # Act + result = CoreCommand._is_debug_or_test() + + # Assert + self.assertTrue(result) + finally: + # Cleanup + settings.DEBUG = original_debug + sys.argv = original_argv + + def test_is_debug_or_test_handles_test_substring_correctly(self): + """ + Тест проверяет, что метод правильно обрабатывает наличие подстроки 'test' в аргументах + """ + + # Arrange + original_debug = settings.DEBUG + settings.DEBUG = False + original_argv = sys.argv.copy() + sys.argv = ['manage.py', 'testing_tool'] + + try: + # Act + result = CoreCommand._is_debug_or_test() + + # Assert + # Метод должен проверять точное вхождение 'test', а не подстроку + self.assertFalse(result) + finally: + # Cleanup + settings.DEBUG = original_debug + sys.argv = original_argv + + def test_is_debug_or_test_returns_false_with_empty_argv(self): + """ + Тест проверяет поведение метода при пустом списке аргументов + """ + + # Arrange + original_debug = settings.DEBUG + settings.DEBUG = False + original_argv = sys.argv.copy() + sys.argv = [] + + try: + # Act + result = CoreCommand._is_debug_or_test() + + # Assert + self.assertFalse(result) + finally: + # Cleanup + settings.DEBUG = original_debug + sys.argv = original_argv + + def test_is_debug_or_test_handles_nonexistent_settings_attribute(self): + """ + Тест проверяет, что метод корректно обрабатывает отсутствие атрибута DEBUG в настройках + """ + + # Arrange + original_debug = getattr(settings, 'DEBUG', None) + if hasattr(settings, 'DEBUG'): + delattr(settings, 'DEBUG') + original_argv = sys.argv.copy() + sys.argv = ['manage.py', 'runserver'] + + try: + # Act & Assert + # В этом случае должно возникнуть исключение, так как settings.DEBUG отсутствует + with self.assertRaises(AttributeError): + CoreCommand._is_debug_or_test() + finally: + # Cleanup + if original_debug is not None: + settings.DEBUG = original_debug + sys.argv = original_argv diff --git a/net/xeaf/rack/tests/core/core_command_report_int_value_tests.py b/net/xeaf/rack/tests/core/core_command_report_int_value_tests.py new file mode 100644 index 0000000..094dffb --- /dev/null +++ b/net/xeaf/rack/tests/core/core_command_report_int_value_tests.py @@ -0,0 +1,317 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CoreCommandReportIntValueTests +""" + +import sys +from io import StringIO + +from net.xeaf.rack.core import CoreCommand +from net.xeaf.rack.core.testing import CoreSimpleTestCase + + +class CoreCommandReportIntValueTests(CoreSimpleTestCase): + """ + Тестовый класс для CoreCommand._report_int_value() + """ + + def setUp(self): + """ + Подготовка тестовой среды: перехват stdout для проверки вывода + """ + + super().setUp() + self.stdout_capture = StringIO() + self.original_stdout = sys.stdout + sys.stdout = self.stdout_capture + + def tearDown(self): + """ + Восстановление stdout после каждого теста + """ + + sys.stdout = self.original_stdout + super().tearDown() + + def test_report_int_value_with_default_length(self): + """ + Тест проверяет вывод целочисленного значения с длиной по умолчанию + """ + + # Arrange + name = "Total records" + value = 42 + expected_length = 70 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn(str(value), output) + self.assertTrue(output.endswith(str(value))) + # Проверяем, что общая длина строки соответствует ожидаемой + # (без учета возможных пробелов в конце) + self.assertEqual(len(output), expected_length) + + def test_report_int_value_with_custom_length(self): + """ + Тест проверяет вывод целочисленного значения с пользовательской длиной + """ + + # Arrange + name = "Count" + value = 100 + custom_length = 50 + + # Act + CoreCommand._report_int_value(name, value, custom_length) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn(str(value), output) + self.assertEqual(len(output), custom_length) + + def test_report_int_value_with_small_value(self): + """ + Тест проверяет вывод маленького целочисленного значения + """ + + # Arrange + name = "Errors" + value = 0 + expected_length = 70 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn("0", output) + self.assertEqual(len(output), expected_length) + + def test_report_int_value_with_large_value(self): + """ + Тест проверяет вывод большого целочисленного значения + """ + + # Arrange + name = "Processed items" + value = 1234567890 + expected_length = 70 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn(str(value), output) + self.assertEqual(len(output), expected_length) + + def test_report_int_value_with_negative_value(self): + """ + Тест проверяет вывод отрицательного целочисленного значения + """ + + # Arrange + name = "Balance" + value = -50 + expected_length = 70 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn("-50", output) + self.assertEqual(len(output), expected_length) + + def test_report_int_value_with_empty_name(self): + """ + Тест проверяет вывод значения с пустым названием + """ + + # Arrange + name = "" + value = 42 + expected_length = 70 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(str(value), output) + # При пустом имени будут только точки и значение + self.assertTrue(output.startswith(".")) + self.assertEqual(len(output), expected_length) + + def test_report_int_value_with_long_name(self): + """ + Тест проверяет вывод значения с длинным названием + """ + + # Arrange + name = "This is a very long name that should be truncated or handled" + value = 42 + expected_length = 70 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn(str(value), output) + self.assertEqual(len(output), expected_length) + + def test_report_int_value_with_minimal_length(self): + """ + Тест проверяет вывод значения с минимальной длиной + """ + + # Arrange + name = "X" + value = 1 + minimal_length = 5 # Должно быть достаточно для "X ... 1" + + # Act + CoreCommand._report_int_value(name, value, minimal_length) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn(str(value), output) + self.assertEqual(len(output), minimal_length) + + def test_report_int_value_with_length_smaller_than_needed(self): + """ + Тест проверяет поведение при длине меньше необходимой для отображения + """ + + # Arrange + name = "Test" + value = 12345 + too_small_length = 10 + + # Act + CoreCommand._report_int_value(name, value, too_small_length) + + # Assert + output = self.stdout_capture.getvalue().strip() + # Метод должен обработать ситуацию без ошибок + # Возможно, обрежет название или значение + self.assertIsInstance(output, str) + self.assertLessEqual(len(output), too_small_length) + + def test_report_int_value_with_zero_length(self): + """ + Тест проверяет поведение при нулевой длине + """ + + # Arrange + name = "Test" + value = 42 + zero_length = 0 + + # Act + CoreCommand._report_int_value(name, value, zero_length) + + # Assert + output = self.stdout_capture.getvalue().strip() + # При нулевой длине вывод должен быть пустым или минимальным + self.assertIsInstance(output, str) + + def test_report_int_value_format_with_dots(self): + """ + Тест проверяет, что между названием и значением выводятся точки + """ + + # Arrange + name = "Records" + value = 100 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + # Проверяем, что между названием и значением есть точки + name_end_index = output.find(name) + len(name) + value_start_index = output.rfind(str(value)) + middle_part = output[name_end_index:value_start_index] + self.assertTrue(all(c == '.' for c in middle_part.replace(' ', ''))) + + def test_report_int_value_with_multiple_calls(self): + """ + Тест проверяет последовательный вывод нескольких значений + """ + + # Arrange + test_data = [ + ("First", 100), + ("Second", 200), + ("Third", 300) + ] + + # Act + for name, value in test_data: + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue() + lines = output.strip().split('\n') + self.assertEqual(len(lines), len(test_data)) + + for i, (name, value) in enumerate(test_data): + self.assertIn(name, lines[i]) + self.assertIn(str(value), lines[i]) + self.assertEqual(len(lines[i]), 70) + + def test_report_int_value_with_unicode_characters(self): + """ + Тест проверяет вывод значения с названием, содержащим Unicode-символы + """ + + # Arrange + name = "Количество записей" + value = 42 + expected_length = 70 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn(str(value), output) + self.assertEqual(len(output), expected_length) + + def test_report_int_value_with_maximum_integer(self): + """ + Тест проверяет вывод максимального целочисленного значения + """ + + # Arrange + name = "Max int" + value = sys.maxsize + expected_length = 70 + + # Act + CoreCommand._report_int_value(name, value) + + # Assert + output = self.stdout_capture.getvalue().strip() + self.assertIn(name, output) + self.assertIn(str(value), output) + self.assertEqual(len(output), expected_length) diff --git a/net/xeaf/rack/tests/core/core_command_report_separator_tests.py b/net/xeaf/rack/tests/core/core_command_report_separator_tests.py new file mode 100644 index 0000000..d27b4f4 --- /dev/null +++ b/net/xeaf/rack/tests/core/core_command_report_separator_tests.py @@ -0,0 +1,229 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CoreCommandReportSeparatorTests +""" + +import sys +from io import StringIO + +from net.xeaf.rack.core import CoreCommand +from net.xeaf.rack.core.testing import CoreSimpleTestCase + + +class CoreCommandReportSeparatorTests(CoreSimpleTestCase): + """ + Тестовый класс для CoreCommand._report_separator() + """ + + def setUp(self): + """ + Подготовка тестовой среды: перехват stdout для проверки вывода + """ + super().setUp() + self.stdout_capture = StringIO() + self.original_stdout = sys.stdout + sys.stdout = self.stdout_capture + + def tearDown(self): + """ + Восстановление stdout после каждого теста + """ + sys.stdout = self.original_stdout + super().tearDown() + + def test_report_separator_with_default_parameters(self): + """ + Тест проверяет вывод разделителя с параметрами по умолчанию + """ + # Arrange + expected_length = 70 + expected_separator = '=' * expected_length + + # Act + CoreCommand._report_separator() + + # Assert + output = self.stdout_capture.getvalue() + self.assertIn(expected_separator, output) + self.assertEqual(output.count('\n'), 1) # Только одна строка с разделителем + + def test_report_separator_with_custom_length(self): + """ + Тест проверяет вывод разделителя с пользовательской длиной + """ + # Arrange + custom_length = 50 + expected_separator = '=' * custom_length + + # Act + CoreCommand._report_separator(length=custom_length) + + # Assert + output = self.stdout_capture.getvalue() + self.assertIn(expected_separator, output) + self.assertEqual(len(expected_separator), custom_length) + + def test_report_separator_with_single_line(self): + """ + Тест проверяет вывод одинарного разделителя + """ + # Arrange + expected_separator = '-' * 70 + + # Act + CoreCommand._report_separator(double=False) + + # Assert + output = self.stdout_capture.getvalue() + self.assertIn(expected_separator, output) + self.assertNotIn('=' * 70, output) + + def test_report_separator_with_double_line(self): + """ + Тест проверяет вывод двойного разделителя (поведение по умолчанию) + """ + # Arrange + expected_separator = '=' * 70 + + # Act + CoreCommand._report_separator(double=True) + + # Assert + output = self.stdout_capture.getvalue() + self.assertIn(expected_separator, output) + + def test_report_separator_with_first_new_line(self): + """ + Тест проверяет добавление пустой строки перед разделителем + """ + # Arrange + expected_separator = '=' * 70 + + # Act + CoreCommand._report_separator(first_new_line=True) + + # Assert + output = self.stdout_capture.getvalue() + self.assertTrue(output.startswith('\n')) + self.assertIn(expected_separator, output) + + def test_report_separator_with_last_new_line(self): + """ + Тест проверяет добавление пустой строки после разделителя + """ + # Arrange + expected_separator = '=' * 70 + + # Act + CoreCommand._report_separator(last_new_line=True) + + # Assert + output = self.stdout_capture.getvalue() + self.assertIn(expected_separator, output) + self.assertTrue(output.endswith('\n')) + + def test_report_separator_with_both_new_lines(self): + """ + Тест проверяет добавление пустых строк до и после разделителя + """ + # Arrange + expected_separator = '=' * 70 + + # Act + CoreCommand._report_separator(first_new_line=True, last_new_line=True) + + # Assert + output = self.stdout_capture.getvalue() + self.assertTrue(output.startswith('\n')) + self.assertTrue(output.endswith('\n')) + self.assertIn(expected_separator, output) + + def test_report_separator_with_minimal_length(self): + """ + Тест проверяет вывод разделителя минимальной длины + """ + # Arrange + minimal_length = 1 + expected_separator = '=' * minimal_length + + # Act + CoreCommand._report_separator(length=minimal_length) + + # Assert + output = self.stdout_capture.getvalue() + self.assertIn(expected_separator, output) + self.assertEqual(len(expected_separator), minimal_length) + + def test_report_separator_with_large_length(self): + """ + Тест проверяет вывод разделителя большой длины + """ + # Arrange + large_length = 200 + expected_separator = '=' * large_length + + # Act + CoreCommand._report_separator(length=large_length) + + # Assert + output = self.stdout_capture.getvalue() + self.assertIn(expected_separator, output) + self.assertEqual(len(expected_separator), large_length) + + def test_report_separator_combination_all_parameters(self): + """ + Тест проверяет комбинацию всех параметров + """ + # Arrange + custom_length = 60 + expected_separator = '-' * custom_length + + # Act + CoreCommand._report_separator( + length=custom_length, + double=False, + first_new_line=True, + last_new_line=True + ) + + # Assert + output = self.stdout_capture.getvalue() + self.assertTrue(output.startswith('\n')) + self.assertTrue(output.endswith('\n')) + self.assertIn(expected_separator, output) + self.assertNotIn('=' * custom_length, output) + + def test_report_separator_no_output_with_zero_length(self): + """ + Тест проверяет поведение при нулевой длине разделителя + """ + + # Act + CoreCommand._report_separator(length=0) + + # Assert + output = self.stdout_capture.getvalue() + # При длине 0 разделитель не выводится, но пустые строки могут быть + self.assertNotIn('=' * 70, output) + self.assertNotIn('-' * 70, output) + + def test_report_separator_handles_negative_length(self): + """ + Тест проверяет поведение при отрицательной длине разделителя + """ + # Arrange + negative_length = -10 + + # Act + CoreCommand._report_separator(length=negative_length) + + # Assert + output = self.stdout_capture.getvalue() + # Отрицательная длина вызовет ошибку или приведет к пустой строке + # В зависимости от реализации, но метод должен отработать без ошибок + self.assertIsInstance(output, str) diff --git a/testing_2_llm.md b/testing_2_llm.md index a20cf88..f44dac3 100644 --- a/testing_2_llm.md +++ b/testing_2_llm.md @@ -42,11 +42,21 @@ ``` Для конструктора паттерн аналогичен: `Тестовый класс для DemoClass.__init__()`. - Оформление тестовых методов: Докстринг внутри каждого тестового метода должен быть многострочным, оформленным с пустыми строками до и после текста. -- СТРОГОЕ ПРАВИЛО ИМПОРТОВ: Все импорты должны располагаться только в самом начале файла. Импорты внутри классов или методов не допускаются. -- Тестирование исключений: При проверке исключений обязательно проверяй не только факт выброса ошибки, но и наличие ключевых слов в тексте сообщения через менеджер контекста `with self.assertRaises(ValueError) as context:`. +- СТРОГОЕ ПРАВИЛО ИМПОРТОВ: Все импорты (включая автоматический импорт только нужных для этого метода классов `Test...` из локального файла моков) должны располагаться только в самом начале файла. Импорты внутри классов или методов не допускаются. + +- МЕТОДОЛОГИЯ AAA (Arrange, Act, Assert): Внутри тестовых методов код должен быть строго разделен на три логических блока с помощью комментариев `# Arrange`, `# Act` и `# Assert`. Покрывай как позитивные, так и негативные сценарии (invalid data, неавторизованный доступ, отсутствие прав, крайние состояния, деструктивные действия с данными, если применимо). + +- ТЕСТИРОВАНИЕ ИСКЛЮЧЕНИЙ: При проверке исключений обязательно проверяй не только сам факт выброса ошибки, но и наличие ключевых слов в тексте сообщения об ошибке через менеджер контекста. Пример оформления: + ```python + # Act & Assert + with self.assertRaises(ValueError) as context: + TestEmptyEnum.choices() + self.assertIn("error message content", str(context.exception)) + ``` + - Формат ответа: Возвращай ТОЛЬКО чистый код на Python внутри одного блока разметки ```python. Без вводных фраз и пояснений после кода. ### АЛГОРИТМ ТВОЕЙ РАБОТЫ Пользователь пришлет тебе команду. - Если это КОМАНДА 1, сгенерируй полный mock-файл и запомни структуру созданных моков. -- Если это КОМАНДА 2, проанализируй целевой метод (с учетом исключения для `__init__`), вспомни структуру моков из первого шага, автоматически выбери подходящие классы `Test...`, импортируй их из локального mock-пакета в начале файла и сгенерируй изолированный тестовый класс по методологии AAA. +- Если это КОМАНДА 2, проанализируй целевой метод (с учетом исключения для `__init__`), вспомни структуру моков из первого шага, автоматически выбери подходящие классы `Test...`, импортируй их из локального mock-пакета в начале файла и сгенерируй изолированный тестовый класс, строго следуя методологии AAA и правилам проверки исключений. diff --git a/testing_llm.md b/testing_llm.md index 40bd747..997f5f5 100644 --- a/testing_llm.md +++ b/testing_llm.md @@ -49,6 +49,17 @@ - Оформление вспомогательных/служебных классов (моков): Все вспомогательные классы для теста должны быть описаны в начале файла, строго ПОСЛЕ импортов, но ДО основного класса тестов. Описывать их внутри методов запрещено. - Именование вспомогательных классов: Все они обязаны иметь префикс `Test...` (например, `TestEnum`, `TestEmptyEnum`, `TestMockSerializer`). Каждый такой класс должен содержать docstring на русском языке с описанием его назначения. - СТРОГОЕ ПРАВИЛО ИМПОРТОВ: Все импорты должны располагаться только в самом начале файла. Импорты внутри классов или методов не допускаются. + +- МЕТОДОЛОГИЯ AAA (Arrange, Act, Assert): Внутри тестовых методов код должен быть строго разделен на три логических блока с помощью комментариев `# Arrange`, `# Act` и `# Assert`. Покрывай как позитивные, так и негативные сценарии (invalid data, неавторизованный доступ, отсутствие прав, крайние состояния, деструктивные действия с данными, если применимо). + +- ТЕСТИРОВАНИЕ ИСКЛЮЧЕНИЙ: При проверке исключений обязательно проверяй не только сам факт выброса ошибки, но и наличие ключевых слов в тексте сообщения об ошибке через менеджер контекста. Пример оформления: + ```python + # Act & Assert + with self.assertRaises(ValueError) as context: + TestEmptyEnum.choices() + self.assertIn("error message content", str(context.exception)) + ``` + - Тестирование исключений: При проверке исключений обязательно проверяй не только сам факт выброса ошибки, но и наличие ключевых слов в тексте сообщения об ошибке через менеджер контекста. Пример: ```python with self.assertRaises(ValueError) as context: