Тестирование Python путем утверждения согласованного поведения в разных версиях кода? - PullRequest
0 голосов
/ 03 февраля 2019

Типичный модульный тест Python утверждает, что некоторая функция возвращает предварительно вычисленное выходное значение, например:

def average(a, b):
    return (a + b) / 2

def test_average():
    assert average(2, 4) == 3

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

Но меня не устраивает то, что рамки тестов неоднозначности оставляют без внимания то, насколько точно установлен результат (3 в примере)предназначен для вычисления.В тесте часто требуется, чтобы различные параметры демонстрировали поведение в крайнем случае, например:

def test_average(a, b):
    params = [
        [1, 3, 2],
        [9000.5, 9000.5, 9000.5], 
        [100, -100, 0],
        [-1, 3, 1],
        [0, 0, 0]
    ]
    for a, b, result in params:
        assert average(a, b) == result

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

  1. Запустите проверяемый код один раз вручную, например, из IPython, и сохраните его возвращаемое значение в кодовой базе в качестве проверочного утверждения
  2. Достаточно переопределитев тестируемом коде ожидаемые результаты вычисляются в самом тестовом примере

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

Это заставило меня задуматься, существуют ли методы тестирования, где запись / сравнение поведения во время ревизии между ревизиями (для тестируемого программного обеспечения)делается явнымНапример, вы можете записать JSON-коллекцию возвращаемых значений «demo case» на диск и в систему управления версиями, чтобы тестовые примеры могли затем утверждать свойства о согласованности этих результатов в разных версиях:

def demo_average():
    arg_a_vals = [0, -4.2, 1/12] + range(-100, 1000, 5)
    arg_b_vals = [0, -10**200, math.pi] + range(2**20, 2**20 + 17)
    return [
        a, b, average(a, b)
        for a in arg_a_vals
        for b in arg_b_vals
    ]

def save_demo(results):
    with open(f"demo_v{__version__}.json", "w") as f:
        json.dump(results, f)

def load_demo(version):
    with open(f"demo_v{version}.json") as f:
        return json.load(f)

def test_average():
    result = demo_average()
    save_demo(result)
    saved_result = load_demo(GOOD_PRIOR_VERSION)
    assert_lists_are_equal(result, saved_result)

Этот примерпреднамеренно просто, но save_demo, load_demo и test_average можно обобщить, поэтому единственный код, который вам нужно повторять в каждом тестовом примере, это какой-то вариант demo_average - перечисление возможных входных значений, выходные данные которых должны сравниватьсячерез ревизии.Обратите внимание, что код не включает в себя любые ожидаемые выходные значения в виде литералов, в отличие от обычного теста утверждения.

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

Существуют ли какие-либо тестовые платформы или проекты Python, которые работаюткак это? То есть, когда программное обеспечение тестируется путем проверки того, что код возвращает согласованные результаты при пересмотрах кода , а не конкретный ожидаемый результат, заявленный в тестовом примере.[Редактировать: Дирк Херманн отмечает в комментариях, что это иногда называют тестирование .]

Обратите внимание, что я изучил tox и яЯ не уверен, что это адрес этого варианта использования.Он поддерживает сравнение между версиями, но в документах в основном рассматривается обеспечение того, чтобы ваши собственные существующие тестовые случаи проходили под несколькими версиями зависимостей вашего проекта, что я не думаю, применимо.

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

...