Создание подкласса TestCase в Python: перезапись поля в родительском TestCase - PullRequest
0 голосов
/ 29 апреля 2018

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

----------------------------------------------------------------------

1 Ответ

0 голосов
/ 03 мая 2018

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

...