Как правильно использовать функцию настройки для каждого теста, которая возвращает значение в unittest? - PullRequest
0 голосов
/ 01 июля 2019

Для каждого из моих тестов в наборе unittest.TestCase мне нужно выполнить некоторую настройку: запустить функцию, которая возвращает различное значение для каждого теста (в зависимости от некоторых свойств каждого теста, которые передаются в качестве аргументов функции установки).

Есть хук setUp(), который я мог бы использовать, но он не возвращает значение и не принимает аргументы.Но, скажем, в этом случае аргументы не важны.

Какая стратегия рекомендуется?

  • Создание пользовательской функции настройки для использования внутри каждого теста
  • Использование setUp() с глобальными переменными
  • Использование setUp() с переменными класса или экземпляра

Ответы [ 2 ]

1 голос
/ 01 июля 2019

Я придерживаюсь общей стратегии: если переменная будет использоваться в нескольких тестовых функциях, я определяю ее в setUp(). Если он будет использоваться только один раз для конкретной функции, определите его в этой функции.

Возьмите следующий пример:

Скажем, у меня есть модуль Python в пакете program с именем list_utils.py и в list_utils.py У меня есть следующие функции:

def list_to_string(mylist):
    """ Takes a list of strings and joins them into a single string.
    """
    return ' '.join(mylist)

def list_extender(mylist, extend_item):
    return mylist.extend(extend_item)

Затем я настроил свой скрипт unittest с указанием mytestlist, потому что он будет использоваться в нескольких тестовых функциях:

from program import list_utils as lu

class TestListUtils(unittest.TestCase):
    """
    A subclass of unittest to test list_utils.py
    """
    def setUp(self):
        self.mytestlist = ['Hi', 'there']

    def test_list_to_string(self):
        """
        Ensures my list is converted to string
        """
        self.assertTrue(isinstance(lu.list_to_string(self.mytestlist), string))
    def test_list_extender(self):
        """
        Ensures list is extended when argument is passed.
        """
        mylocalvariable = 'Adam'
        self.assertTrue(lu.list_extender(self.mytestlist, mylocalvariable)[-1] == 'Adam')

    def tearDown(self):
        pass

if __name__ == '__main__':
    unittest.main()

Вы видите, что для list_extender я передал mylocalvariable, потому что я бы использовал его только в области действия этой функции, но mytestlist был определен в setUp, потому что я использовал его несколько раз. Следуя этому общему подходу, вы не должны слишком раздувать ваш setUp, и вам также не придется повторно создавать экземпляры переменных при каждом конкретном тестировании модуля, если они будут использоваться несколько раз.

0 голосов
/ 04 июля 2019

Обратите внимание на мое предназначение между настройкой и Setup ниже: каждый тест требует определенных действий по настройке, но они не обязательно включаются в метод Setup.

Я предпочитаю, чтобы каждая функция тестированиябыть легко понятым само по себе.Я не хочу смотреть на тестовый код, задающийся вопросом «откуда это значение внезапно возникает» или тому подобное.Это означает, что я избегаю распространенного метода Setup, но вместо этого использую вспомогательные функции / методы, которые также имеют описательные имена.Чтобы быть более точным:

  • Если для тестового примера требуется конкретная настройка, я обычно встраиваю его непосредственно в тест.
  • Если подмножество тестовых наборов имеет идентичные или похожиеНастройка, я считаю создание вспомогательного метода для извлечения общих частей.Имя этого вспомогательного метода должно быть описательным, например makeTrafficLightInGreenState, для вспомогательной фабрики, производящей объект светофора определенного типа.В этом примере у меня также может быть makeTrafficLightInRedState для другой группы тестов.Я считаю это предпочтительным по сравнению с обычным Setup методом, который просто создает зеленый и красный светофор: в конце концов, вы не понимаете, какая часть Setup подходит для какого теста.Конечно, при написании ваших вспомогательных методов вы также можете указать их параметры, что в примере со светофором может привести к 'makeTrafficLightInState (green)'.Но какой подход выбрать здесь, зависит от конкретной ситуации.
  • Даже если у всех тестов есть что-то общее, например, создание определенного объекта, я предпочитаю помещать это в описательно-вспомогательный метод с именемвместо того, чтобы полагаться на неявный вызов Setup.
  • Иногда я также использую Setup, но только для действий, которые не нужны для понимания логики тестовых случаев.Например, если есть какое-то исходное состояние, которое необходимо сохранить в начале тестовых случаев и восстановить после него, его можно поместить в Setup без какого-либо негативного влияния на читаемость отдельных тестовых случаев.

В вашем случае, похоже, у вас уже есть функция с параметрами, которая подходит для доставки данных, специфичных для тестового примера.Я бы просто вызвал эту функцию из каждого тестового примера, в котором она нужна.Или, если вызов этой функции не прост (вам нужно создать дополнительные объекты в качестве аргументов для нее или тому подобное), снова поместите ее в вспомогательный метод.

...