Создание подкласса TestCase в Python: перезапись поля в родительском TestCase

Я пишу интеграционные тесты для приложения Alexa.

В нашем приложении используется шаблон «контроллер-запрос-ответ». Контроллер получает запрос с указанным намерением и переменными сеанса, направляет запрос функциям, которые выполняют некоторые вычисления с переменными сеанса, и возвращает объект ответа с результатами этого вычисления.

Мы получаем правильное поведение от UnhandledIntentTestCase в том, что касается test_for_smoke. Однако test_returning_reprompt_text никогда не срабатывает, потому что return_reprompt_text никогда не перезаписывается.

Может кто-нибудь объяснить, как я могу перезаписать его в родительском классе и / или как правильное имя намерения передается объекту запроса в setUpClass?

intent_base_case.py

import unittest

import mycity.intents.intent_constants as intent_constants
import mycity.mycity_controller as mcc
import mycity.mycity_request_data_model as req
import mycity.test.test_constants as test_constants




###############################################################################                                                                
# TestCase parent class for all intent TestCases, which are integration tests #                                                                
# to see if any changes in codebase have broken response-request model.       #                                                                
#                                                                             #                                                                
# NOTE: Assumes that address has already been set.                            #                                                                
###############################################################################                                                                

class IntentBaseCase(unittest.TestCase):

    __test__ = False

    intent_to_test = None
    returns_reprompt_text = False

    @classmethod
    def setUpClass(cls):
        cls.controller = mcc.MyCityController()
        cls.request = req.MyCityRequestDataModel()
        key = intent_constants.CURRENT_ADDRESS_KEY
        cls.request._session_attributes[key] = "46 Everdean St"
        cls.request.intent_name = cls.intent_to_test
        cls.response = cls.controller.on_intent(cls.request)

    @classmethod
    def tearDownClass(cls):
        cls.controller = None
        cls.request = None

    def test_for_smoke(self):
        self.assertNotIn("Uh oh", self.response.output_speech)
        self.assertNotIn("Error", self.response.output_speech)

    def test_correct_intent_card_title(self):
        self.assertEqual(self.intent_to_test, self.response.card_title)


    @unittest.skipIf(not returns_reprompt_text,
                     "{} shouldn't return a reprompt text".format(intent_to_test))
    def test_returning_reprompt_text(self):
        self.assertIsNotNone(self.response.reprompt_text)


    @unittest.skipIf(returns_reprompt_text,
                   "{} should return a reprompt text".format(intent_to_test))
    def test_returning_no_reprompt_text(self):
        self.assertIsNone(self.response.reprompt_text)

test_unhandled_intent.py

import mycity.test.intent_base_case as base_case


########################################                                                                                                       
# TestCase class for unhandled intents #                                                                                                       
########################################                                                                                                       


class UnhandledIntentTestCase(base_case.IntentBaseCase):

    __test__ = True

    intent_to_test = "UnhandledIntent"
    returns_reprompt_text = True

выход

======================================================================
FAIL: test_correct_intent_card_title (mycity.test.test_unhandled_intent.UnhandledIntentTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/wdrew/projects/alexa_311/my_city/mycity/mycity/test/intent_base_case.py", line 44, in test_correct_intent_card_title
    self.assertEqual(self.intent_to_test, self.response.card_title)
AssertionError: 'UnhandledIntent' != 'Unhandled intent'
- UnhandledIntent
?          ^
+ Unhandled intent
?          ^^


======================================================================
FAIL: test_returning_no_reprompt_text (mycity.test.test_unhandled_intent.UnhandledIntentTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/wdrew/projects/alexa_311/my_city/mycity/mycity/test/intent_base_case.py", line 56, in test_returning_no_reprompt_text
    self.assertIsNone(self.response.reprompt_text)
AssertionError: 'So, what can I help you with today?' is not None

----------------------------------------------------------------------
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
50
1

Ответы 1

Это из-за порядка исполнения. Декораторы SkipIf выполняются один раз во время синтаксического анализа класса IntentBaseCase. Они не выполняются повторно для каждого класса или для каждого вызова тестовой функции.

Шаблон декоратора для SkipIf разработан для использования с фиксированными глобальными переменными, такими как версии зависимых модулей, операционной системы или какого-либо другого внешнего ресурса, доступность которого может быть вычислена или известна в глобальном контексте.

Пропуск тестов - это тоже то, что следует делать по внешним причинам, а не по внутренним, таким как потребности подкласса. Пропуск по-прежнему является своего рода неудачным тестом, который указывается в отчете, поэтому вы можете видеть, что ваш набор тестов не выполняет всю функциональную область проекта.

Вам следует изменить структуру своего базового класса, чтобы функции были доступны для запуска только в подклассе, и пропустить использование для этого Пропустить. Моя рекомендация была бы такой:

class IntentBaseCase(unittest.TestCase):
    ...

class RepromptBaseCase(IntentBaseCase):
    def test_returning_reprompt_text(self):
        self.assertIsNotNone(self.response.reprompt_text)

class NoRepromptBaseCase(IntentBaseCase):
    def test_returning_no_reprompt_text(self):
        self.assertIsNone(self.response.reprompt_text)

Вам также следует подумать о том, чтобы переместить часть ответа из setUp и поместить ее в собственную функцию test_, а также изменить эти функции test_returning на более простые функции assertReprompt и assertNoReprompt. Настроить тесты в setUp - хорошая идея, но запускать там реальный код - плохая идея.

Другие вопросы по теме