Рубрики
Без рубрики

Как создать документацию из ваших испытаний Python

Автор оригинала: cedd burge.

Что, если я сказал вам, что вы можете автоматически создавать документацию из ваших существующих тестов, которые всегда будут в курсе?

И что, если он может быть в формате Markdown, так что это будет предано с остальной частью вашего кода и отображаться на GitLab/Github?

Звучит довольно круто, верно? Давайте посмотрим, как это сделано.

Контекст

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

Я хотел бы работать на кодовой базе с такой документацией.

Проблема с документацией

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

Итак, для моей ситуации, по крайней мере, мне нужен способ создавать и обновлять документацию автоматически.

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

Есть много Инструменты Это может генерировать диаграммы зависимости в зависимости от времени от кода, и я использовал довольно много из них.

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

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

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

Решение

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

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

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

Решение включает в себя инструмент кода, импортируемый тестом. Затем этот приборный код сохраняет запись иерархии вызова времени выполнения и может написать результаты как Диаграмма Mermaid Markdown (Tecnhially A Диаграмма последовательности ).

Код ниже ( Тест от пакета Python ) показывает, как это работает.

Для каждого существующего теста вы создаете тест «обертки», который отвечает за инициализацию иерархии вызова и сохранения диаграммы. Если у вас много тестов, вы можете представить Декоратор Сохранить повторение.

from docs_from_tests.instrument_call_hierarchy import instrument_and_import_package, instrument_and_import_module, initialise_call_hierarchy, finalise_call_hierarchy
from samples.hello_world_combiner import HelloWorldCombiner

# you can instrument entire packages / folders at once like this
instrument_and_import_package(os.path.join(Path(__file__).parent.absolute(), '..', 'samples'), 'samples')
# You can instrument individual modules like this
# instrument_and_import_module('tests.blah')

# this is a wrapper around the test that also outputs the documentation / sequence diagram
def test_hello_world():
    # the initialises the recording of the call hierarchy
    initialise_call_hierarchy('start')

    # This runs the actual test
    _test_hello_world()
    
    # this finalises the call hierarchy and returns the root
    root_call = finalise_call_hierarchy()

    # this returns a sequence diagram of the call hierarchy
    sequence_diagram = root_call.sequence_diagram(
        show_private_functions=False,
        excluded_functions=[
            'HelloWorldCombiner.__init__',
        ]
    )

    # this writes out the markdown to disk    
    sequence_diagram_filename = os.path.join(os.path.dirname(__file__), '..', 'doc', 'top-level-sequence-diagram.md')
    Path(sequence_diagram_filename).write_text(sequence_diagram)

# this is the original / source test
def _test_hello_world():
    assert HelloWorldCombiner().combine() == 'Hello world'

Бег Pteest В этом коде приведет к запуску теста, а Реклама «Диаграмма как код» (ниже) создается в каталоге Дока:

sequenceDiagram
  start->>HelloWorldCombiner.combine: calls x1
  HelloWorldCombiner.combine->>hello: calls x1
  hello-->>HelloWorldCombiner.combine: returns str
  HelloWorldCombiner.combine->>world: calls x1
  world-->>HelloWorldCombiner.combine: returns str
  HelloWorldCombiner.combine-->>start: returns str

Это делает следующую диаграмму:

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

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

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

Качество кода

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

Если нет, то желание создавать хорошие диаграммы, должно вести вас также создавать хорошие тесты.

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

Заключение

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

Вся функциональность находится в пакете Python ( Docs-From-Tests) и есть Пример репо, который демонстрирует, как его использовать Отказ