Лучшие практики для модульного тестирования в Python - несколько функций применяются к одному и тому же объекту - PullRequest
0 голосов
/ 17 марта 2019

У меня есть набор функций, которые применяются к похожему объекту, например массив Numpy, который представляет собой n-мерную рамку:

# 3-D box parameterized as:
#     box[0] = 3-D min coordinate
#     box[1] = 3-D max coordinate
box = np.array([
    [1, 3, 0],
    [4, 5, 7]
])

Теперь у меня есть целый набор функций, которые я хочу запускать в списках блоков, например. volumes, intersection, smallest_containing_box и т. Д. На мой взгляд, вот то, как я надеялся настроить это:

# list of test functions:
test_funcs = [volume, intersection, smallest_containing_box, ...]
# manually create a bunch of inputs and outputs
test_set_1 = (
    input = [boxA, boxB, ...], # where each of these are np.Array objects
    output = [
        [volA, volB, ...], # floats I calculated manually
        intersection, # np.Array representing the correct intersection
        smallest_containing_box, # etc.
    ]
)
# Create a bunch of these, eg. test_set_2, test_set_3, etc. and bundle them in a list:
test_sets = [test_set_1, ...]
# Now run the set of tests over each of these:
test_results = [[assertEqual(test(t.input), t.output) for test in test_funcs] for t in test_sets]

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

class TestCase1(unittest.TestCase):
    def setUp(self):
        self.input = [...]
        self.volume = [volA, volB, ...]
        self.intersection = ...
        # etc.

    def test_volume(self):
        self.assertEqual(volume(self.input), self.volume)

    def test_intersection(self):
        self.assertEqual(intersection(self.input), self.output)

    # etc.

# Repeat this for every test case!?

Это похоже на безумное количество шаблонов. Я что-то упустил?

Ответы [ 2 ]

0 голосов
/ 20 марта 2019

Попробуйте unittest.TestSuite(). Это дает вам объект, куда вы можете добавить тестовые случаи. В вашем случае создайте набор, затем зациклите свои списки, создавая экземпляры TestCase, которые имеют только один метод тестирования. Передайте тестовые данные в конструктор и сохраните их в свойствах, а не в setUp().

Модуль модульного тестирования обнаружит комплект при его создании в методе с именем suite() и запустит все из них.

Примечание. Присвойте имя каждому экземпляру TestCase или выясните, какая из них не удалась.

0 голосов
/ 20 марта 2019

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

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

Я скептически отношусь к тому, что с вашим подходом, ориентированным на данные тестирования, вы одинаково успешно найдете ошибки в ваших различных функциях.: Для функции volume могут быть сценарии переполнения (а также сценарии переполнения), которые не применяются для intersection или smallest_containing_box.Напротив, должны быть пустые пересечения, одноточечные пересечения и т. Д. Таким образом, кажется, что каждая из функций, вероятно, нуждается в специально разработанных тестовых сценариях.

Относительно стандартного кода, который, кажется, является следствиеммодульного тестирования, ориентированного на код: это можно ограничить несколькими способами.Согласитесь, у вас будут разные методы тестирования для разных тестируемых функций.Но тогда вы можете использовать параметризованные тесты, чтобы избежать дальнейшего дублирования кода.И для случая, когда вы по-прежнему видите преимущество в использовании (по крайней мере, иногда) общих тестовых данных для различных функций: для таких случаев вы можете использовать фабричные функции, которые создают тестовые данные и могут вызываться из разных тестовых случаев.,Например, у вас может быть заводская функция make-unit-cube для использования в разных тестах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...