Как начать тестирование на существующем скрипте python? - PullRequest
3 голосов
/ 01 мая 2020

У меня довольно большой опыт работы с python в качестве инструмента для обработки данных, без CS-фона (но я хочу учиться).

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

Мой вопрос: как мне вообще начать работать с таким существующим скриптом? Я вижу pytest и unittest, но это где я должен начать? Код выглядит примерно так:

class Simulator:
    parameters = input_file
    def __init__(self):
        self.fn1
        self.fn2
        self.fn3

    def fn1():
        # with nested functions
    def fn2
    def fn3
    ...
    def fn(n)

Каждая функция либо генерирует, либо воздействует на некоторые данные. Будет ли способ протестировать какой-либо стандартизированный ввод / вывод и проверить это? Есть ли способ сделать это в рамках стандартного соглашения о тестировании?

Спасибо за любые советы или рекомендации, ура!

Ответы [ 4 ]

4 голосов
/ 01 мая 2020

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

Вот тест для простой функции add:

def add(a, b):
    return a + b

def test_add_function():
    a = 1
    b = 2
    assert add(a, b) == 3  # we KNOW that adding 1 + 2 must equal 3

Если вы вызываете test_add_function и AssertionError не поднято, поздравляю! Ваш тест пройден.

Конечно, тестирование усложняется, если у вас нет «чистых» функций, а есть объекты, которые работают с общими данными, например, классы. Тем не менее логика c в основном такая же: вызовите функцию и проверьте, действительно ли происходит ожидаемый результат :

class MyClass:
    def __init__(self, a):
        self.a = a

    def add_one_to_a(self):
        self.a += 1


def test_method_add_one_to_a():
    initial_a = 1
    instance = MyClass(a=1)
    assert instance.a == initial_a  # we expect this to be 1
    instance.add_one_to_a()  # instance.a is now 2
    assert instance.a == initial_a + 1  # we expect this to be 2

Я предлагаю прочитать / посмотреть некоторые учебные пособия по Python Модуль unittest для того, чтобы вымочить ноги, особенно для привыкания к классу unittest.TestCase, который очень помогает в обычных тестовых операциях, таких как процедуры установки / разрыва (которые позволяют, например, "refre sh" ваш экземпляр Simulator между тестами), тестирование, если возникает ошибка, когда функция вызывается с неправильными аргументами, и т. д. c.

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

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

2 голосов
/ 01 мая 2020

Если вы достаточно опытны с Python, но изо всех сил пытаетесь осмысленно применить юнит-тестирование к набору сценариев, которые принимают данные и преобразуют их, я бы выбрал другой подход, по крайней мере, если вывод определен c

  • хранить образцы данных где-нибудь и держать их под контролем источника

  • запускать отдельные функции для этих тестовых данных.

    • записать вывод. предполагается, что это «заведомо хорошо» /baseline.

      • Одна из проблем заключается в том, что вам, возможно, придется «вычищать» непрерывно изменяющиеся данные, такие как отметки времени или GUID.
      • sort, сортировать, сортировать множество данных выходит несортированными, и это хорошо, если все записи верны. Вы не можете ничего осмысленно сравнивать в этих обстоятельствах, поэтому вам нужно будет сортировать их детально c.
      • Различие в файлах обычно работает построчно. поэтому лучше "разбить" несколько полей в 1 строке на 1 поле в строке, возможно, с меткой <row1key>.f1 : <value1>\n<row1key>.f2: : <value2>
    • на этом этапе, вам не нужно проверить что-нибудь через этот механизм. (традиционные подходы к тестированию модулей все еще могут использоваться в других местах)

  • всякий раз, когда вы изменяете / реорганизуете код, запускайте образцы данных для соответствующих функций.

    • сравните ваш новый результат с вашим предыдущим базовым уровнем. если он не совпадает, у вас есть две возможности:

      • , новый код выводит лучше, т.е. что-то исправляет. новый вывод становится базовым. сохраните это в базовой линии.
      • старый вывод лучше. исправляйте новый код до тех пор, пока вы не получите тот же вывод
    • , если вы храните в виде текста / json / yaml, вы можете использовать утилиты разного типа, такие как Winmerge, Beyond Compare ( никогда не использовал), diff, opendiff, et c, чтобы помочь вам найти точки расхождения. На самом деле, в начале часто проще попросить Python просто записать выходные файлы без проверки на равенство, а затем использовать инструменты сравнения файлов для сравнения нескольких файлов последнего запуска и текущего запуска.

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

Обратите внимание, что вы всегда можете использовать обычные методы pytest / unittest, снабжающие ваши функции более ограниченными тщательно продуманными тестовыми данными, в которых проявляется какой-то конкретный аспект.

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

У меня есть html -ориентированный инструментарий на github, ленивые регрессионные тесты, на основе этот подход. Возможно, не подходит для конвейера данных, но вы действительно можете написать свой собственный.

0 голосов
/ 01 мая 2020

Независимо от того, как сильно вы тестируете программу, всегда разумно предположить, что всегда найдутся ошибки, оставшиеся необнаруженными, другими словами, невозможно проверить все. Для начала я рекомендую вам полностью понять, как работает программа; Таким образом, вы будете знать, каковы ожидаемые и важные значения, которые должны быть возвращены, и какие исключения следует выдавать при возникновении ошибки. Вам придется написать тесты самостоятельно, что может быть хлопотно, и звучит так, как будто вы не хотите этого делать, но тщательное тестирование предполагает настойчивость и решительность. Как вы, возможно, знаете, отладка и исправление кода могут занимать намного больше времени, чем сама часть кода.

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

0 голосов
/ 01 мая 2020

Надеюсь, у вас все в порядке!

pytest подходит для простых случаев, таких как ваш (1 файл сценария).

Начать действительно просто. Просто установите его, используя pip:

pip install -U pytest

Затем создайте тестовый файл (pytest запустит все файлы формы test _ *. Py или * _test.py в текущем каталоге и его подкаталогах)

# content of test_fn1.py

from your_script import example_function

def test_1():
    assert example_function(1, 2, 3) == 'expected output'

Вы можете добавить в этот файл столько тестов, сколько хотите, и столько файлов тестов, сколько хотите. Чтобы запустить, go в папку в терминале и просто выполните pytest. Для организации создайте папку с именем test, в которой находятся все тестовые файлы. Если вы сделаете это, обратите внимание на то, как вы импортируете свой скрипт, так как он больше не будет находиться в одной папке.

Проверьте pytest docs для получения дополнительной информации.

Надеюсь, это поможет! Оставайтесь в безопасности!

...