Обработка собралась. Синтаксический контроль прошёл. Валидатор формы прошёл. Автопрогон отработал и написал "ошибок нет". Отдаём.
Через час прилетает четыре падения подряд. Все четыре на том, что мы сами не нажимали.
Ниже разбор всех четырёх: что упало, почему проверки молчали и что пришлось завести, чтобы такое ловилось до выдачи, а не после. Последний случай самый неприятный: там всё дело в одной букве, и платформа про неё не говорит ни слова.
Четыре падения
| Что упало | Сообщение | Почему проверки молчали |
|---|---|---|
НачатьЗапускПриложения с Неопределено |
без текста | клиентская ветка вызывается только после сборки файла |
чтение КонсольГотова |
Преобразование значения к типу Булево не может быть выполнено | модульная переменная читалась раньше первого присваивания |
Параметры = Новый Структура(...) |
Поле объекта недоступно для записи (Параметры) | имя занято свойством управляемой формы, оно только для чтения |
Элементы.ПолеТаблицы |
Поле объекта не обнаружено (ПолеТаблицы) | элемент был в Form.xml, но загрузчик выбросил его молча |
Общее у всех четырёх: синтаксис корректен. Поэтому и молчали проверки. Синтаксический контроль смотрит на текст модуля, а падает выполнение.
Случай первый и второй: состояние, которого ещё нет
Два первых падения это один класс, хоть и выглядят по-разному.
Модульная переменная объявлена, но присваивается в обработчике, который к моменту чтения ещё не отработал:
&НаКлиенте Перем КонсольГотова; &НаКлиенте Процедура ОбновитьСостояние() Если КонсольГотова Тогда // здесь Неопределено, а не Ложь ...
В 1С необъявленное состояние это Неопределено, а не Ложь. Условие с ним не сравнивает, а падает. Синтаксически всё безупречно: переменная объявлена, тип не указан, потому что его и нельзя указать.
Лечится одной строкой в ПриОткрытии или проверкой через ЗначениеЗаполнено. Но найти это можно только выполнением, причём выполнением именно в том порядке, в котором нажимает пользователь.
То же со вторым: НачатьЗапускПриложения получил Неопределено вместо пути, потому что путь заполняется в другой ветке, которая при этом сценарии не отрабатывала.
Вывод отсюда скучный, но полезный: клиентские ветки, которые вызываются только после какого-то шага, автопрогоном не покрываются, если прогон этот шаг не делает. У нас прогон жал одну команду из трёх.
Случай третий: имя, которое уже занято платформой
Это ловушка, на которую наступают все, кто пишет формы руками:
&НаСервере Процедура ЗаполнитьНастройки() Параметры = Новый Структура("Путь, Режим"); // Поле объекта недоступно для записи
У управляемой формы есть собственное свойство Параметры, и оно только для чтения. Своя переменная с таким именем в модуле формы объявиться не может: имя разрешается в свойство формы, и присваивание падает.
Полный список того, во что нельзя писать, платформа нигде не печатает одним местом, но практически это Параметры, Элементы, Команды, Модифицированность, ЭтотОбъект и ещё несколько. Ошибка вылезает только при выполнении конкретной процедуры.
Мы завели на это отдельную проверку: скрипт проходит по модулю формы и ищет присваивание в любое имя из закрытого списка. Проверка тупая, ловит за секунду, а стоила бы иначе одного падения у покупателя.
Случай четвёртый: одна буква, и элемента в форме нет
Вот это стоит запомнить всем, кто собирает формы из XML, генерирует их скриптами или правит выгрузку руками.
В Form.xml был элемент с тегом:
<SpreadsheetDocumentField name="ПолеТаблицы">
Правильно пишется SpreadSheetDocumentField, с заглавной S в середине. Разница в одной букве.
Что делает загрузчик конфигурации: выбрасывает такой элемент молча. Не ошибка, не предупреждение, не строка в лог. Сборка проходит успешно, файл получается, форма открывается. Элемента в ней просто нет, и первое же обращение Элементы.ПолеТаблицы падает с "Поле объекта не обнаружено".
При этом человек смотрит на Form.xml, видит там свой элемент и делает единственно возможный вывод: сломан модуль. Полчаса уходит на отладку кода, в котором всё правильно.
Это не единичная особенность одного тега. Ровно так же у нас пропадал VerticalStretch, написанный с другим регистром. Механика общая: неизвестный тег в форме не является ошибкой загрузки, он просто игнорируется.
Как проверять: после сборки сверять состав Form.xml с тем, что фактически оказалось в форме, и отдельно проверять теги по белому списку платформы. У нас это второй скрипт, и он ловит оба симптома: и пропавший элемент, и тег, которого не существует.
Дешёвый вариант без скриптов: собрать, открыть форму в конфигураторе и посмотреть глазами, все ли элементы на месте. Работает, пока элементов десяток.
Как воспроизвести это за пять минут
Если не верите, что платформа молчит, проверьте на любой тестовой обработке:
- Выгрузите обработку в XML.
- Найдите в
Form.xmlлюбой элемент и поменяйте регистр одной буквы в имени тега: например,CheckBoxFieldнаCheckboxField. - Загрузите обратно и соберите.
- Откройте форму.
Сборка пройдёт без единого сообщения. Элемента в форме не будет. В Form.xml, который лежит у вас на диске, он при этом на месте, и глазами вы его видите.
Отсюда практический признак для диагностики: если модуль падает на "Поле объекта не обнаружено", а элемент в XML есть, смотрите не на модуль, а на написание тега. Причём именно на регистр: платформа различает SpreadSheetDocumentField и SpreadsheetDocumentField как разные теги, из которых второй ей неизвестен.
Отличить "элемент выброшен" от "элемент не создан" помогает простая проверка прямо из модуля:
&НаСервере Процедура ПроверитьСоставФормы() Для Каждого Элемент Из Элементы Цикл Сообщить(Элемент.Имя + " / " + ТипЗнч(Элемент)); КонецЦикла; КонецПроцедуры
Список того, что реально загрузилось, сверяется с тем, что вы писали в XML. Расхождение и есть ответ.
Что должен жать автопрогон
После этой истории у нас прогон устроен так, и это минимум, который окупается:
- каждая команда каждой формы, а не главный сценарий. Команда, которая открывает другую форму, тоже жмётся, и в открывшейся форме жмутся её команды;
- результат пишется после каждого нажатия, а не общим итогом. Общий итог "ошибок нет" ровно так и скрыл, что до третьей кнопки прогон не доходил: он падал молча и заканчивался успехом;
- проверяется не отсутствие исключения, а результат: таблица заполнилась, файл создался, поле не пустое. Команда, которая тихо ничего не сделала, для прогона это успех, а для пользователя нет;
- прогон запускается на пустой базе тоже. Половина падений на состоянии вылезает именно там, где нет данных и переменные остаются незаполненными.
Последний пункт добавился позже и по такому же поводу: метрика, которая на нагруженном стенде считалась прекрасно, на пустом кластере пропадала целиком, и панель у покупателя оказывалась пустой в первый час знакомства с продуктом.
Почему все проверки были зелёными
Стоит разобрать отдельно, потому что это общий случай, а не частность про формы.
У нас было три уровня проверки, и каждый честно делал свою работу:
- синтаксический контроль смотрит текст модуля. Все четыре падения синтаксически корректны, ему не за что зацепиться;
- валидатор формы проверяет структуру XML: обязательные атрибуты, корректность вложенности. Тег с опечаткой структуру не нарушает, он просто лишний;
- автопрогон запускает обработку и жмёт кнопку. Одну. Из трёх.
Каждый уровень отвечал "у меня чисто", и вместе они складывались в ощущение покрытия, которого не было. Это и есть самое дорогое в истории: не четыре падения, а ложная уверенность перед выдачей.
Сравните с тем, как выглядит правда: покрытие было одна команда из трёх, то есть 33 %, а отчёт печатал "ошибок нет" и выглядел как 100 %.
Отдельно стоит сказать про соблазн, который возникает после такой истории: добавить ещё один уровень проверки и успокоиться. Он не помогает, если новый уровень смотрит туда же, куда смотрели прежние. Четвёртое падение не поймал бы ни один анализатор кода, каким бы умным он ни был: в коде всё правильно, ошибка живёт в XML формы и проявляется только тем, что элемент не доехал. Проверка должна была сравнить два состояния между собой, а не проверить каждое по отдельности, и в этом вся разница.
Тот же принцип работает и в обратную сторону. Когда среда падает без внятного текста, первым делом стоит прогнать через неё заведомо исправный артефакт: чужую рабочую обработку, простейший запрос, пустую форму. Одна минута, а разделяет "сломан мой код" и "сломана среда". У меня был случай, когда HTTP-сервис отдавал страницу 500 вместо JSON на любой вызов, и полчаса это выглядело как дефект модуля, пока тот же вызов не упал точно так же на заведомо рабочем файле. Дело оказалось в правах пользователя сервиса, а не в коде.
Что мы поменяли
Три вещи, все дешёвые:
- Автопрогон жмёт каждую команду каждой формы, включая те, что открывают другие формы, и пишет результат после каждой, а не общий итог в конце. Общий итог скрывал, что до третьей кнопки прогон не доходил.
- Проверка присваиваний в свойства формы по закрытому списку имён.
- Сверка Form.xml с фактическим составом формы плюс проверка тегов по белому списку.
Оба скрипта проверены воспроизведением: специально ломали регистр буквы в теге и специально писали в Параметры, оба симптома ловятся.
И четвёртое, не техническое. Перед тем как писать форму, стоит посмотреть, делали ли мы такую раньше. Форма справки у нас к тому моменту была написана пять раз, и во всех пяти это одно поле HTML-документа с модулем в шесть строк. Здесь я написал своё устройство с двумя полями и сторожем готовности, и именно оно принесло второе падение. Копирование работающего скучнее, но дешевле.
Правило, которое я вынес
Кнопка, которую не нажал автопрогон, это кнопка, которую нажмёт пользователь.
Формулировка звучит очевидно, пока не посмотришь на свой прогон и не посчитаешь, сколько команд он реально трогает. У нас было три команды и одна нажималась.
Второе следствие шире: отсутствие ошибки не равно успеху. Загрузчик, который молча выбрасывает неизвестный тег, сообщает об успехе. Прогон, который дошёл до первой кнопки, сообщает об успехе. Признаком успеха стоит считать полезный результат (элемент есть в форме, команда отработала и вернула данные), а не молчание инструмента.
Вопрос
Мне интересно, как с этим у тех, кто собирает формы генераторами. Судя по тому, что теги выбрасываются без предупреждения, наступать на это должны регулярно, но в обсуждениях я такого почти не встречал: обычно пишут про ошибки загрузки, а не про молчаливое исчезновение.
Если у вас есть способ заставить платформу ругаться на неизвестные теги формы при загрузке, напишите, пожалуйста. Я такого ключа не нашёл, и проверку пришлось делать снаружи, своим скриптом. Возможно, зря.
Вступайте в нашу телеграмм-группу Инфостарт