Я пишу интеграционные тесты для приложения 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
----------------------------------------------------------------------






Это из-за порядка исполнения. Декораторы 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 - хорошая идея, но запускать там реальный код - плохая идея.