4 метода тестирования приложений Python для командной строки (CLI)

4 метода тестирования приложений Python для командной строки (CLI)

Вы только что закончили сборку своего первого приложения командной строки Python. Или, может быть, ваш второй или третий. Вы изучали Python некоторое время, и теперь вы готовы построить something, больше и сложнее , но все еще работает в командной строке. Или вы привыкли к building and test веб-приложениям или настольным приложениям с графическим интерфейсом, но сейчас начинаете создавать приложения CLI.

Во всех этих и других ситуациях вам необходимо изучить и освоить различные методы тестирования приложения Python CLI.

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

*В этом руководстве вы узнаете четыре практических метода тестирования приложений командной строки Python:*
  • Отладка «Lo-Fi» с помощью + print () +

  • Использование визуального отладчика Python

  • Юнит тестирование с помощью pytest и mocks

  • Интеграционное тестирование

    *Бесплатный бонус:* ссылка: # [Нажмите здесь, чтобы получить наш шпаргалку по тестированию Python], в которой кратко описаны методы, продемонстрированные в этом руководстве.

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

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

В этом исходном коде я обнаружил несколько ошибок, которые мы покажем в наших методах тестирования.

_ Примечание : для простоты этот код не включает некоторые базовые рекомендации, такие как проверка наличия ключей в словаре. _

В качестве первого шага давайте подумаем о наших объектах на каждом этапе этого приложения. Мы начнем со структуры, которая описывает Джона В. Общественность:

JOHN_DATA = {
    'name': 'John Q. Public',
    'street': '123 Main St.',
    'city': 'Anytown',
    'state': 'FL',
    'zip': 99999,
    'relationships': {
        'siblings': ['Michael R. Public', 'Suzy Q. Public'],
        'parents': ['John Q. Public Sr.', 'Mary S. Public'],
    }
}

Затем мы выравниваем другие словари, ожидая этого после вызова нашей первой функции преобразования + initial_transform +:

JOHN_DATA = {
    'name': 'John Q. Public',
    'street': '123 Main St.',
    'city': 'Anytown',
    'state': 'FL',
    'zip': 99999,
    'siblings': ['Michael R. Public', 'Suzy Q. Public'],
    'parents': ['John Q. Public Sr.', 'Mary S. Public'],
}

Затем мы собираем всю адресную информацию в одну адресную запись с помощью функции + final_transform +:

JOHN_DATA = {
    'name': 'John Q. Public',
    'address': '123 Main St. \nAnytown, FL 99999'
    'siblings': ['Michael R. Public', 'Suzy Q. Public'],
    'parents': ['John Q. Public Sr.', 'Mary S. Public'],
}

И вызов + print_person + запишет это в консоль:

Hello, my name is John Q. Public, my siblings are Michael R. Public
and Suzy Q. Public, my parents are John Q. Public Sr. and Mary S. Public,
and my mailing address is:
123 Main St.
Anytown, FL 99999

testapp.py:

def initial_transform(data):
    """
    Flatten nested dicts
    """
    for item in list(data):
        if type(item) is dict:
            for key in item:
                data[key] = item[key]

    return data


def final_transform(transformed_data):
    """
    Transform address structures into a single structure
    """
    transformed_data['address'] = str.format(
        "{0}\n{1}, {2} {3}", transformed_data['street'],
        transformed_data['state'], transformed_data['city'],
        transformed_data['zip'])

    return transformed_data


def print_person(person_data):
    parents = "and".join(person_data['parents'])
    siblings = "and".join(person_data['siblings'])
    person_string = str.format(
        "Hello, my name is {0}, my siblings are {1}, "
        "my parents are {2}, and my mailing"
        "address is: \n{3}", person_data['name'],
        parents, siblings, person_data['address'])
    print(person_string)


john_data = {
    'name': 'John Q. Public',
    'street': '123 Main St.',
    'city': 'Anytown',
    'state': 'FL',
    'zip': 99999,
    'relationships': {
        'siblings': ['Michael R. Public', 'Suzy Q. Public'],
        'parents': ['John Q. Public Sr.', 'Mary S. Public'],
    }
}

suzy_data = {
    'name': 'Suzy Q. Public',
    'street': '456 Broadway',
    'apt': '333',
    'city': 'Miami',
    'state': 'FL',
    'zip': 33333,
    'relationships': {
        'siblings': ['John Q. Public', 'Michael R. Public',
                    'Thomas Z. Public'],
        'parents': ['John Q. Public Sr.', 'Mary S. Public'],
    }
}

inputs = [john_data, suzy_data]

for input_structure in inputs:
    initial_transformed = initial_transform(input_structure)
    final_transformed = final_transform(initial_transformed)
    print_person(final_transformed)

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

«Lo-Fi» отладка с печатью

Это один из самых простых способов тестирования. Все, что вам нужно сделать здесь, это + print + переменная или объект, который вас интересует - до вызова функции, после вызова функции или внутри функции.

Соответственно, они позволяют вам проверять ввод функции, вывод функции и логику функции.

Если вы сохраните приведенный выше код как + testapp.py + и попытаетесь запустить его с + python testapp.py +, вы увидите ошибку, подобную этой:

Traceback (most recent call last):
  File "testapp.py", line 60, in <module>
    print_person(final_transformed)
  File "testapp.py", line 23, in print_person
    parents = "and".join(person_data['parents'])
KeyError: 'parents'

В + person_data + отсутствует ключ, который передается в + print_person +. Первым шагом будет проверка ввода в + print_person + и выяснение того, почему наш ожидаемый вывод (печатное сообщение) не генерируется. Мы просто добавим вызов функции + print + перед вызовом + print_person +:

final_transformed = final_transform(initial_transformed)
print(final_transformed)
print_person(final_transformed)

Функция + print + делает здесь работу, показывая в своем выводе, что у нас нет ключа + parent + верхнего уровня - ни ключа + siblings + - но в интересах нашего здравого смысла, я буду покажу вам + pprint +, который печатает многоуровневые объекты в более читабельной форме. Чтобы использовать его, добавьте + из pprint import pprint + в начало вашего скрипта.

Вместо + print (final_transformed) +, мы вызываем + pprint (final_transformed) + для проверки нашего объекта:

{'address': '123 Main St.\nFL, Anytown 99999',
 'city': 'Anytown',
 'name': 'John Q. Public',
 'relationships': {'parents': ['John Q. Public Sr.', 'Mary S. Public'],
                   'siblings': ['Michael R. Public', 'Suzy Q. Public']},
 'state': 'FL',
 'street': '123 Main St.',
 'zip': 99999}

Сравните это с ожидаемой окончательной формой выше.

Поскольку мы знаем, что + final_transform + не касается словаря + Relations +, пришло время посмотреть, что происходит в + initial_transform +. Обычно я бы использовал традиционный отладчик, чтобы пройти через это, но я хочу показать вам еще одно использование отладки печати.

Мы можем напечатать состояние объектов в коде, но мы не ограничены этим. Мы можем печатать все, что захотим, поэтому мы также можем печатать маркеры, чтобы увидеть, какие логические ветви выполняются и когда.

Поскольку + initial_transform + - это, прежде всего, несколько циклов, и поскольку внутренние словари должны обрабатываться внутренним циклом for, мы должны проверить, что там происходит, если что-нибудь:

def initial_transform(data):
    """
    Flatten nested dicts
    """
    for item in list(data):
        if type(item) is dict:
            print "item is dict!"
            pprint(item)
            for key in item:
                data[key] = item[key]

    return data

Если мы встретим словарь внутри нашего ввода + data +, тогда мы будем предупреждены в консоли, а затем мы увидим, как выглядит элемент.

После запуска наш консольный вывод не изменился. Это хорошее доказательство того, что наше утверждение + if + работает не так, как ожидалось. Хотя мы можем продолжить печать, чтобы найти ошибку, это отличный способ продемонстрировать сильные стороны использования отладчика.

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

Заворачивать

*Когда использовать отладку печати:*
  • Простые объекты

  • Более короткие сценарии

  • На первый взгляд простые ошибки

  • Быстрые проверки

    *Погрузитесь глубже:*
  • pprint - предварительно печатать печатные объекты

    *Плюсы:*
  • Быстрое тестирование

  • Легко использовать

    *Минусы:*
  • В большинстве случаев вам нужно запустить всю программу, в противном случае:

  • Вам нужно добавить дополнительный код для ручного управления потоком

  • Вы можете случайно оставить тестовый код, когда закончите, особенно в сложном коде

Использование отладчика

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

Есть много отладчиков, и они часто приходят с IDE. В Python также есть a модуль с именем + pdb +, который можно использовать в REPL для код отладки. Вместо того, чтобы вдаваться в детали реализации всех доступных отладчиков, в этом разделе я покажу вам, как использовать отладчики с общими функциями, такими как установка breakpoints и watches.

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

Но давайте вернемся к контрольным точкам. Они будут добавлены там, где вы хотите начать или продолжить сеанс отладки. Поскольку мы отлаживаем метод + initial_transform +, мы захотим поместить его туда. Обозначим точку останова через + (*) +:

def initial_transform(data):
    """
    Flatten nested dicts
    """
(*) for item in list(data):
        if type(item) is dict:
            for key in item:
                data[key] = item[key]

    return data

Теперь, когда мы начнем отладку, выполнение будет приостановлено в этой строке, и вы сможете увидеть переменные и их типы в конкретный момент выполнения программы. У нас есть несколько вариантов навигации по нашему коду: step over, step in и step out являются наиболее распространенными.

Step over - это тот, который вы будете использовать чаще всего - он просто переходит к следующей строке кода.

Step in, пытается углубиться в код. Вы будете использовать это, когда столкнетесь с вызовом функции, который вы хотите исследовать более глубоко - вы попадете прямо к коду этой функции и сможете там проверить состояние. Вы также часто используете это, когда путаете это для step over. К счастью, step out может спасти нас, это возвращает нас к звонящему.

Мы также можем установить здесь watch, что-то вроде + type (item) is dict +, что вы можете сделать в большинстве IDE с помощью кнопки «add watch» во время сеанса отладки. Теперь это покажет + True + или + False + независимо от того, где вы находитесь в коде.

Установите часы и теперь перешагните так, чтобы вы остановились на строке + if type (item) dict: +. Теперь вы должны видеть состояние часов, новую переменную + item + и объект + data +.

Python Debugger Screenshot: Переменные наблюдения

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

def initial_transform(data):
    """
    Flatten nested dicts
    """
    for item in list(data):
        if type(data[item]) is dict:
            for key in data[item]:
                data[key] = item[key]

    return data

Мы должны снова запустить его через отладчик и просто убедиться, что код работает так, как мы ожидаем. И мы не, структура теперь выглядит так:

john_data = {
    'name': 'John Q. Public',
    'street': '123 Main St.',
    'city': 'Anytown',
    'state': 'FL',
    'zip': 99999,
    'relationships': {
        'siblings': ['Michael R. Public', 'Suzy Q. Public'],
        'parents': ['John Q. Public Sr.', 'Mary S. Public'],
    },
    'siblings',
    'parents',
}

Теперь, когда мы рассмотрели, как используется визуальный отладчик, давайте углубимся и применим наши новые знания к тесту, выполнив упражнение, приведенное ниже.

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

Заворачивать

*Когда использовать отладчик Python:*
  • Более сложные проекты

  • Трудно обнаружить ошибки

  • Вам нужно осмотреть более одного объекта

  • У вас есть приблизительное представление о том, где происходит ошибка, но вам нужно сосредоточиться на ней

    *Погрузитесь глубже:*
  • Условные точки останова

  • Оценка выражений при отладке

    *Плюсы:*
  • Контроль за ходом программы

  • С высоты птичьего полета состояние приложения

  • Не нужно точно знать, где происходит ошибка

    *Минусы:*
  • Сложно вручную смотреть очень большие объекты

  • Длительный код займет очень много времени для отладки

Модульное тестирование с Pytest и Mocks

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

Хотя логика в нашем коде довольно проста, она может легко увеличиться в размере и сложности или стать обязанностью всей команды. Как мы тестируем приложение более структурированным, подробным и автоматизированным способом?

Введите юнит-тесты.

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

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

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

Повторяемость и изоляция являются ключевыми для такого рода тестов, хотя мы все еще продолжаем нашу тему сравнения ожидаемых результатов с фактическими результатами. Теперь, когда у вас есть общее представление о модульном тестировании, вы можете сделать небольшой обход и посмотреть, как проводить модульное тестирование приложений Flask с помощью minimum viable test suite .

Pytest

Итак, теперь, когда мы углубились в теорию, давайте посмотрим, как это работает на практике. Python поставляется со встроенным модулем + unittest +, но я считаю, что + pytest + отлично справляется с тем, что предоставляет + unittest +. В любом случае, я просто покажу основы модульного тестирования, поскольку одно только модульное тестирование может занять несколько длинных статей.

Общепринятым условием является размещение всех ваших тестов в каталоге + test + внутри вашего проекта. Поскольку это небольшой скрипт, достаточно файла + test_testapp.py + на том же уровне, что и + testapp.py +.

Мы напишем модульный тест для + initial_transform +, чтобы показать, как настроить набор ожидаемых входов и выходов и убедиться, что они совпадают. Основной шаблон, который я использую с + pytest +, - это настройка fixture, которая будет принимать некоторые параметры и использовать их для генерации входных данных теста и ожидаемых выводы, которые я хочу.

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

import pytest
import testapp as app

@pytest.fixture(params=['nodict', 'dict'])
def generate_initial_transform_parameters(request):

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

Сначала мы используем декоратор + @ pytest.fixture +, чтобы объявить следующее определение функции фиксатором. Мы также используем именованный параметр + params + для использования с + generate_initial_transform_parameters +.

Отличная особенность заключается в том, что всякий раз, когда используется декорированная функция, она будет использоваться с каждым параметром, поэтому простой вызов + generate_initial_transform_parameters + вызовет его дважды, один раз с + nodict + в качестве параметра и один раз с `+ dict + `.

Чтобы получить доступ к этим параметрам, мы добавляем специальный объект pytest + request + в сигнатуру нашей функции.

Теперь давайте создадим наши входы и ожидаемые результаты:

@pytest.fixture(params=['nodict', 'dict'])
def generate_initial_transform_parameters(request):
    test_input = {
        'name': 'John Q. Public',
        'street': '123 Main St.',
        'city': 'Anytown',
        'state': 'FL',
        'zip': 99999,
    }
    expected_output = {
        'name': 'John Q. Public',
        'street': '123 Main St.',
        'city': 'Anytown',
        'state': 'FL',
        'zip': 99999,
    }

    if request.param == 'dict':
        test_input['relastionships'] = {
            'siblings': ['Michael R. Public', 'Suzy Q. Public'],
            'parents': ['John Q. Public Sr.', 'Mary S. Public'],
        }
        expected_output['siblings'] = ['Michael R. Public', 'Suzy Q. Public']
        expected_output['parents'] = ['John Q. Public Sr.', 'Mary S. Public']

    return test_input, expected_output

Ничего удивительного в этом нет, мы настроили ввод и ожидаемый вывод, и если у нас есть параметр + 'dict' +, то мы изменим ввод и ожидаемый вывод, что позволит нам проверить блок + if +.

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

def test_initial_transform(generate_initial_transform_parameters):
    test_input = generate_initial_transform_parameters[0]
    expected_output = generate_initial_transform_parameters[1]
    assert app.initial_transform(test_input) == expected_output

Тестовые функции должны начинаться с + test_ + и основываться на https://dbader.org/blog/python-assert-tutorial [+ assert + операторов]. Здесь мы утверждаем, что результат, который мы получаем от передачи нашего ввода в нашу реальную функцию, равен нашему ожидаемому результату. Когда вы запустите это либо в своей IDE с тестовой конфигурацией, либо с помощью + pytest + в CLI, вы получите …​ ошибки! Наш вывод еще не совсем правильный. Давайте исправим это, используя следующее упражнение - практический опыт неоценим, а применение того, что вы прочитали на практике, поможет вам вспомнить в будущем.

Mocks

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

Давайте добавим вызов внешней функции в + initial_transform +:

def initial_transform(data):
    """
    Flatten nested dicts
    """
    for item in list(data):
        if type(data[item]) is dict:
            for key in data[item]:
                data[key] = data[item][key]
            data.pop(item)

    outside_module.do_something()
    return data

Мы не хотим совершать живые вызовы + do_something () +, поэтому вместо этого мы будем шутить в нашем тестовом скрипте. Макет поймает этот вызов и вернет все, что вы настроили для возврата. Мне нравится настраивать макеты в приборах, так как это часть тестовой настройки, и мы можем хранить весь этот установочный код вместе:

@pytest.fixture(params=['nodict', 'dict'])
def generate_initial_transform_parameters(request, mocker):
    [...]
    mocker.patch.object(outside_module, 'do_something')
    mocker.do_something.return_value(1)
    [...]

Теперь каждый раз, когда вы вызываете + initial_transform +, вызов + do_something + будет перехватываться и возвращать 1. Вы также можете воспользоваться параметрами фикстуры, чтобы определить, что возвращает ваш макет - это важно, когда ветвь кода определяется результатом внешнего вызова.

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

def initial_transform(data):
    """
    Flatten nested dicts
    """
    for item in list(data):
        if type(data[item]) is dict:
            for key in data[item]:
                data[key] = data[item][key]
            data.pop(item)

    outside_module.do_something()
    outside_module.do_something()
    return data

Мы настроили наш макет следующим образом: список выходных данных (для каждого последующего вызова) передается в + side_effect +:

@pytest.fixture(params=['nodict', 'dict'])
def generate_initial_transform_parameters(request, mocker):
    [...]
    mocker.patch.object(outside_module, 'do_something')
    mocker.do_something.side_effect([1, 2])
    [...]

Mocking очень мощный, настолько мощный, что вы даже можете set mock-серверы для тестирования сторонних API-интерфейсов, и я еще раз призываю вас чтобы глубже погрузиться в насмешку с помощью + mocker +.

Заворачивать

*Когда использовать рамки модульного тестирования Python:*
  • Большие, сложные проекты

  • Проекты OSS

    *Полезные инструменты:*
  • Pytest fixtures

  • deepdiff для сравнения сложных объектов

  • насмешник

    *Плюсы:*
  • Автоматизирует выполнение тестов

  • Может ловить много типов ошибок

  • Простая настройка и модификация для команд

    *Минусы:*
  • Нудно писать

  • Должен быть обновлен с большинством изменений кода

  • Не копировать истинное приложение работает

Интеграционное тестирование

Интеграционное тестирование является одним из более простых методов тестирования, но, возможно, одним из самых важных. Это влечет за собой фактическое выполнение приложения с реальными данными в производственной среде.

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

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

То, как вы это сделаете, сильно зависит от вашего приложения, например, наше тестовое приложение может запускаться самостоятельно с помощью + python testapp.py +. Однако давайте представим, что наш код представляет собой сегмент большого распределенного приложения, такого как конвейер ETL - в этом случае вам придется запускать всю систему на тестовых серверах с подкачанным кодом, запускать данные через него и убедиться, что он выполнен это через всю систему в правильной форме. За пределами мира приложений командной строки для интеграционного тестирования приложений Django можно использовать такие инструменты, как pyVows.

Заворачивать

*Когда использовать интеграционное тестирование в Python:*
  • Всегда ;-)

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

    *Полезные инструменты:*
  • tox среда и управление автоматизацией тестирования

    *Плюсы:*
  • Посмотрите, как ваше приложение работает в реальных условиях

    *Минусы:*
  • Большие приложения могут быть трудно точно отслеживать поток данных через

  • Должны иметь тестовые среды, которые очень близки к производственным средам

Собираем все вместе

В заключение - все тестирование CLI заключается в сравнении ожидаемых результатов с фактическими результатами с учетом некоторого набора входных данных. Методы, о которых я говорил выше, - это все способы сделать это, и во многих отношениях они дополняют друг друга. Это будут важные инструменты, которые вы поймете, когда продолжите создавать приложения для командной строки в Python, но это руководство является лишь отправной точкой.

У Python очень богатая экосистема, и она распространяется на инструменты и методики тестирования, поэтому отойдите от этого и исследуйте больше - вы можете найти инструмент или технику, которую я здесь не упомянул, и которую вы абсолютно любите. Если это так, я хотел бы услышать об этом в комментариях!

В качестве краткого резюме, вот методы, о которых мы узнали сегодня и как они используются:

  • Отладка печати - распечатка переменных и других маркеров в коде, чтобы увидеть, как выполняется выполнение

  • Отладчики - контролируют выполнение программы, чтобы получить представление о состоянии приложения и ходе выполнения программы с высоты птичьего полета

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

  • Интеграционное тестирование - тестирование изменений кода в контексте более широкого приложения

А теперь иди и проверь! Работая с этими методами, обязательно дайте мне знать в комментариях, как вы их использовали и какие ваши любимые.

Чтобы получить шпаргалку по тестированию Python, в которой обобщены методы, продемонстрированные в этом руководстве, щелкните ссылку ниже:

*Бесплатный бонус:* ссылка: # [Нажмите здесь, чтобы получить наш шпаргалку по тестированию Python], в которой кратко описаны методы, продемонстрированные в этом руководстве.