Перейти к содержимому

Навыки

chimera-test-the-wiring-not-the-class

Класс, собранный вручную в тесте, доказывает, что класс работает, а не что до него что-то доходит, — покрывайте тот путь, которым система идёт в production.

ПаттернПроисхождение: cleanСостояние: activev0.1.0 · Apache-2.0

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

Когда это вспоминается

  • в тестах работает, в приложении нет
  • добавил компонент за фабрикой или реестром
  • флаг по умолчанию выключен
  • тесты зелёные, поведение сломано

Ценность обычно лежит в разделах «Избегать» и «Проверить». «Делать» — это раздел, который пишут все.

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

Повод

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

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

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

Делать

  1. Назовите точку входа, до которой пользователь действительно доходит: подкоманду CLI, HTTP-маршрут, цикл запуска агента, запланированную джобу. Запишите её до того, как писать тест.

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

  3. Стройте объект так, как его строит production, — вызывайте настоящую фабрику или загрузчик конфигурации:

    # WRONG — proves the class, not the wiring: the component is handed to the thing under test,
    # so the test passes whether or not anything in production ever hands it over.
    assert "reminder" in render(feature=Feature(text="reminder"))
    
    # RIGHT — build it the way the entry point builds it, then look for the same observable
    assert "reminder" in build_the_real_way(config).render()
    

    Собственный случай Chimera стоит назвать, потому что класс никогда не был сломан. У skill cards были работающий retriever, работающее хранилище и работающий инжектор — а chimera/config.py:244 содержит skill_cards: bool = Field(default=False, ...), поэтому на стандартной установке ничего никогда не подставлялось. Все unit-тесты проходили. Измерение, которое в итоге это поймало, посчитало отчеканенные навыки против подставленных и получило 39 против нуля.

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

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

Избегать

Собирать руками коллабораторов, которых связывает production. Форма сбоя — класс-обработчик с полным unit-покрытием, который роутер не регистрирует, или возможность, чей ключ конфигурации по умолчанию выключен: класс верен, тесты верны, а в продукте функция ничего не делает. Ничего не красное, поэтому ничего и не расследуют, и разрыв доживает до момента, когда человек попробует функцию руками.

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


monkeypatch.setattr(mod, "load_plugins", lambda: [MyPlugin()])

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

Проверить

Удалите связывание и запустите набор. Закомментируйте строку регистрации: декоратор @register, запись в словаре диспетчеризации, вызов include_router(...), значение по умолчанию в схеме конфигурации.

Затем двоичный вопрос: покраснел ли тест, и был ли это тест, который ни разу не называет ваш класс по имени?

Если набор остался зелёным — покрытие у вас только на уровне класса, а связывание не тестируется. Если единственный красный тест — тот, что конструирует класс напрямую, ответ тот же. После этого верните строку и убедитесь, что снова зелено, — и запустите git diff перед коммитом, чтобы удалённая регистрация не уехала в релиз.

Риск

Тесты через точку входа медленнее, тяжелее в отладке и хуже локализуют: когда такой падает, вы знаете, что функция сломана, но не знаете, который из десяти компонентов её сломал. Это реальная цена, и неверная реакция на эту карточку — удалить свои unit-тесты в пользу end-to-end. Держите и те и другие: тест связывания сообщает, что сломалось, unit-тест — что именно.

Проход через настоящую точку входа может к тому же задеть то, что в CI задевать не хочется: платный API, живую базу, файловую систему за пределами песочницы. Если единственный способ дойти до точки входа — потратить деньги или изменить production, не форсируйте; покройте вместо этого функцию-сборщик напрямую и примите, что вы на шаг не дошли до настоящего пути.

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

Как применить

Карточка — это данные. Склонируйте репозиторий и импортируйте её по пути: всё, что приходит по сети, считается заражённым и удерживается до одобрения — это и есть желаемое поведение, и поэтому здесь нет установщика в одну строку.

git clone https://github.com/brcampidelli/chimera-agent.git
chimera skills-import chimera-agent/skills/chimera-test-the-wiring-not-the-class/SKILL.md

Целостность

SHA-256 файла в опубликованном виде. Импортёр может проверить, что полученное совпадает с показанным на этой странице.

564f2b0aaff3bbb51ca9bfa013d89e3d47d92166f498803c688c3b859b378475

Читать карточку в репозитории