Неблагодарное это дело – выдавать сообщения об ошибках

28.09.13

Разработка - Тестирование QA

Методика формирования и выдачи сообщений об ошибках. Описывается способ работы над ошибками в данных, прилагается программный код. Приводятся примеры.

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

Это хороший тест, проверьте себя. Прагматы выбирают первый вариант, зануды (не в обиду) -  второй, а гении … - вообще игнорируют Smile.

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

Ниже приводится простая процедура для преодоления этих трудностей. В комментариях к ней приводится пример использования.

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

 

// Процедура обрабатывает текст ошибки
//
// Назначение:
//
//     1. Иерархическая обработка ошибок.
//        На каждом уровне иерархии ошибку можно дополнить уточняющими
//        сведениями о месте ее возникновения.
//        Примерамии иерархии являются: вложенный вызовов процедур,
//        вложенные циклы, вложенные условия "Если Тогда Иначе" и т. д.
//
//     2. Гибкость выбора места вывода ошибок.
//        Вопрос: где выводить ошибку, в процедуре, где она возникла,
//        или в вызывающей процедуре решается гибко.
//        Если внешнюю процедуру ошибка интересует, то ошибка будет передана ей,
//        иначе вывод ошибки будет сделан во внутренней процедуре.
//
//     3. Накопление списка ошибок и выдачи их единым результатом,
//
//
//
// Параметры
//
//     ТекстНакопительОшибок - либо Неопределено, либо строка. Входной и выходной параметр.
//                             Текст, содержащий список ошибок
//     ТекстОшибки           - строка, входной параметр.
//                             Текст ошибки или собранный ранее накопитель ошибок
//     КоординатыОшибки      - любой тип, входной параметр.
//                             Приводится к строковому типу. Текст информирующий пользователя
//                             о месте возникновения ошибки. Следует указать место в
//                             именительном падеже, например "Строка 1" или ЭтотОбъект.Ссылка
//
//
// Описание
//
// 1. Если ТекстНакопительОшибок - имеет тип "Строка", то ТекстОшибки и КоординатыОшибки
//    будут добавлены в ТекстНакопительОшибок, при этом, вывода на экран не будет
//
// 2. В противном случае ТекстОшибки и КоординатыОшибки будут выведены на экран
//
// Замечание 1. Повторные одинаковые ошибки накопитель отбрасывает
// Замечание 2. Если ТекстОшибки - не простая ошибка, а уже ранее собранный накопитель
//              ошибок, то КоординатыОшибки будут добавлены слева к уже существующим
//              координатам каждой из ошибок
//
//
//
// Пример.
//
//
//      Процедура ОбработатьСтроку(НомерСтроки, Ошибки = Неопределено)
//          ОшибкуОтдать(Ошибки, "Лимит превышен", "Строка " + НомерСтроки);
//      КонецПроцедуры
//
//
//      Процедура ВариантСУточнением(Ошибки = Неопределено)
//          Ош = "";
//          ОбработатьСтроку(1, Ош);
//          ОбработатьСтроку(2, Ош);
//          Если ЗначениеЗаполнено(Ош) Тогда
//              ОшибкуОтдать(Ошибки, Ош, "ТЧ Данные");  // отдаем ошибку нижнего уровня наверх,
//                                                      // уточняя своими координатами
//          КонецЕсли;
//      КонецПроцедуры
//
//
//      Процедура ВариантБезУточнения(Ошибки = Неопределено)
//          ОбработатьСтроку(1, Ошибки);             // Проносим ошибку без контроля
//          ОбработатьСтроку(2, Ошибки);             //
//      КонецПроцедуры
//
//
//      Процедура Испытание()
//          ВариантСУточнением();                    // выдаст:
//                                                   //     Лимит превышен. См. ТЧ Данные, Строка 1
//                                                   //     Лимит превышен. См. ТЧ Данные, Строка 2
//
//          Ошибки = "";
//          ВариантСУточнением(Ошибки);
//          Если Ошибки <> "" Тогда
//              ОшибкуОтдать(, Ошибки, "Испытание"); // выдаст:
//                                                   //     Лимит превышен. См. Испытание, ТЧ Данные, Строка 1
//                                                   //     Лимит превышен. См. Испытание, ТЧ Данные, Строка 2
//          КонецЕсли;
//
//
//
//          ВариантБезУточнения();                   // выдаст:
//                                                   //     Лимит превышен. См. Строка 1
//                                                   //     Лимит превышен. См. Строка 2
//
//          Ошибки = "";
//          ВариантБезУточнения(Ошибки);
//          Если Ошибки <> "" Тогда
//              ОшибкуОтдать(, Ошибки, "Испытание"); // выдаст:
//                                                   //     Лимит превышен. См. Испытание, Строка 1
//                                                   //     Лимит превышен. См. Испытание, Строка 2
//          КонецЕсли;
//      КонецПроцедуры
//
//

Процедура ОшибкуОтдать(ТекстНакопительОшибок = Неопределено, ТекстОшибки = "", КоординатыОшибки = Неопределено) Экспорт

    Если не
ЗначениеЗаполнено(ТекстОшибки) Тогда
        Если не
ЗначениеЗаполнено(ТекстНакопительОшибок) или не ЗначениеЗаполнено(КоординатыОшибки) Тогда
            Возврат;
        КонецЕсли;
    КонецЕсли;

   
РазделительСписка = "..|..";    // между ошибками
   
Сцепка = ":,:";                 // между текстом ошибки и координатами


    // Возможно пользователь использовал Символы.ПС в своих нуждах, поэтому
    // временно заменим все Символы.ПС на другую комбинацию символов.
    // Потом вернем назад

   
ВместоПС = "{;}";                 // временно заменяет Символы.ПС



   
ТекстНак = СтрЗаменить(СокрЛП(ТекстНакопительОшибок), Символы.ПС, ВместоПС);
   
ТекстНак = СтрЗаменить(ТекстНак, РазделительСписка, Символы.ПС);

   
ТекстТек = СтрЗаменить(СокрЛП(ТекстОшибки), Символы.ПС, ВместоПС);
   
ТекстТек = СтрЗаменить(ТекстТек, РазделительСписка, Символы.ПС);

   
Коорд    = СокрЛП(КоординатыОшибки);


   
// Разложение параметра ТекстНакопительОшибок в массив строк
   
мвОшибки = Новый Массив;
    Для
к = 1 По СтрЧислоСтрок(ТекстНак) Цикл
       
мвОшибки.Добавить(СтрПолучитьСтроку(ТекстНак, к));
    КонецЦикла;


   
// Разложение параметра ТекстОшибки на строки и их добавление в массив строк
   
Для НомерСтроки = 1 По СтрЧислоСтрок(ТекстТек) Цикл

       
ТекстСтроки = СокрЛП(СтрПолучитьСтроку(ТекстТек, НомерСтроки));
       
КоордСтроки = "";


       
к = Найти(ТекстСтроки, Сцепка);
        Если
к > 0 Тогда
           
КоордСтроки = СокрЛП(Сред(ТекстСтроки, к + СтрДлина(Сцепка)));
           
ТекстСтроки = СокрЛП(Лев(ТекстСтроки, к-1));
        КонецЕсли;


        Если
Коорд <> "" Тогда
            Если
КоордСтроки = "" Тогда
               
КоордСтроки = Коорд;
            Иначе
               
КоордСтроки = Коорд + ", " + КоордСтроки;
            КонецЕсли;
        КонецЕсли;


        Если
КоордСтроки <> "" Тогда
           
ТекстСтроки = ТекстСтроки + Сцепка + КоордСтроки;
        КонецЕсли;


        Если
мвОшибки.Найти(ТекстСтроки) = Неопределено Тогда
           
мвОшибки.Добавить(ТекстСтроки);
           
ТекстНак = ТекстНак + ?(ТекстНак = "", "", Символы.ПС) + ТекстСтроки;
        КонецЕсли;

    КонецЦикла;




    Если
ТипЗнч(ТекстНакопительОшибок) = Тип("Строка") Тогда

       
ТекстНак = СтрЗаменить(ТекстНак, Символы.ПС, РазделительСписка);
       
ТекстНак = СтрЗаменить(ТекстНак, ВместоПС, Символы.ПС);

       
ТекстНакопительОшибок = ТекстНак;

    Иначе

       
ТекстНак = "";
        Для каждого
ТекстСтроки Из мвОшибки Цикл
           
ТекстСтр = ТекстСтроки;
            Если
СтрДлина(ТекстСтр) > 100 Тогда
               
ТекстСтр = СтрЗаменить(ТекстСтр, Сцепка, ВместоПС + "См. ");
            Иначе
               
ТекстСтр = СтрЗаменить(ТекстСтр, Сцепка, ". См. ");
            КонецЕсли;
           
ТекстНак = ТекстНак + ?(ТекстНак = "", "", РазделительСписка) + ТекстСтр;
        КонецЦикла;

       
ТекстНак = СтрЗаменить(ТекстНак, РазделительСписка, Символы.ПС); // каждую ошибку с новой строки
       
ТекстНак = СтрЗаменить(ТекстНак, ВместоПС, Символы.ПС);          // обратная замена

       
Сообщение = Новый СообщениеПользователю;
       
Сообщение.Текст = ТекстНак;
       
Сообщение.Сообщить();

    КонецЕсли;


КонецПроцедуры

Вступайте в нашу телеграмм-группу Инфостарт

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

HighLoad оптимизация Тестирование QA Программист 1С 8.3 Бесплатно (free)

Шесть внешних отчётов на боевой базе крупной сети переписали за неделю. По секундомеру ускорились четыре, в акт пошли три: у одного отчёта минус тринадцать процентов оказались выбросом нагрузки прода, а не эффектом кода. Поймал это чередующийся замер до/после в одном окне, обычные три прогона с метрикой "минимум" показывали ускорение уверенно. Дальше в тексте: почему построчная сверка выхода ломается ровно на тех правках, ради которых её заводят; из чего собирается повторяемый слепок результата и почему главную работу в нём делает нормализация, а хеш только сигнализация; где слепок не берётся вовсе и его заменяет по-колоночная сверка; чем проверять пересчёт итогов, чистку данных и операции с кластером. Отдельно - гипотеза, которую замер отклонил, и честный счёт, во что вся эта дисциплина обходится.

21.08.2026    509    nedomolkov.ivan    0    

0

Тестирование QA Программист Бесплатно (free)

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

17.08.2026    557    user2181343    0    

1

Тестирование QA Программист 1С 8.3 Россия Бесплатно (free)

Написать тест на 1С несложно. Дорого стоит привести базу в состояние, где прогон вообще стартует: семь шагов, каждый ломает запуск молча — без ошибки, без записи в журнале. И эту цену платит заново каждый новый человек на проекте. Разбираю молчаливый отказ окружения как класс проблем, показываю, почему его не закрывают ни документация, ни скрипт развёртывания, и предлагаю решение — фиксацию состояния в артефакте с машинно-проверяемым критерием приёмки. Подход проверен на сквозной задаче (первая задача экзамена «1С:Специалист по платформе» с юнит- и функциональными тестами), а затем перенесён на пул тестовых баз с типовыми конфигурациями. Отдельно — каталог симптомов и их настоящих причин, который пригодится независимо от инструментария.

13.08.2026    868    chagbig    1    

0

Тестирование QA Программист Бесплатно (free)

Разбираем, как в крупных компаниях регуляторные требования, постмортемы и внутренние ограничения постепенно превращаются в многоуровневую систему инструкций, в которой разработчику все сложнее понять, что и когда нужно проверить. Показываем opensource-сервис внешних проверок для GitLab, который автоматизирует часть этой бюрократии и сводит результат к понятному сигналу: зеленое – все в порядке, красное – нужно обратить внимание на конкретное требование. Объясняем, как такие проверки помогают «сдвинуть влево» контроль задач и снизить риск отказа во внедрении задачи в последний момент перед релизом. А заодно смотрим, может ли связка Autumn + «Вино» + немного разработческого энтузиазма превратить обязательные инструкции в инструмент, который команда сама захочет развивать.

12.08.2026    782    Golovanoff    2    

3

Поиск данных Тестирование QA Программист 1С 8.3 1С 8.5 Бесплатно (free)

Как мы пришли к Юнит-тестированию и почему стоит его использовать. Использование универсальных тестов для проверки работы IS MagicInput в вашей конфигурации.

16.07.2026    1984    Evg-Lylyk    2    

5

Тестирование QA Программист Бесплатно (free)

Tantor Postgres 18 - масштабный релиз СУБД, за которым стоят месяцы тестирования, сотни часов нагрузочных прогонов и десятки исправлений, о которых пользователь никогда не узнает просто потому, что они были найдены и устранены до выхода версии. Александр Симонов, руководитель направления развития 1С в "Тантор Лабс", рассказывает, как устроен процесс тестирования изнутри - почему одного эталонного прогона недостаточно, что делать, когда ванильный PostgreSQL 18 ломает собственные оптимизации, и как Tantor Postgres приближается к той планке, которую MS SQL Server держал годами.

07.07.2026    1415    Tantor    2    

6

Тестирование QA Программист Бесплатно (free)

Юнит-тестирование в 1С – тема, о которой много говорят, но внедряют далеко не везде: для одних это признак зрелой разработки, для других – избыточная формальность или лишняя трата времени. Разбираем, почему классическая пирамида тестирования в 1С часто оказывается перевернутой, какие ограничения накладывает платформа и почему юнит-тесты в реальных проектах нередко смешиваются с интеграционными. Показываем, где юнит-тесты действительно помогают ускорить разработку, упростить проверку логики и повысить качество кода, а где их применение превращается в хрупкую и дорогую поддержку. Отдельно разбираем типичные ошибки: зависимость тестов от данных и окружения, избыточное мокирование, тестирование платформы вместо бизнес-логики и отсутствие негативных кейсов.

29.06.2026    3032    alexandr_yang    6    

12

Тестирование QA Программист 1С:Предприятие 8 Бесплатно (free)

Как мы пришли к использованию Ванессы для автоматизации действий пользователей? Сначала я увидел в интернете лекцию Олега Филиппова про RPA. И когда встал вопрос про автоматизацию небольших процессов, то эта лекция у меня прекрасно соединилась с опытом тестирования программ с помощью Vanessa Automation. Минимум усилий, минимум ручного кода и высокая скорость внедрения. И самое главное, не надо менять программу. А если программа изменится, то алгоритм быстро поправить и дополнить.

19.06.2026    2173    swimdog    3    

8
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. maxx 1002 29.09.13 19:55 Сейчас в теме
Идея хорошая, я бы еще добавил в объект СообщениюПользователя путь к данным объектов или мест где возникла ошибка, чтобы навигация работала по списку ошибок, т.е нажал по ошибке и перешел сразу к месту где ошибка
2. Abadonna 3979 30.09.13 04:34 Сейчас в теме
(0)
// Возможно пользователь использовал Символы.ПС в своих нуждах, поэтому
// временно заменим все Символы.ПС на другую комбинацию символов.
// Потом вернем назад

ВместоПС = "{;}";

Рекомендую: (ALT+0134) (ALT+0135) - никогда простой юзверь такое на клавиатуре не наберет ;)
CratosX; rkravtsov; +2 Ответить
3. Yashazz 4933 30.09.13 12:45 Сейчас в теме
Да, уже витает в воздухе.
Я себе смастерил накопитель в виде дерева, которое отслеживает "погружение" в вызовы различных процедур да функций; а потом вывожу его в макет с расшифровкой, и показываю, где были какие сообщения. Красиво и удобно, но кода требует, конечно.
4. ufo58 161 30.09.13 17:12 Сейчас в теме
все бы ничего
и идея хороша, и посыл нормальный
но ... выводить "Сообщением", тем более в управляемых формах ... бррр
да и Дерево ... тяжеловато
подумай, может дать вариант формирования тип Текст ?
с последующим просмотром
или сохранением ...
5. TitanLuchs 639 02.10.13 14:42 Сейчас в теме
Присоединюсь к предыдущему оратору. При возможности тоже стараюсь избегать использование "Сообщить" и "СообщениеПользователю" в УФ, потому что перекореживание формы в этот момент - это иногда зрелище не для слабонервных. Одно время в своей конфе открывал отдельное окно с сообщениями, на быстродействии это не сказывалось (потому что форма все равно перерисовывается при изменении размера), но юзеров не так бесило.
6. pakill 43 03.10.13 09:38 Сейчас в теме
Вообще-то я старался сделать легкую процедуру, не зависящую ни от конфигурации, ни от контекста.
Писать что-то тяжеловесное на тот случай, который может никогда и не произойти как-то не хотелось. Я лишь ставил цель, предоставить пользователю шанс самостоятельно разобраться и исправить ошибку в данных, если таковая возникнет.

Насчет вывода ошибки с помощью Сообщить():
А как ее еще выводить на стороне сервера?

Это можно сгладить лишь частично:

#Если Сервер Тогда
Сообщить(...);
#Иначе
Предупреждение(...);
#КонецЕсли

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

7. Yashazz 4933 03.10.13 12:57 Сейчас в теме
(6) Вот я именно и заменил "Сообщить" своей процедурой. И УФ не корёжит, и на сервере работает, в протокол пишет.
8. devel0per 1 08.10.13 02:08 Сейчас в теме
Хоть комментарий к процедуре и прекрасен, но из него не ясно чем плох отладчик, ведь
нам никто не запрещает им пользоваться на сервере.
Не лучше ли будет вместо:
Коорд    = СокрЛП(КоординатыОшибки);

и
Если Коорд <> "" Тогда

писать:
Если ПустаяСтрока(Коорд) Тогда

Вот не помню в клюшках функц. ПустаяСтрока была?
14. pakill 43 09.10.13 02:44 Сейчас в теме
(8). Чем же плох отладчик? Чтож. Дайте пользователю отладчик. Пусть ищет и исправляет ошибки в данных (подчеркиваю в данных, а не в коде). Ну а я, как-то не готов давать пользователю отладчик.

Коорд = СокрЛП(..) // это чтобы вместо: "текст , текст" всегда было так: "текст, текст"
Если Коорд <> "" Тогда

Здесь первая строка нужна сама по себе, а не для того, чтобы выполнить проверку во второй строке.
Но поскольку она все же есть, то не вижу ничего плохого чтобы сравнить <> ""


(12). Насчет Попытка-Исключение.
А Вы попробуйте воспроизвести в точности тот пример из комментария с помощью попытки-исключения.
И сразу же выяснится, что вы будете заниматься разбором строк в каждой процедуре, где используете эту конструкцию. Кроме того, у Вас возникнут трудности при передаче списка ошибок. И чтобы облегчить жизнь придется написать свою процедуру, занимающуюся разбором строк и накоплением ошибок.
Но скорее всего, Вы просто используете ту самую процедуру ОшибкуОтдать(), приведенную выше.
Так что, попытка-исключение - это всего лишь, один из способов ее применения.


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


Заметьте, принципиальную разницу: сообщение может быть выдано не только на верхнем уровне, а на любом из уровней. В примерах как раз показан вывод из разных процедур. И это зависит от того, как вызвана процедура.
В Вашем случае - это наличие или отсутствие конструкции попытка-исключение в процедурах верхнего уровня.
Это значит, что "ВызватьИсключение" - это и есть Ваш способ вывода ошибки, т.е. Вы с этим уже заранее определились.
9. WalterMort 247 08.10.13 13:49 Сейчас в теме
Автор изобрел велосипед, вместо грамотной работы с исключениями.
10. Evil Beaver 8412 08.10.13 13:56 Сейчас в теме
(9) WalterMort, ну тогда уж приводите пример "грамотной" работы с исключениями в рамках поставленной задачи. Что вы понимаете под "грамотной" работой?
12. WalterMort 247 08.10.13 14:55 Сейчас в теме
(10) Обычная передача ошибок снизу наверх. Классов исключений в 1С нет, но описания ошибки для замысла автора достаточно:

Процедура Внешняя()
Попытка
Внутренняя();
Исключение
ВызватьИсключение "Невозможно выполнить внешняя: " + ОписаниеОшибки(); // наверх
КонецПопытки;
КонецПроцедуры

Процедура Внутренняя()
Попытка
а = б/0;
Исключение
ВызватьИсключение "Невозможно выполнить внутренняя"; // передаем наверх
КонецПопытки;
КонецПроцедуры


Результат: "Невозможно выполнить внешняя: Невозможно выполнить внутренняя"



Ну а исключение клиентского (самого высокого) уровня уже можно как угодно отработать. Сообщением, предупреждением, или записью в лог.
13. Evil Beaver 8412 08.10.13 16:05 Сейчас в теме
(12) WalterMort, вот поскольку классов исключений нет, то все, что могут передать исключения - это строки. Отсюда, шибко "грамотной" работы с исключениями все равно не будет.
11. Bukaska 158 08.10.13 13:56 Сейчас в теме
(9) WalterMort, Ой да ладно.. ну что же вы все как эти... даже не знаешь как назвать... Я конечно понимаю что нужно реализовываться, знаю что и инициатива бывает наказуема. Но кто не работает, тот не ест)))
15. monkbest 114 27.05.16 09:10 Сейчас в теме
С пользователем на УФ надо разговаривать правильно :)
http://prosto1s.ru/index.php/59-soobshcheniya-polzovatelyu-v-upravlyaemykh-formakh-eshche-raz
Для отправки сообщения требуется регистрация/авторизация