Несмотря на простоту использования Тестера, внедрение тестирования требует определенных усилий. На практике, в среднем, нужно от недели до двух для того, чтобы адаптироваться к разработке с Тестером.
Перед погружением в детали, определимся с тем, когда автоматизированное тестирование применять неэффективно:
- Вы создаете макет, прототип приложения для демонстрации заказчику
- Вы вносите точечные исправления в старый код, в те механизмы, сценариев к которым нет и не будет, и вся эта работа без перспектив эволюции модулей
- Вы прощупываете будущую реализацию, когда еще нет четкого понимания, что это должно быть, документ или справочник, отчет или динамический список. В этом случае, программист нередко переключается в режим “потока” и быстро накидывает объекты метаданных для того, чтобы доказать или опровергнуть самому себе достижимость результата выбранным путем. В таких ситуациях, практически невозможно выработать сценарий, но это и не нужно. Строго говоря, пока не сформируется полное представление о способе выполнения задачи, сценарное тестирование применять не стоит
Процесс внедрения нужно начинать плавно. Вначале это может быть один программист или аналитик и небольшой проект, затем, когда в коллективе уже кто-то будет владеть данной техникой, и сможет оперативно отвечать на вопросы, можно постепенно расширять область действия Тестера.
Установка¶
Если вы занимаетесь обслуживанием клиентов частным образом, Тестер имеет смысл установить локально на ваш компьютер или ноутбук, файловый вариант.
Если вы работаете в команде, вне зависимости от того, работают все программисты над одним проектом или у каждого свой, Тестер желательно установить в локальной сети или облаке. Если ваш коллектив более 3 программистов, я бы рекомендовал установить клиент-серверный вариант платформы.
Даже если ваша команда сильно распределена, с пингом до ~125мс всё еще можно комфортно работать через интернет, разработка Тестера велась с учетом удаленности программистов. Если по каким-то причинам, облачное решение организовать не получается, можно установить программу локально на каждый компьютер специалиста, файловый вариант. Для организации общего хранилища тестов, можно использовать Git. У Тестера есть возможность инкрементальной выгрузки/загрузки тестов в файловую систему.
Кроме этого, для организации ночного тестирования, потребуется установить на выделенном для этого сервере или виртуальной машине тонкий клиент 1С:Предприятие, с возможностью его подключения к облаку, где ведется разработка тестов. В случае использования Git для хранения тестов, нужно будет обеспечить предварительную закачку тестов на сервер тестирования, с использованием утилит от Git. Подробнее, ночное тестирование описано разделе Запуск тестов по расписанию.
Тестер позволяет в одной базе вести работу с тестами для неограниченного числа приложений (конфигураций). Не стоит создавать отдельную базу с Тестером под каждый проект/конфигурацию/клиента. В Тестере возможна настройка ограничения доступа пользователей к приложениям.
Базы данных¶
Тестирование при помощи Тестера предполагает наличие двух баз данных: рабочей и начальной.
Рабочая база - это база данных в которой работает программист, и в которой выполняются все тесты. Соответственно, у каждого программиста своя рабочая база.
Начальная база - это база, заполненная начальными данными. Начальная база нужна для того, чтобы периодически создавать/перегружать рабочие базы. Наполненность начальной базы зависит от типа выполняемых работ. Начальная база общая для всех программистов.
Создание и обновление начальной базы¶
Если вы разрабатываете типовую конфигурацию, за основу начальной базы можно взять начальную базу, которую вы будете поставлять с вашей конфигурацией. В этой базе (подготавливаемой для тестирования, а не поставки в решении), рекомендуется включить все функциональные опции, добавить несколько пользователей с разными ролями, заполнить стандартную нормативно-справочную информацию, создать как минимум одну организацию, склад, подразделение и другое, в зависимости от специфики вашего решения.
Если вы дорабатываете типовую конфигурацию, в качестве начальной базы может выступать база клиента, или демонстрационная база типового решения, обновленная и настроенная спецификой вашего заказчика.
Кроме самого факта существования начальных данных, я рекомендую разработать и поддерживать тест, который будет проверять начальную базу на согласованность данных. Например, вам для работы тестов требуется определенный курс валюты, или наличие графика работ Пятидневка, или должен быть заполнен регистр адресации бизнес-процессов типовыми ролями и исполнителями и т.д. С ростом функционала вашей системы, и возможным, в этой связи, расширением начальных данных – доработка теста проверки начальной базы будет всегда гарантировать понимание базовых условий.
Использовать такой тест удобно в следующих случаях:
- Каждый программист/аналитик/тестировщик может четко проверить консистентность своей рабочей базы перед тем как вообще что-то тестировать (напомню, рабочая база создается на основании начальной)
- Данный тест можно запускать первым в списке тестов для ночного тестирования, по результатам которого, определить целесообразность дальнейшего тестирования
- Тест хорошо работает как напоминание разработчику, какие данные можно брать не думая об их существовании, а какие нужно создавать дополнительно. В процессе работы, рабочая база превращается в хлам, и помнить, какие данные там начальные, а какие нет, сложно. Разворачивать каждый раз начальную базу последней версии – может быть не всегда удобно или возможно, при этом, быстрое определение происхождения данных при написании сценария очень важный момент. Например, в базе есть группа материалов “Детали насосно-компрессорного оборудования”, и если есть тест проверки начальных данных, в нем можно произвести поиск по коду и вычислить, происхождение этой группы, буквально в несколько кликов.
Примечание
В качестве решения проблемы загрязнения рабочей базы я не рассматриваю откат транзакций в сценарном цикле, что вы можете найти как подход в некоторых авторитетных источниках. По моему опыту, используя такой подход говорить о сколь-нибудь серьёзном тестировании не приходится.
Начальную базу нужно подключить к хранилищу, куда сливаются финальные обновления конфигурации. Это необходимо для оперативного обновления начальной базы новым функционалом, обновления и/или заполнения базы новыми начальными данными.
Если над решением трудится коллектив, начальную базу нужно расположить в общедоступном месте, чтобы каждый специалист мог в любой момент "обнулить" свою рабочую базу, загрузив в неё начальную.
Если новый функционал требует обновления/добавления начальных данных, такую задачу можно решить двумя способами:
- Ответственный разработчик, открывает обновленную начальную базу, дополняет вручную нужными данными, дорабатывает тест проверки начальных данных, запускает этот тест, убеждается в согласованности данных, и выгружает полученную базу в общедоступное место
- Всё тоже самое что в п.1 с той разницей, что вместо ручного обновления, ответственный разработчик, пишет сценарный тест.
Второй вариант дольше, но предпочтительней, потому что позволит всем участникам команды обновлять свои рабочие базы без обнуления начальной выгрузкой данных.
Начальные данные желательно использовать в режиме “только чтение" по отношению к тестируемому функционалу. Например, если у вас в начальных данных определена организация по умолчанию, и у вас есть тест, который проверяет механизм ввода новой/изменения/удаления организации, то лучше в этом тесте не использовать в качестве тестовой организации, существующую организацию в начальной базе. Даже если вашим тестом предусмотрен откат изменений к начальному состоянию, нет гарантии, на каком этапе упадет ваш тест. Когда это произойдет, он оставит после себя испорченные начальные данные, что может привести к цепной реакции падения тестов, выполняемых ночью, по расписанию.
Если вашим тестам нужно изменять начальные данные, тогда в начале таких тестов проверяйте, и при необходимости, устанавливайте их начальное состояние. После отработки сценария, верните эти настройки в исходное положение. Если такой тест упадет, тогда другой тест, зависимый от этих же настроек, восстановит начальные значения в исходное состояние перед своим стартом.
Например, если ваш тест проверяет работу механизма контроля остатков в зависимости от системной настройки приложения, вашему тесту придётся изменять эту настройку. Если ваш тест упадет, настройка не будет возвращена в первоначальное положение и следующий запуск этого теста уже вряд ли отработает правильно, даже если с функциональной точки зрения всё в порядке. Таким образом, вначале желательно тестом сходить в настройки, установить требуемое значение и затем продолжить основной сценарий. К слову, подобные проверки не всегда требуются в оперативной работе, поэтому их запуск имеет смысл делать по условию, например, по имени пользователя ночного тестировщика, или флагу глобальных настроек (см. API).
Эталонная база¶
Эталонная база данных используется для тестирования бизнес-логики приложения.
Использование эталонной базы при интеграции тестирования в процесс разработки не эффективно и не используется Тестером по следующим причинам:
- Требуется особый уход за эталонными данными, их корректировка в соответствии с изменениями функционала решения
- Эталонные данные инертны: новый функционал требует новых эталонных данных, которые могут пересекаться с уже существующими эталонными данными. Вытекающая отсюда осторожность при манипуляции с эталонными данными ограничивает или замедляет развитие спектра тестируемого функционала
- Оперативный прогон тестов практически невозможен, выгружать/обновлять/загружать эталонные базы долго. Под оперативностью понимается тестирование при каждом запуске программистом приложения на выполнение, а не увеличение числа релизов и закачки схемы на CI-сервере с прогоном тестов в течение дня
- Количество эталонных баз стремится расти, это осложняет их обслуживание и весь процесс тестирования
Вместо хранения эталонных данных в эталонной базе, проверяемую бизнес-логику нужно хранить в самом Тестере.
С точки зрения Тестера, проверка бизнес-логики – это совокупность программного кода сценария, в котором проверяются значения тестируемых полей, и результирующая отчетность по данным информационной системы. Подробнее о тестировании бизнес-логики см. здесь.
Данные для тестирования¶
Данные для тестирования это входящая для сценариев информация, требуемая для успешного их прохождения. Если ваш тест открывает форму списка справочника изделий и устанавливает фильтр по заводу-производителю, вашими тестовыми данными как минимум будут: запись в справочнике заводов-изготовителей, запись в справочнике изделий, с заполненным реквизитом завода-изготовителя. Тестовыми данными в этом случае будут и пользователь, под которым была запущена информационная база, единица измерения для изделия и другие сущности.
Тестовые данные состоят из двух слоёв:
- Начальные данные, которые хранятся в начальной базе и обеспечивают минимальный набор типовой, редко изменяемой информации
- Создаваемые данные, которые необходимы для тестирования конкретного функционала
Наибольший интерес представляет второй слой. Существует много подходов создания тестовых данных, но в их совокупности можно выделить две стратегии:
Выгрузка заготовленных данных из предварительно подготовленных баз или шаблонов, и последующая их загрузка перед началом или во время тестирования.¶
Вариантов выгрузки/загрузки в данном подходе много. Это может быть конвертация данных, универсальный обмен данными, макеты с данными, выгрузки в dt-файлы или обычная сериализация прикладных объектов в JSON или XML.
Плюсы:
- Очевидность подхода
- Быстрый старт в подготовке данных
Минусы:
- Сложность поддержки согласованности данных в развивающемся функционале приложения, проблемы загрузки данных при измененной структуре метаданных, смене владельцев, появлению полей обязательных для заполнения, переименованию или удалению объектов и их реквизитов.
- Потенциальная опасность получения не консистентных данных и ложного положительного прохождения теста. Такие тестовые данные как правило загружаются в специальном режиме, без дополнительных проверок и срабатывания обработчиков форм. Фактически, тестовые данные готовятся не так, как это бы сделал пользователь.
- Сложность логистики хранения, взаимосвязанности наборов файлов, данных и тестов.
- Статичность. Данные загружаются как есть и их сложно менять под вариативность тестов в случае необходимости.
- Проблемы с удалением тестовых данных для повторных запусков сценариев.
Формирование тестовых данных самим сценарием.¶
При этом подходе, все тестовые данные должны создаваться, а не храниться для загрузки. Сам процесс создания не должен отличаться от процесса разработки целевых сценарных тестов. Другими словами, сегодня вы пишите тест проверки создания нового товара, а завтра этот тест уже может быть использован как метод для создания тестовых данных, для тестирования создания документа Реализация товаров.
Плюсы:
- Гибкость создания требуемого окружения тестовых данных под различные вариации тестов. Возможность разработки сложных взаимосвязанных тестов.
-
Высокое качество тестов за счет дополнительного тестирование всего стека используемых объектов при подготовке данных. Это очень важный нюанс, потому что создание тестовых данных таким образом, выполняется для разных видов целевых сценариев, что автоматически покрывает тестами разнообразие условий создания объектов.
Например, у нас может быть тест-метод создания поставщика. Но поставщик может создаваться из разных контекстов: из формы списка контрагентов или из поля ввода на форме документа. И если тестовые данные загружать “извне”, можно не отловить ошибку создания контрагента из документа. Тест проверки документа пройдет успешно, хотя фактически, пользователь документ ввести не сможет (потому что не сможет создать для документа поставщика). -
Консистентность полученного тестового окружения, заполненность реквизитов, отработка возможных событий и программных оповещений об изменении данных, другими словами - полная штатная эмуляция работы приложения.
- Слабая связанность с изменчивостью структуры метаданных. Если изменяется структура данных объекта, достаточно изменить (если необходимо) один тест-метод, при этом все остальные тестовые данные менять не придётся.
- Простота хранения. Все тест-методы хранятся в одной среде, и не являются внешними по отношению к системе тестирования.
- Использование одних и тех же тестовых данных даже в случае, когда их версии метаданных отличаются. В этом случае, в тест-методах можно организовывать условия по обработке/заполнению полей в зависимости от версии используемой программистом конфигурации (см. функцию МояВерсия ())
Минусы:
- Сложно без опыта выстроить структуру библиотечных тестов для создания тестовых данных
- Требуется первоначальная инвестиция времени в разработку тестов-методов
Тестер исповедует вторую стратегию в подготовке тестовых данных. С точки зрения Тестера, формирование тестовых данных не должно быть в отрыве от целевого тестирования, тестовые данные должны быть подвижны, и они не должны готовиться отдельно от системы разработки тестов.
Структура сценария¶
Перед написанием тестов, нужно задуматься и определить сценарии, которые будут соответствовать вашему краткосрочному планированию. Важно не отвлекаться в этот момент и мысленно не накидывать возможные отклонения. Для каждого отклонения, можно будет потом определить приоритетность и разработать отдельный тест. Даже если вы в состоянии представить большой сценарий, рекомендуется его разделить на несколько небольших и взаимосвязанных тестов.
Для успешного внедрения тестирования, очень важно, чтобы скорость создания и прогона тестов была высокой.
Чтобы быстро создавать тесты, нужны две составляющих: тренировка мышления, для быстрого выбора шаблона сценария под задачу и библиотека готовых тестов-методов, для создания всего необходимого по ходу тестирования. Эти вещи приходят со временем.
Чтобы быстро прогонять тесты, нужно чтобы они были следующей структуры:
- Определение окружения
- Создание окружения
- Целевая часть
Определение окружения – это функция в модуле вашего сценария, которая задает параметры теста в виде структуры. Примеры параметров теста: список приходуемых товаров, с ценами и количеством, наименование поставщика, валюта договора и т.д.
Создание окружения – процедура в модуле теста, которая по переданным параметрам создаст необходимое тесту окружение. В этой же процедуре, должна быть проверка уже созданного ранее окружения, чтобы оно не создавалось при повторном запуске сценария.
Целевая часть – это непосредственно код, который будет проверять то, что вы запланировали сделать, и ничего более.
Скорость прогона тестов достигается за счет выполнения тестом только целевой его части, то есть фактически той, которая выполняется вручную при каждом запуске приложения после модификации программистом. Конечно, создание окружения тоже займет какое-то время, и как минимум один раз, тест должен выполнить эту часть сценария, но и это ровно то, что программист бы сделал вручную. Технически, создание окружения вручную может быть быстрее, но лишь на короткий отрезок жизни приложения.
Яснее на примере. Давайте представим, что вы занимаетесь доработкой документа Списание ТМЗ, который нужно дополнить особыми движениями по регистрам. Для тестирования (неважно как, вручную или нет) вам понадобится материал на складе, себестоимость которого вы знаете, установленные параметры учетной политики, на которые вы полагаетесь, и другие настройки системы, например, вариант контроля остатков. Таким образом, вам нужно будет вручную, как минимум один раз проверить, что настройки системы в порядке. Затем, нужно будет создать несколько материалов, возможно отдельный склад и как минимум одно поступление ТМЗ, и вы не забудете дату поступления сделать днем ранее. Таким образом – вы определяете и создаете окружение, чтобы затем выполнить целевую часть – провести списание и проверить, что особые движения появились и они правильные. Другими словами – выполняются все три описанных выше структурных действия для разработки автоматизированного сценария.
Такой несложный подход не просто перекладывает ручные действия в код, он позволяет вам существенно продвинуться в вопросах развития нового функционала, и тестирования отклонений.
Пример. Добавим в описанную задачу необходимость проверки сообщения об ошибке при списании количества материала, большего, чем есть на складе. Я сильно не ошибусь, если предскажу работу смекалки программиста: он просто откроет уже проведенное списание (предварительно добавив его в избранное для максимальной “скорости”), отменит его проведение, в колонку с количеством введет 999999, запишет документ, а потом проведет его и проанализирует сообщение об ошибке. Если сообщения не будет, программист вернется в код, сделает необходимые доработки и будет повторять итерацию до тех пор, пока сообщение об ошибке не появится.
И тут начинаются проблемы. Во-первых, проверить нормальное поведение списания уже так просто не получится. Нужно будет привести данные в порядок, убрать 999999 и вернуть то количество, которое было на момент, когда мы проверяли правильность доработанных движений. И нам уже вряд удастся быстро вспомнить, каким именно было то первоначальное количество. Во-вторых, даже если программист предварительно скопирует документ, перед тем как менять данные – максимум он потом сможет проверить отсутствие ошибок проведения, но расчетные показатели, результат проведения, он проверить уже не сможет.
Другими словами, имеем классическую картину: последующая доработка механизма потенциальна опасна для предыдущего функционала. И при отсутствии возможности быстрого восстановления окружения теста и его оперативного прогона (а не ночью), потенциальная опасность, рано или поздно перейдет в кинетическую, реальную ошибку.
Дело не только в том, когда мы обнаружим проблему, сейчас, после ночного тестирования, или тестирования тестировщиками, а в том, что когда мы находимся глубоко в коде, нередко нужно здесь и сейчас проверить механизмы, чтобы определиться с выбранным направлением, понять, что наращиваемый функционал не конфликтует и не искажает другие алгоритмы.
Я хотел бы обратить внимание, что даже при условии, когда тесты программиста проходят, они могут содержать методологические ошибки или ошибки понимания задачи. Такие ошибки может выловить отдельный департамент, специальные тестировщики или в конце концов заказчик. Но это отнюдь не означает, что вся история с тестированием программистами не имеет смысла. Наоборот, на основе готовых тестов, программисту достаточно будет скорректировать целевую часть (третья часть сценария), а всю остальную работу по подготовке данных и их проверке выполнит система.
Практика¶
Перейдем к практической части: поработаем с Тестером в режиме создания нового функционала для конфигурации УТ11 на базе постановки задачи от бизнес-аналитика.
Постановка задачи¶
Компания торгует сигарами. Необходимо доработать документ Реализация товаров и услуг
(далее Реализация
) для возможности указания даты согласования следующих продаж по выбранным позициям. Предполагается следующий сценарий:
- Менеджер вводит документ реализации, для выбранных позиций, на своё усмотрение он устанавливает дату, когда ему нужно было бы перезвонить клиенту и уточнить, насколько хорошо продаются сигары. Например, из всего перечня, его интересуют только сигары со вкусом ментола.
- У менеджера есть список, который он открывает каждый день и видит, когда, по каким клиентам и каким позициям ему нужно провести переговоры с клиентом для организации следующих поставок интересующих его позиций.
- Менеджер выбирает нужную позицию из списка, звонит клиенту, и вводит в систему результат переговоров
Документ Реализация
Необходимо в документ Реализация
, в табличную часть, добавить поле для ввода даты согласования (без времени).
Поле не обязательно для заполнения. Если поле задано – тогда данная позиция считается отмеченной для согласования. По таким позициям необходимо предусмотреть обособленный учет на базе регистра сведений Согласование
. Записи в данный регистр производить при проведении документа. При повторном проведении документа, существующие записи регистра сведений не обновлять, только добавлять новые.
Регистр сведений Согласование
Это новый, независимый регистр сведений, с возможностью редактирования.
Регистр должен хранить такие данные:
- Документ
Реализация
,Клиент
,Менеджер
,Товар
,Количество
,Сумма
– автоматически должны заполняться при проведении реализации и не должны быть доступны для редактирования - Флаг
Отработано
– будет устанавливаться пользователем - Флаг
Комментарий
– будет вводиться пользователем и фиксировать результат переговоров. Поле должно быть доступно для редактирования только если флагОтработано
истина.
Для регистра необходимо разработать форму списка и форму элемента. В форме списка должны быть предусмотрены отборы по менеджеру, клиенту и товару.
Запретить пользователю вводить новые записи в данный регистр интерактивно. Примечание: запрет на ввод нового сделать программно с выводом соответствующего сообщения. Не использовать вместо этого настройку команд и ролей пользователя.
Объект расположить в подсистеме Продажи / Оптовые продажи
.
Тесты
Необходимо разработать следующий тест:
- Ввод одной реализации с двумя позициями, одна из которых будет отмечена для согласования
- Открытие списка на согласование, отбор по клиенту, открытие формы регистра на редактирование, установка галочки, сохранение и закрытие
- Убедиться, что позиция ушла из списка
Примечание
Данный тест требуется автором технического задания. Исполнитель (программист) волен разработать столько дополнительных тестов, сколько ему потребуется для обеспечения надлежащего качества выполнения работ.
Разработка¶
По этой задаче мы можем прикинуть ход разработки и тесты, которые нам понадобятся. Условимся, что начальная база у нас уже есть, в ней проведены нужные настройки, отключены окна-подсказки, проверки контрагентов и так далее (о начальной базе см. здесь). В моем случае, начальной базой будет демо-база УТ11.
Можно выделить как минимум четыре этапа:
Этап 1: добавить реквизит Дата
согласования в табличную часть, в процедуре ОбработкаПроверкиЗаполнения
реализовать проверку, чтобы дата согласования была больше даты документа
Этап 2: доработать процедуру ОбработкаПроведения
, добавить туда логику формирования записей по регистру сведений
Этап 3: реализовать форму списка с фильтрами, как требует бизнес-аналитик
Этап 4: реализовать форму редактирования записи регистра для ввода данных по согласованным позициям.
Этап 1¶
Состыкуем работу по кодированию и тестированию.
Программирование
В Конфигураторе:
- Добавляем реквизит
ДатаСогласования
в табличную частьТовары
документаРеализация
- В процедуре
ОбработкаПроверкиЗаполнения
реализуем проверку даты
Тестирование
В Тестере:
Создадим тест ПроверкаДатыСогласования
, где:
- Введем новую реализацию, добавим строку, впишем в строку некорректную дату
- Попытаемся провести, и в списке сообщений найдем требуемое сообщение об ошибке
- Затем изменим дату на правильную, проведем и убедимся, что сообщения об ошибке уже нет
Так как это будет наш первый тест для конфигурации УТ11, нам нужно произвести разовую настройку приложения в Тестере.
Необходимо создать приложение УТ11, см. Меню функций / Приложения
. Для создаваемого приложения нужно задать актуальную на момент написания теста версию, например так:
Затем, это приложение нужно установить приложением по умолчанию, для этого нужно переключиться в дерево сценариев, открыть Опции
, выбрать в поле Приложение
УТ11 и нажать кнопку справа:
Следующий шаг – это создание папки РеализацияТоваровУслуг
внутри папки Документы
, и следом, создание нашего теста ПроверкаДатыСогласования
.
В итоге, должно получится так:
Откроем выделенный на картинке сценарий, переключимся в текстовый редактор и введем этот текст:
// Сценарий:
// - Вводим новый документ Реализация
// - Устанавливаем неправильную дату согласования, проверяем наличие ошибки
// - Устанавливаем правильную дату, проверяем отсутствие ошибки
Подключить ();
ЗакрытьВсё ();
// Вводим новый документ
Коммандос ( "e1cib/data/Document.РеализацияТоваровУслуг" );
Здесь ( "Реализация *" );
// Определяем даты
дата = Дата ( Взять ( "!Дата" ) );
плохая = Формат ( дата - 86400, "DLF=D" );
хорошая = Формат ( дата + 86400, "DLF=D" );
// Вводим плохую дату
Нажать ( "!ТоварыДобавить" );
Установить ( "!ТоварыДатаСогласования", плохая );
// Проводим документ
Нажать ( "!ФормаПровести" );
Пауза ( 1 );
если ( НайтиСообщения ( "Дата согласования*" ).Количество () = 0 ) тогда
Стоп ( "Должна быть ошибка неверной установки даты согласования" );
конецесли;
// Вводим хорошую дату
товары = Получить ( "!Товары" );
Установить ( "!ТоварыДатаСогласования [1]", хорошая, товары );
// Проводим документ
Нажать ( "!ФормаПровести" );
Пауза ( 1 );
если ( НайтиСообщения ( "Дата согласования*" ).Количество () > 0 ) тогда
Стоп ( "Не должно быть ошибок неверной установки даты согласования" );
конецесли;
// Закрываем документ не сохраняя
Закрыть ();
Нажать ( "Нет", "1*" );
Комментарии по коду:
- В этом тесте у нас нет эталонных данных, мы их формируем кодом сценария. Например, правильную и неправильную дату мы вычисляем согласно дате документа, которая нигде в свою очередь не хранится, а присваивается системой автоматически при вводе нового документа
- Может показаться, что для простого тестирования корректности даты, сценарий получился большой. В данном конкретном случае, возможно и не стоило создавать специальный тест для такой задачи, ведь мы всё равно следующими шагами будем проверять формирование движений по регистру, и если что-то пойдет не так, оно пойдет не так и в следующих тестах. Однако, с точки зрения подачи информации, и постепенного раскрытия темы, я посчитал нужным наличие этого сценария. Кроме этого, всё-таки этот сценарий теперь у нас навсегда, мы можем запускать его ночью, и мы можем быть спокойны, что делая какие-то глобальные замены мы не повредим (например) текст сообщения об ошибке
- В этом тесте нет всей трехступенчатой структуры, которую я описывал ранее (см. здесь), но здесь это и не нужно в силу простоты сценария и как следствие – скорости его прогона
Настало время запуска сценария. Для этого убедитесь, что в конфигураторе настроен запуск приложения в режиме клиента тестирования:
(эта настройка сохраняется и её не придётся устанавливать каждый раз)
Запускаем приложение, затем переключаемся в Тестер, устанавливаем наш сценарий основным и нажимаем кнопку Запустить
:
Если вы на момент запуска теста еще не реализовали процедуру проверки даты согласования, наш тест упадет.
Реализуем теперь в модуле реализации, в обработчике ОбработкаПроверкиЗаполнения
, такую проверку:
пустая = Дата ( 1, 1, 1 );
для каждого строка из Товары цикл
согласование = строка.ДатаСогласования;
если ( согласование = пустая ) тогда
продолжить;
иначеесли ( согласование < Дата ) тогда
Сообщить ( "Дата согласования установлена неверно" );
Отказ = истина;
конецесли;
конеццикла;
Запускаем приложения на выполнение, переключаемся в Тестер и жмем там F5
. В результате чего, тест должен завершиться без ошибок.
Этап 2¶
Переходим ко второму запланированному рывку. В таблице ниже состыкуем работу по кодированию и тестированию:
Программирование
В Конфигураторе:
- Добавляем регистр сведений
Согласование
- Дорабатываем обработку проведения для формирования записей в регистр
Тестирование
В Тестере:
Создадим тест ПродажаСДатойСогласования
, где:
- Определим окружение:
- Наименование Поставщика
- Наименование Покупателя
- Товар1 с ценой и количеством
- Товар2 с ценой, количеством и датой согласования
- Создадим окружение
- Создадим поставщика, покупателя и товары
- Сделаем поступление
- Создадим и проведем реализацию
- Целевая часть
- Откроем список реализаций и найдем там созданную реализацию
- Откроем реализацию и проведем
- Откроем регистр согласований и проверим наличие нашей записи
Этот тест сложнее, потому что перед проверкой целевой части, нам потребуется создать окружение. Напомню, что окружение будет создано только один раз, при первом запуске сценария, а затем, будет прогоняться только его целевая часть. Этот трюк будет понятен из кода сценария, который будет разработан в конце этого этапа.
Для создания окружения, нам будут нужны следующие объекты:
- Поставщик
- Товар
- Склад
- Покупатель
- Поступление
В реальных проектах, создание окружения простая операция, но мы делаем всё с ноля, у нас еще нет библиотечных тестов, поэтому перед написанием логики окружения, нам нужны тест-методы для создания объектов (о тест-методах см. здесь).
Метод для создания поставщика¶
Практически все тест-методы имеют одинаковую структуру: они создаются в группе тестов, соответствующей прикладной модели объектов 1С. Это не жесткое правило, в своих проектах вы можете создавать тест-методы по-своему, но предлагаемый подход уже себя зарекомендовал.
Создадим первый тест-метод. В дереве сценариев, в папке Справочники
, создадим папку Поставщики
, внутри неё метод Создать
, а следом метод Параметры
, с тем, чтобы получилась такая структура:
В результате, у нас получился метод Создать
и подчиненный ему метод Параметры
. Для того, чтобы в каком-нибудь сценарии воспользоваться этим методом, нам достаточно будет написать такой код:
// Вначале вызовем тест-метод для получения параметров
параметры = Вызвать ( "Справочники.Поставщики.Создать.Параметры" );
// Заполняем параметры
параметры.Наименование = "ТОО Ромашка";
// Запускаем главный метод передавая ему параметры
Вызвать ( "Справочники.Поставщики.Создать", параметры );
Справку по всем функциям Тестера см. в разделе API.
Для создания сценариев-методов, в форме элемента сценария, на вкладке Свойства
, нужно назначить соответствующий тип:
Подробнее о дереве и свойствах сценариев см. здесь.
Перейдем в редактор кода сценария Параметры
, введем следующий текст:
// Параметры создания нового элемента
//
// Возврат:
// Структура параметров
СтандартнаяОбработка = ложь;
п = новый Структура ();
п.Вставить ( "Наименование" );
п.Вставить ( "Доступ", "Поставщики" );
возврат п;
Затем, откроем редактор сценария Справочники.Поставщики.Создать
и введем:
// Создает нового поставщика по переданным параметрам
//
// Параметры:
// Структура параметров, полученная методом "Справочники.Поставщики.Создать.Параметры"
//
// Возврат:
// Код созданного поставщика
Коммандос ( "e1cib/data/Справочник.Партнеры" );
форма = Здесь ( "Партнер (соз*" );
// *********************************************
// Заполнение полей
// *********************************************
Установить ( "!НаименованиеПолноеКомпания", _.Наименование );
Нажать ( "Поставщик" );
Установить ( "!ГруппаДоступа", _.Доступ );
// *********************************************
// Запись, получение кода
// *********************************************
Нажать ( "!ФормаЗаписать" );
код = Взять ( "!Код" );
// *********************************************
// Создание контрагента
// *********************************************
Нажать ( "Контрагенты", ПолучитьСсылки () );
список = Здесь ( "Контрагенты*" );
Нажать ( "!ФормаСоздать" );
Здесь ( "Контрагент (соз*" );
Установить ( "!Наименование", _.Наименование );
Нажать ( "!ФормаЗаписатьИЗакрыть" );
Закрыть ( список );
возврат код;
Параметризированный метод создания нового поставщика готов. Перейдем к созданию остальных методов.
Метод для создания покупателя¶
Проделаем в дереве сценариев практически те же действия для разработки сценария создания покупателя:
Затем введем код сценариев, Справочники.Покупатели.Создать.Параметры
:
// Параметры создания нового элемента
//
// Возврат:
// Структура параметров
СтандартнаяОбработка = ложь;
п = новый Структура ();
п.Вставить ( "Наименование" );
п.Вставить ( "Доступ", "Прочие" );
п.Вставить ( "Сделка", "Произвольная продажа" );
возврат п;
и Справочники.Покупатели.Создать
:
// Создает нового покупателя по переданным параметрам
//
// Параметры:
// Структура параметров, полученная методом "Справочники.Покупатели.Создать.Параметры"
//
// Возврат:
// Код созданного поставщика
Коммандос ( "e1cib/data/Справочник.Партнеры" );
форма = Здесь ( "Партнер (соз*" );
// *********************************************
// Заполнение полей
// *********************************************
Установить ( "!НаименованиеПолноеКомпания", _.Наименование );
Нажать ( "!Клиент" );
Установить ( "!ГруппаДоступа", _.Доступ );
// *********************************************
// Запись, получение кода
// *********************************************
Нажать ( "!ФормаЗаписать" );
код = Взять ( "!Код" );
// *********************************************
// Создание контрагента
// *********************************************
Нажать ( "Контрагенты", ПолучитьСсылки () );
список = Здесь ( "Контрагенты*" );
Нажать ( "!ФормаСоздать" );
Здесь ( "Контрагент (соз*" );
Установить ( "!Наименование", _.Наименование );
Нажать ( "!ФормаЗаписатьИЗакрыть" );
Закрыть ( список );
возврат код;
Метод для создания товаров¶
Справочники.Номенклатура.Создать.Параметры
// Параметры создания нового элемента
//
// Возврат:
// Структура параметров
п = новый Структура ();
п.Вставить ( "Наименование" );
п.Вставить ( "Единица", "шт" );
п.Вставить ( "Вид", "Товар" );
п.Вставить ( "СтавкаНДС", "18%" );
возврат п;
Справочники.Номенклатура.Создать
// Создает новую номенклатуру по переданным параметрам
//
// Параметры:
// Структура параметров, полученная методом "Справочники.Номенклатура.Создать.Параметры"
//
// Возврат:
// Код созданной номенклатуры
Коммандос ( "e1cib/data/Справочник.Номенклатура" );
форма = Здесь ( "Номенклатура (соз*" );
// *********************************************
// Заполнение полей
// *********************************************
Установить ( "!ВидНоменклатурыОбязательныеПоля", _.Вид );
Установить ( "Рабочее наименование", _.Наименование );
Установить ( "Единица хранения", _.Единица );
Установить ( "Ставка НДС", _.СтавкаНДС );
// *********************************************
// Запись, получение кода и завершение
// *********************************************
Нажать ( "!ФормаЗаписать" );
код = Взять ( "!Код" );
Закрыть ();
возврат код;
Метод для создания складов¶
Справочники.Склады.Создать.Параметры
// Параметры создания нового элемента
//
// Возврат:
// Структура параметров
п = новый Структура ();
п.Вставить ( "Наименование" );
возврат п;
Справочники.Склады.Создать
// Создает новый склад по переданным параметрам
//
// Параметры:
// Структура параметров, полученная методом "Справочники.Склады.Создать.Параметры"
Коммандос ( "e1cib/data/Справочник.Склады" );
форма = Здесь ( "* (соз*" );
// *********************************************
// Заполнение полей
// *********************************************
Установить ( "!Наименование", _.Наименование );
Установить ( "!ТипСкладаОптовый", "Оптовый склад" );
// *********************************************
// Запись, получение кода и завершение
// *********************************************
Нажать ( "!ФормаЗаписатьИЗакрыть" );
Метод для создания документа ПоступлениеТоваровУслуг¶
Документы.ПоступлениеТоваровУслуг.Создать.Параметры
// Параметры создания нового документа
//
// Возврат:
// Структура параметров
СтандартнаяОбработка = ложь;
п = новый Структура ();
п.Вставить ( "Дата" );
п.Вставить ( "Организация", __.Организация );
п.Вставить ( "Склад" );
п.Вставить ( "Поставщик" );
п.Вставить ( "Товары", новый Массив ); // Массив Документы.ЗаказПоставщику.Создать.Строка
п.Вставить ( "ОставитьОткрытым", ложь ); // Закрыть или оставить на экране форму документа
возврат п;
Документы.ПоступлениеТоваровУслуг.Создать.Товар
// Параметры создания новой строки документа
//
// Возврат:
// Структура параметров
СтандартнаяОбработка = ложь;
п = новый Структура ();
п.Вставить ( "Товар" ); // Наименование или Код товара
п.Вставить ( "Количество" );
п.Вставить ( "Цена" );
возврат п;
Документы.ПоступлениеТоваровУслуг.Создать
// Создает новый документ Поступление товаров и услуг по переданным параметрам
//
// Параметры:
// Структура параметров, полученная методом "Документы.ПоступлениеТоваровУслуг.Создать.Параметры"
//
// Возврат:
// Номер созданного заказа
Коммандос ( "e1cib/data/Документ.ПоступлениеТоваровУслуг" );
форма = Здесь ( "Поступление товаров и услуг (соз*" );
// *********************************************
// Заполнение шапки
// *********************************************
дата = _.Дата;
если ( дата <> неопределено ) тогда
Установить ( "!Дата", Формат ( дата, "DLF=DT" ) );
конецесли;
Установить ( "!Партнер", _.Поставщик );
Установить ( "!Организация", _.Организация );
значение = _.Склад;
если ( значение <> неопределено ) тогда
Установить ( "!Склад", значение );
конецесли;
// *********************************************
// Заполнение товаров
// *********************************************
таблица = Фокус ( "!Товары" );
для каждого товар из _.Товары цикл
Нажать ( "!ТоварыДобавить", таблица );
Установить ( "!ТоварыНоменклатура", товар.Товар );
Установить ( "!ТоварыКоличествоУпаковок", товар.Количество );
Установить ( "!ТоварыЦена", товар.Цена );
конеццикла;
// *********************************************
// Запись документа, получение номера
// *********************************************
Нажать ( "!ФормаЗаписать" );
номер = Взять ( "!Номер" );
// *********************************************
// Проведение документа и возврат номера
// *********************************************
Нажать ( "!ФормаПровести" );
закрыть = не _.ОставитьОткрытым;
если ( закрыть ) тогда
Закрыть ();
конецесли;
возврат номер;
Метод для создания документа РеализацияТоваровУслуг¶
Документы.РеализацияТоваровУслуг.Создать.Параметры
// Параметры создания нового документа
//
// Возврат:
// Структура параметров
СтандартнаяОбработка = ложь;
п = новый Структура ();
п.Вставить ( "Дата" );
п.Вставить ( "Организация", __.Организация );
п.Вставить ( "Склад" );
п.Вставить ( "Покупатель" );
п.Вставить ( "Товары", новый Массив ); // Массив Документы.ЗаказПоставщику.Создать.Строка
п.Вставить ( "ОставитьОткрытым", ложь ); // Закрыть или оставить на экране форму документа
п.Вставить ( "Комментарий" );
возврат п;
Документы.РеализацияТоваровУслуг.Создать.Товар
// Параметры создания новой строки документа
//
// Возврат:
// Структура параметров
СтандартнаяОбработка = ложь;
п = новый Структура ();
п.Вставить ( "Товар" ); // Наименование или Код товара
п.Вставить ( "Количество" );
п.Вставить ( "Цена" );
п.Вставить ( "Согласование" );
возврат п;
Документы.РеализацияТоваровУслуг.Создать
// Создает новый документ Реализация товаров и услуг по переданным параметрам
//
// Параметры:
// Структура параметров, полученная методом "Документы.РеализацияТоваровУслуг.Создать.Параметры"
//
// Возврат:
// Номер созданного заказа
Коммандос ( "e1cib/data/Документ.РеализацияТоваровУслуг" );
форма = Здесь ( "Реализация товаров и услуг (соз*" );
// *********************************************
// Заполнение шапки
// *********************************************
дата = _.Дата;
если ( дата <> неопределено ) тогда
Установить ( "!Дата", Формат ( дата, "DLF=DT" ) );
конецесли;
Установить ( "!Партнер", _.Покупатель );
Установить ( "!Организация", _.Организация );
значение = _.Склад;
если ( значение <> неопределено ) тогда
Установить ( "!Склад", значение );
конецесли;
значение = _.Комментарий;
если ( значение <> неопределено ) тогда
Установить ( "!Комментарий", значение );
конецесли;
// *********************************************
// Заполнение товаров
// *********************************************
таблица = Фокус ( "!Товары" );
никогда = Дата ( 1, 1, 1 );
для каждого товар из _.Товары цикл
Нажать ( "!ТоварыДобавить", таблица );
Установить ( "!ТоварыНоменклатура", товар.Товар );
Установить ( "!ТоварыКоличествоУпаковок", товар.Количество );
Очистить ( "!ТоварыВидЦены" );
Установить ( "!ТоварыЦена", товар.Цена );
согласование = товар.Согласование;
если ( согласование <> никогда ) тогда
Установить ( "!ТоварыДатаСогласования", Формат ( согласование, "DLF=D" ) );
конецесли;
конеццикла;
// *********************************************
// Запись документа, получение номера
// *********************************************
Нажать ( "!ФормаЗаписать" );
номер = Взять ( "!Номер" );
// *********************************************
// Проведение документа и возврат номера
// *********************************************
Нажать ( "!ФормаПровести" );
закрыть = не _.ОставитьОткрытым;
если ( закрыть ) тогда
Закрыть ();
конецесли;
возврат номер;
На этом, подготовка методов закончена.
Создание теста ПродажаСДатойСогласования¶
Переходим к написанию конечного теста для второго этапа. Для этого, в дереве сценариев, создадим такой тест:
Сделаем его основным, и внесем следующий код:
// Сценарий:
// - Создадим окружение в результате которого будет существовать документ
// реализация с двумя товарами, для второго товара указана дата согласования
// - Откроем список реализаций
// - Найдем созданную реализацию и (пере)проведем её
// - Откроем регистр согласований и найдем там запись, которую должна была сделать реализация
// - Проверим правильность сформированной записи
Вызвать ( "Общее.Начало" );
ЗакрытьВсё ();
ид = "25C555B6";
тест = окружение ( ид );
создатьОкружение ( тест );
// ************************************
// Целевой сценарий
// ************************************
// Откроем реализации
Коммандос ( "e1cib/list/Документ.РеализацияТоваровУслуг" );
Здесь ( "Документы реализации" );
// Перейдем к нашему документу
Кстроке ( "!СписокРеализацииТоваровУслуг", "Клиент", тест.Покупатель );
// Проведем
Нажать ( "!СписокРеализацииТоваровУслугПровести" );
// Откроем регистр согласований
Коммандос ( "e1cib/list/РегистрСведений.Согласование" );
Здесь ( "Согласование" );
// Перейдем к записи
ок = Кстроке ( "!Список", "Клиент", тест.Покупатель );
если ( не ок ) тогда
Стоп ( "После проведения реализации не сформировались записи в регистре Согласование" );
конецесли;
// Проверим правильность сформированной записи.
// Выведем стандартный список
Нажать ( "!ФормаВывестиСписок" );
Здесь ( "Вывести список" );
// Включим флажок "Только выделенные"
было = Взять ( "!OnlySelectedElement" );
если ( было = "Нет" ) тогда
Нажать ( "!OnlySelectedElement" );
конецесли;
// Укажем, что выводить будем в табличный документ
Подобрать ( "!DocumentTypeElement", "Табличный документ" );
// Формируем мини-отчет
Нажать ( "!Ok" );
// Просим тестер проверить этот отчет
Здесь ( "Список" );
ПроверитьШаблон ( "" );
// *********************************************
// Процедуры
// *********************************************
Функция окружение ( ИД )
дата = ТекущаяДата ();
п = новый Структура ();
п.Вставить ( "ИД", ИД );
п.Вставить ( "Дата", дата );
п.Вставить ( "Согласование", ТекущаяДата () + 172800 ); // Через два дня
п.Вставить ( "ДатаПоступления", дата - 86400 );
п.Вставить ( "Поставщик", "Поставщик " + ИД );
п.Вставить ( "Покупатель", "Покупатель " + ИД );
п.Вставить ( "Склад", "Склад " + ИД );
п.Вставить ( "Товары", определитьТовары ( п ) );
возврат п;
КонецФункции
Функция определитьТовары ( Тест )
ид = Тест.ИД;
список = новый Массив ();
список.Добавить ( новыйТовар ( "Товар1 " + ид, 5, 150, 250 ) );
список.Добавить ( новыйТовар ( "Товар2 " + ид, 15, 250, 350, Тест.Согласование ) );
возврат список;
КонецФункции
Функция новыйТовар ( Товар, Количество, Стоимость, Цена, Согласование = неопределено )
п = новый Структура ();
п.Вставить ( "Товар", Товар );
п.Вставить ( "Количество", Количество );
п.Вставить ( "Стоимость", Стоимость );
п.Вставить ( "Цена", Цена );
п.Вставить ( "Согласование", Согласование );
возврат п;
КонецФункции
Процедура создатьОкружение ( Тест )
ид = Тест.ИД;
// *****************************************************
// Проверяем, нужно ли создавать данные для тестирования
// *****************************************************
если ( СозданоОкружение ( ид ) ) тогда
возврат;
конецесли;
// ******************************************************************
// Проверим, чтобы в настройках были отключены соглашения с клиентами
// ******************************************************************
Коммандос ( "e1cib/command/Обработка.ПанельАдминистрированияУТ.Команда.Продажи" );
Здесь ( "Продажи" );
опция = Взять ( "!ИспользованиеСоглашенийСКлиентами" );
если ( опция <> "Не использовать" ) тогда
Стоп ( "В целях обучения, этап создания соглашений с клиентами в сценариях не предусмотрен.
|В окне, которое сейчас на экране с тестируемым приложением, откройте группу
|Оптовые продажи и в поле Использование соглашений с клиентами, выберите Не использовать" );
конецесли;
// ******************
// Создаем склад
// ******************
п = Вызвать ( "Справочники.Склады.Создать.Параметры" );
п.Наименование = Тест.Склад;
Вызвать ( "Справочники.Склады.Создать", п );
// ******************
// Создаем поставщика
// ******************
п = Вызвать ( "Справочники.Поставщики.Создать.Параметры" );
п.Наименование = Тест.Поставщик;
Вызвать ( "Справочники.Поставщики.Создать", п );
// ******************
// Создаем покупателя
// ******************
п = Вызвать ( "Справочники.Покупатели.Создать.Параметры" );
п.Наименование = Тест.Покупатель;
Вызвать ( "Справочники.Покупатели.Создать", п );
// **************
// Создаем товары
// **************
для каждого товар из Тест.Товары цикл
п = Вызвать ( "Справочники.Номенклатура.Создать.Параметры" );
п.Наименование = товар.Товар;
Вызвать ( "Справочники.Номенклатура.Создать", п );
конеццикла;
// *******************
// Создаем поступление
// *******************
товары = новый Массив ();
для каждого позиция из тест.Товары цикл
товар = Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать.Товар" );
товар.Товар = позиция.Товар;
товар.Количество = позиция.Количество;
товар.Цена = позиция.Стоимость;
товары.Добавить ( товар );
конеццикла;
п = Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать.Параметры" );
п.Товары = товары;
п.Дата = тест.ДатаПоступления;
п.Поставщик = тест.Поставщик;
п.Склад = тест.Склад;
Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать", п );
// *******************
// Создаем реализацию
// *******************
товары = новый Массив ();
для каждого позиция из тест.Товары цикл
товар = Вызвать ( "Документы.РеализацияТоваровУслуг.Создать.Товар" );
товар.Товар = позиция.Товар;
товар.Количество = позиция.Количество;
товар.Цена = позиция.Цена;
товар.Согласование = позиция.Согласование;
товары.Добавить ( товар );
конеццикла;
п = Вызвать ( "Документы.РеализацияТоваровУслуг.Создать.Параметры" );
п.Товары = товары;
п.Дата = тест.Дата;
п.Покупатель = тест.Покупатель;
п.Склад = тест.Склад;
Вызвать ( "Документы.РеализацияТоваровУслуг.Создать", п );
// ********************************
// Регистрируем созданное окружение
// ********************************
СохранитьОкружение ( ид );
КонецПроцедуры
Так как это наш главный сценарий для второго этапа, разберем его подробней.
Сценарий начинается с Вызвать ( "Общее.Начало" );
. Я делаю этот вызов для задания глобальных переменных и выполнения общих задач перед началом работы любого теста. Посмотрим, что внутри теста Общее.Начало
:
// Начальный скрипт для инициализации глобального окружения процесса тестирования
// В скрипте могут задаваться общие параметры, которые могут быть доступны во всех
// остальных сценариях
СтандартнаяОбработка = ложь;
если ( __ = неопределено ) тогда
__ = новый Структура ();
иначе
возврат;
конецесли;
// *************************************************************************
// Используем переменную "__" для задания произвольных глобальных параметров
// *************************************************************************
__.Вставить ( "ПроверятьЛогику", истина );
__.Вставить ( "ЛокальнаяВалюта", "РУБ" );
если ( ИмяПриложения = "УТ11" ) тогда
__.Вставить ( "Организация", "Торговый дом ""Комплексный""" );
конецесли;
// ***********************************************
// Выполняем подключение к тестируемому приложению
// ***********************************************
Подключить ();
Если вы используете демо-базу Тестера, тогда тест Общее.Начало там уже есть. Откройте этот тест и внесите в него изменения, в частности, нам нужна вот эта часть модуля:
если ( ИмяПриложения = "УТ11" ) тогда
__.Вставить ( "Организация", "Торговый дом ""Комплексный""" );
конецесли;
Работает это всё следующим образом. Когда тест ПродажаСДатойСогласования
будет запущен, он в свою очередь запустит тест Общее.Начало
, который определит несколько переменных и подключится к тестируемому приложению. Все тесты, которые будут запускаться дальше, уже будут знать, что в переменной “__” хранится имя организации, локальная валюта и другие параметры. Это своего рода глобальное параметризирование тестируемой среды (использование двойного подчеркивания в качестве глобальной переменной выбрано для независимости от варианта языка 1С).
Затем, тест закрывает все окна в тестируемом приложении. Это нужно для того, чтобы случайно не активировать те формы, которые используется в процессе работы теста, но находятся в несогласованном с ним состоянии.
Следующие три строки определяют и создают окружение (см. Структура сценария):
// Переменная "ид" задает идентификатор окружения.
// Идентификатор окружения может быть любой, но я рекомендую использовать
// указанный ниже шаблон алфавитно-цифровой последовательности,
// которая может быть автоматически сгенерирована в редакторе модуля
// по комбинации клавиш: Ctrl+Shit+I
// Таким образом, чтобы пересоздать окружение, нужно будет просто сменить "ид".
// Почему это так - будет понятно ниже.
ид = "25C555B6";
// В переменную "тест" функция окружение () вернет структуру с параметрами
// нашего теста. Эти параметры и есть определение окружения.
тест = окружение ( ид );
// Процедура создатьОкружение () выполняет создание необходимых тестовых данных.
// Внутри этой процедуры будут вызываться тесты-методы, которые мы создавали в разделах выше.
создатьОкружение ( тест );
Посмотрим, что происходит внутри функции окружение ()
:
Функция окружение ( ИД )
дата = ТекущаяДата ();
п = новый Структура ();
п.Вставить ( "ИД", ИД );
п.Вставить ( "Дата", дата );
п.Вставить ( "Согласование", ТекущаяДата () + 172800 ); // Через два дня
п.Вставить ( "ДатаПоступления", дата - 86400 );
п.Вставить ( "Поставщик", "Поставщик " + ИД );
п.Вставить ( "Покупатель", "Покупатель " + ИД );
п.Вставить ( "Склад", "Склад " + ИД );
п.Вставить ( "Товары", определитьТовары ( п ) );
возврат п;
КонецФункции
Ничего необычного, готовится структура с параметрами, но концептуальным является использование входящего параметра ИД
.
Тестовые данные создаются, а значит, они должны быть уникальны. В противном случае, будут создаваться дубли объектов, которые практически, будет невозможно использовать. Действительно, если мы будем создавать “ТОО Ромашка" каждый раз, какую тогда "ТОО Ромашка" выбрать при создании документа? Таким образом, строки кода "Поставщик " + ИД, "Покупатель " + ИД и другие, гарантируют создание уникальных тестовых данных. Это также позволяет выполнять ваши тесты у других участников разработки, без синхронизации заготовленных шаблонов, использования предзаполненных баз данных и других внешних файлов.
Вероятно, данный подход вызывает недоумение, потому что база со временем превращается в месиво из ничего незначащих идентификаторов. Но на практике, любая база разработчика превращается в обрывки экспериментов из “порванных” данных, это лишь вопрос времени. Если вы в рабочих базах бережно храните некую информацию, на которую опирается ваша разработка – перенесите эти данные в начальную базу. Если базы содержат ошибки для воспроизведения – напишите тесты, которые эти ошибки воспроизводят. Другими словами, если у вас в рабочей базе есть что-то ценное с прикладной точки зрения – напишите сценарий. Это высвободит вас от содержания хозяйства из информационных баз, даст возможность другим разработчикам проверить ваши варианты в их базах на любом этапе разработки. Весь полезный опыт, сосредоточенный в данных, лучше перевести в код сценария, что бы его всегда можно было повторить.
Другой, кажущийся тревожным момент – это глубина уникальности тестовых данных и их объем. И тут нужно отметить, что не все тестовые данные нужно делать уникальными. Уникальности требует только то, что вам нужно для тестирования (включая проверку бизнес логики). К примеру, для нашего теста нужна уникальность наименований поставщика, покупателя, склада и товаров. Нам не требуется уникальность единиц измерений, договоров, соглашений, групп товаров и других связанных данных. Рост данных также не будет проблемой. Даже если вы каждые 5 минут запускаете тест создающий окружение (что крайне маловероятно), то в течение дня таких запусков будет не больше 100, что для дневного роста базы ничтожно мало. На практике, необходимость в обнулении рабочей базы загрузкой в неё начальной, возникает примерно раз в полгода. Согласитесь, что программисты, тестирующие всё вручную, примерно с той же периодичностью выводят из строя свои рабочие базы.
Следующий участок кода, процедура создатьОкружение ()
. Внутри этой процедуры происходит создание тестовых данных. Создание окружения процесс не быстрый, но и не обязательный для каждого запуска сценария в процессе программирования задачи. Для того, чтобы окружение не создавалось каждый раз, можно использовать два подхода. Первый – комментировать вызов процедуры создатьОкружение ()
после того, как оно однажды было создано. Однако со временем, это начинает утомлять.
Второй – вначале процедуры, проверять, было ли окружение уже создано, и если нет, в конце процедуры фиксировать факт его создания:
Процедура создатьОкружение ( Тест )
ид = Тест.ИД;
// *****************************************************
// Проверяем, нужно ли создавать данные для тестирования
// *****************************************************
если ( СозданоОкружение ( ид ) ) тогда
возврат;
конецесли;
// ...код создания окружения...
// ********************************
// Регистрируем созданное окружение
// ********************************
СохранитьОкружение ( ид );
КонецПроцедуры
Методы СозданоОкружение/СохранитьОкружение
работают с идентификатором по ключу компьютер + имя пользователя.
Примечание
Переход к строке сделан через попытку, потому что в версиях платформы <= 8.3.9
есть ошибка работы метода перехода к строке. В случае отсутствия искомой строки, платформа ошибочно генерирует исключение вместо возврата булевого значения.
Остальная и основная часть процедуры создатьОкружение ()
содержит рутину по заполнению параметров и вызову сценариев-методов. Несмотря на простоту логики создания объектов, может потребоваться многократный запуск кода создания окружения при его разработке. В этом случае, запускать полный цикл теста может быть не эффективно. Одним из часто используемых трюков является временный вынос кода в начало основного сценария, с вставкой оператора возврат; до начала основного теста, например так:
Подключить ();
// Отлаживаем по-быстрому...
п = Вызвать ( "Справочники.Склады.Создать.Параметры" );
п.Наименование = "Тестовый склад";
Вызвать ( "Справочники.Склады.Создать", п );
возврат; // Когда отладим, вырежем этот код в тело процедуры создатьОкружение ()
// Сценарий:
// - Создадим окружение в результате которого будет существовать документ
// реализация с двумя товарами, для второго товара указана дата согласования
// - Откроем список реализаций
// - Найдем созданную реализацию и (пере)проведем её
// - Откроем регистр согласований и найдем там запись, которую должна была сделать реализация
// - Проверим правильность сформированной записи
Вызвать ( "Общее.Начало" );
ЗакрытьВсё ();
// ....
Таким образом, кусок за куском логики можно отлаживать и переносить в процедуру создатьОкружение ()
. Обратите внимание, что для пересоздания всего окружения, вам просто нужно будет сменить ИД
в начале теста.
Я описал определение и создание окружения, перейдем теперь к целевой части сценария ПродажаСДатойСогласования
:
// Откроем реализации
Коммандос ( "e1cib/list/Документ.РеализацияТоваровУслуг" );
Здесь ( "Документы реализации" );
// Перейдем к нашему документу
Кстроке ( "!СписокРеализацииТоваровУслуг", "Клиент", тест.Покупатель );
// Проведем
Нажать ( "!СписокРеализацииТоваровУслугПровести" );
// Откроем регистр согласований
Коммандос ( "e1cib/list/РегистрСведений.Согласование" );
Здесь ( "Согласование" );
// Перейдем к записи
ок = Кстроке ( "!Список", "Клиент", тест.Покупатель );
если ( не ок ) тогда
Стоп ( "После проведения реализации не сформировались записи в регистре Согласование" );
конецесли;
// Проверим правильность сформированной записи.
// Выведем стандартный список
Нажать ( "!ФормаВывестиСписок" );
Здесь ( "Вывести список" );
// Включим флажок "Только выделенные"
было = Взять ( "!OnlySelectedElement" );
если ( было = "Нет" ) тогда
Нажать ( "!OnlySelectedElement" );
конецесли;
// Укажем, что выводить будем в табличный документ
Подобрать ( "!DocumentTypeElement", "Табличный документ" );
// Формируем мини-отчет
Нажать ( "!Ok" );
// Просим тестер проверить этот отчет
Здесь ( "Список" );
ПроверитьШаблон ( "" );
Последовательность шагов вполне очевидна, привлекает интерес часть кода проверки сформированных движений. Для этого используется стандартная функция платформы по выводу любого табличного поля в табличный документ:
Нажать ( "!ФормаВывестиСписок" );
Здесь ( "Вывести список" );
// и далее...
и возможность Тестера сравнивать табличный документ с макетом, сохраненным в сценарии:
Здесь ( "Список" );
ПроверитьШаблон ( "" );
На картинке совмещено для наглядности тестируемое приложение (находится выше) и Тестер (находится ниже). Как вы понимаете, я немного забежал вперед, шаблон сохраненный в Тестере был предварительно скопирован из тестируемого приложения, на момент готовности программного кода по формированию этих движений, но программированием второго рывка мы еще не занимались. Это вынужденная мера, мне нужно было логически завершить описание трех-структурного подхода к организации сценариев.
Если мы сейчас запустим сценарий на выполнение, он упадет на этапе создания окружения, ведь у нас кроме логики проведения, еще нет реквизита ДатаСогласования
в табличной части Товары
документа РеализацияТоваровУслуг
.
Займемся программированием:
- Добавим нужный реквизит:
- Выведем на форму:
- В процедуру ОбработкаПроведения модуля объекта, добавим код:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
с = "
|выбрать Товары.Ссылка.Менеджер как Менеджер, Товары.Ссылка.Партнер как Клиент,
| Товары.Номенклатура как Товар, Товары.Количество как Количество, Товары.Сумма как Сумма,
| Товары.КодСтроки как Строка, Товары.ДатаСогласования как ДатаСогласования, &Ссылка как Реализация
|из Документ.РеализацияТоваровУслуг.Товары как Товары
| //
| // Согласованные
| //
| левое соединение РегистрСведений.Согласование как Согласованные
| по Согласованные.Строка = Товары.КодСтроки
|где Товары.Ссылка = &Ссылка
|и Товары.ДатаСогласования <> датавремя ( 1, 1, 1 )
|и не isnull ( Согласованные.Отработано, ложь )
|";
з = новый Запрос ( с );
з.УстановитьПараметр ( "Ссылка", Ссылка );
таблица = з.Выполнить ().Выгрузить ();
для каждого строка из таблица цикл
запись = РегистрыСведений.Согласование.СоздатьМенеджерЗаписи ();
ЗаполнитьЗначенияСвойств ( запись, строка );
запись.Записать ();
конеццикла;
// ...далее идет типовой код конфигурации
Примечание
Чтобы оставаться в фарватере изложения, я опускаю некоторые детали реализации. Например, вы наверняка обратили внимание на отсутствие отработки ситуации удаления строки из табличной части, ранее которая была на согласовании.
Запускаем приложение, запускаем тест. После завершения теста, у вас должно получиться это:
Выделите в УТ11 этот макет и скопируйте в Тестер на вкладку Шаблон
сценария ПродажаСДатойСогласования
. Затем, в Тестере, выделите значимую область, вызовите правым кликом контекстное меню и укажите Отметить
область:
Для того, чтобы шаблон не проверялся как есть, нам нужно его параметризировать. Для этого, в каждой ячейке, которая может меняться в зависимости от времени и условий запуска теста, в контекстном меню произведем замену значения на шаблон:
Проделаем так с каждым полем, чтобы получилась в итоге следующая картина:
Обратите внимание, что значения ячеек Количество
и Сумма
оставлены как есть. Это важно, потому что в отличие от предыдущих полей, которые могу изменяться в зависимости от идентификатора окружения, эта часть фиксированная, прописана в условии теста и должна быть гарантированно такой (более полный пример проверки бизнес логики см. Проверка бизнес логики).
Запускаем сценарий на выполнение и убеждаемся, что тест проходит успешно.
Переходим к следующему запланированному этапу.
Этап 3¶
В этом рывке нам нужно разработать форму списка для регистра Согласование с фильтрами по клиенту, товару и менеджеру.
В таблице ниже состыкуем работу по кодированию и тестированию:
Программирование
В Конфигураторе:
- Добавляем форму списка в регистр сведений Согласование
- Создаем три реквизита формы соответствующих типов, выкладываем их на форму
- Настраиваем свойства и обработчики
- Прописываем код обработчиков фильтрации
Тестирование
В Тестере:
Создадим тест ФильтрацияСписка, где:
- Определим окружение:
- Наименование Поставщика
- Наименование Покупателя
- Наименование Потенциального клиента, по которому продаж нет
- Товар с датой согласования
- Создадим окружение
- Создадим поставщика, покупателей и товар
- Сделаем поступление
- Создадим и проведем реализацию
- Целевая часть
- Откроем список регистра Согласование
- Установим отбор по покупателю, проверим список
- Установим отбор по потенциальному клиенту, проверим пустой список
- Установим/снимем отборы по товару и менеджеру
Для разнообразия, в этот раз начнем с программирования, а затем напишем тест.¶
- Добавим основную форму списка, добавим реквизиты формы, расположим на форме:
- В модуле формы списка, определим обработчики полей фильтрации:
&НаКлиенте
Процедура КлиентФильтрПриИзменении ( Элемент )
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список,
"Клиент",
КлиентФильтр,
ВидСравненияКомпоновкиДанных.Равно,
,
ЗначениеЗаполнено(КлиентФильтр));
КонецПроцедуры
&НаКлиенте
Процедура ТоварФильтрПриИзменении ( Элемент )
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список,
"Товар",
ТоварФильтр,
ВидСравненияКомпоновкиДанных.Равно,
,
ЗначениеЗаполнено(ТоварФильтр));
КонецПроцедуры
&НаКлиенте
Процедура МенеджерФильтрПриИзменении ( Элемент )
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список,
"Менеджер",
МенеджерФильтр,
ВидСравненияКомпоновкиДанных.Равно,
,
ЗначениеЗаполнено(МенеджерФильтр));
КонецПроцедуры
На этом этапе с программированием всё. Перейдем к разработке теста. Для этого, как и в прошлые разы, в дереве сценариев, создадим новый сценарий. Установим этот сценарий основным, и назовем его ФильтрацияСписка
:
Введем следующий код сценария:
// Сценарий:
// - Создадим окружение в результате которого будет существовать проведенный документ
// реализация с товаром, для которого указана дата согласования
// - Откроем список Согласование
// - Установим отбор по покупателю, убедимся, что в списке есть отфильтрованная запись
// - Установим отбор по потенциальному клиенту, убедимся, что список пуст
// - Очистим фильтры по товару и менеджеру, проверим что они отрабатывают,
// допускаем, что если нет ошибок тогда фильтрация происходит
Вызвать ( "Общее.Начало" );
ЗакрытьВсё ();
ид = "25CB69CB";
тест = окружение ( ид );
создатьОкружение ( тест );
// ************************************
// Целевой сценарий
// ************************************
// Откроем регистр согласований
Коммандос ( "e1cib/list/РегистрСведений.Согласование" );
Здесь ( "Согласование" );
// Установим фильтр по покупателю
покупатель = тест.Покупатель;
Ввести ( "!КлиентФильтр", покупатель );
// Найдем его в списке
ок = Кстроке ( "!Список", "Клиент", покупатель );
если ( не ок ) тогда
Стоп ( "В списке не найдена отфильтрованная по клиенту запись" );
конецесли;
// Установим фильтр по потенциальному клиенту
Ввести ( "!КлиентФильтр", тест.ПотенциальныйКлиент );
// Список должен быть пустой
пусто = 0 = Вызвать ( "Таблица.Количество", Получить ( "!Список" ) );
если ( не пусто ) тогда
Стоп ( "Список должен быть пуст" );
конецесли;
Очистить ( "!КлиентФильтр" );
// Установим и очистим фильтр по товару
Ввести ( "!ТоварФильтр", тест.Товары [ 0 ].Товар );
Очистить ( "!ТоварФильтр" );
// Очистим фильтр по менеджеру
Очистить ( "!МенеджерФильтр" );
// *********************************************
// Процедуры
// *********************************************
Функция окружение ( ИД )
дата = ТекущаяДата ();
п = новый Структура ();
п.Вставить ( "ИД", ИД );
п.Вставить ( "Дата", дата );
п.Вставить ( "Согласование", дата + 172800 ); // Через два дня
п.Вставить ( "ДатаПоступления", дата - 86400 );
п.Вставить ( "Поставщик", "Поставщик " + ИД );
п.Вставить ( "Покупатель", "Покупатель " + ИД );
п.Вставить ( "ПотенциальныйКлиент", "Потенциальный клиент " + ИД );
п.Вставить ( "Склад", "Склад " + ИД );
п.Вставить ( "Товары", определитьТовары ( п ) );
возврат п;
КонецФункции
Функция определитьТовары ( Тест )
ид = Тест.ИД;
список = новый Массив ();
список.Добавить ( новыйТовар ( "Товар1 " + ид, 15, 250, 350, Тест.Согласование ) );
возврат список;
КонецФункции
Функция новыйТовар ( Товар, Количество, Стоимость, Цена, Согласование = неопределено )
п = новый Структура ();
п.Вставить ( "Товар", Товар );
п.Вставить ( "Количество", Количество );
п.Вставить ( "Стоимость", Стоимость );
п.Вставить ( "Цена", Цена );
п.Вставить ( "Согласование", Согласование );
возврат п;
КонецФункции
Процедура создатьОкружение ( Тест )
ид = Тест.ИД;
// *****************************************************
// Проверяем, нужно ли создавать данные для тестирования
// *****************************************************
если ( СозданоОкружение ( ид ) ) тогда
возврат;
конецесли;
// ******************
// Создаем склад
// ******************
п = Вызвать ( "Справочники.Склады.Создать.Параметры" );
п.Наименование = Тест.Склад;
Вызвать ( "Справочники.Склады.Создать", п );
// ******************
// Создаем поставщика
// ******************
п = Вызвать ( "Справочники.Поставщики.Создать.Параметры" );
п.Наименование = Тест.Поставщик;
Вызвать ( "Справочники.Поставщики.Создать", п );
// *******************
// Создаем покупателей
// *******************
п = Вызвать ( "Справочники.Покупатели.Создать.Параметры" );
п.Наименование = Тест.Покупатель;
Вызвать ( "Справочники.Покупатели.Создать", п );
п.Наименование = Тест.ПотенциальныйКлиент;
Вызвать ( "Справочники.Покупатели.Создать", п );
// **************
// Создаем товары
// **************
для каждого товар из Тест.Товары цикл
п = Вызвать ( "Справочники.Номенклатура.Создать.Параметры" );
п.Наименование = товар.Товар;
Вызвать ( "Справочники.Номенклатура.Создать", п );
конеццикла;
// *******************
// Создаем поступление
// *******************
товары = новый Массив ();
для каждого позиция из тест.Товары цикл
товар = Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать.Товар" );
товар.Товар = позиция.Товар;
товар.Количество = позиция.Количество;
товар.Цена = позиция.Стоимость;
товары.Добавить ( товар );
конеццикла;
п = Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать.Параметры" );
п.Товары = товары;
п.Дата = тест.ДатаПоступления;
п.Поставщик = тест.Поставщик;
п.Склад = тест.Склад;
Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать", п );
// *******************
// Создаем реализацию
// *******************
товары = новый Массив ();
для каждого позиция из тест.Товары цикл
товар = Вызвать ( "Документы.РеализацияТоваровУслуг.Создать.Товар" );
товар.Товар = позиция.Товар;
товар.Количество = позиция.Количество;
товар.Цена = позиция.Цена;
товар.Согласование = позиция.Согласование;
товары.Добавить ( товар );
конеццикла;
п = Вызвать ( "Документы.РеализацияТоваровУслуг.Создать.Параметры" );
п.Товары = товары;
п.Дата = тест.Дата;
п.Покупатель = тест.Покупатель;
п.Склад = тест.Склад;
Вызвать ( "Документы.РеализацияТоваровУслуг.Создать", п );
// ********************************
// Регистрируем созданное окружение
// ********************************
СохранитьОкружение ( ид );
КонецПроцедуры
Комментарии по коду теста:
- Фактически, этот сценарий является упрощенной версией сценария, который мы создали на предыдущем этапе.
- Я опустил в этом сценарии проверку настройки соглашений с клиентами, в реальном проекте вы эту настройку выделите в отдельный тест проверки окружения и не будете включать в каждый ваш тест.
- В этом сценарии используется библиотечный тест
Вызвать ( "Таблица.Количество", Получить ( "!Список" ) )
. Хороший пример того, как можно общую логику по работе с объектами системы выделять в отдельные тесты. - Чистого времени на разработку этого теста, у освоившего Тестер программиста уходит до 7 минут.
- Не стесняйтесь копировать сценарии, всё-таки код сценария не совсем тоже самое, что код решения. Во многих случаях, лучше не злоупотреблять возможностью организации в тестах изощренных зависимостей ради ухода от повторения кода. Ведь тесты падают и время на анализ “почему?” тоже нужно брать в расчет. При сложных зависимостях, это будет сделать не просто (хотя полный стек ошибок и отладчик в Тестере есть).
- При копировании тестов не забывайте менять идентификатор окружения.
Запустите тест, он должен пройти без ошибок. Этот этап готов, идем дальше.
Этап 4¶
На этом этапе нам нужно разработать форму редактирования записи для регистра Согласование
. Форма для редактирования нужна пользователю для фиксации результатов переговоров с заказчиком (условия задачи были описаны здесь).
В таблице ниже состыкуем работу по кодированию и тестированию.
Программирование
В Конфигураторе:
- Добавляем форму записи в регистр сведений Согласование
- Формируем внешний вид
- Пишем логику работы формы
Тестирование
В Тестере:
Создадим тест РедактированиеФормыСогласования
, где:
- Определим окружение:
- Наименование Поставщика
- Наименование Покупателя
- Товар с датой согласования
- Создадим окружение
- Создадим поставщика, покупателя и товар
- Сделаем поступление
- Создадим и проведем реализацию
- Целевая часть
- Откроем список регистра Согласование
- Установим отбор по покупателю и откроем единственную в списке запись
- Убедимся, что поле Комментарий недоступно для редактирования
- Нажмем галку Отработано, убедимся, что поле Комментарий теперь доступно.
- Сохраним и закроем форму
- Откроем её еще раз и убедимся, что поле Комментарий доступно
- Снимем галку Отработано, затем опять включим и убедимся, что поле Комментарий очищено
- Попробуем в списке создать новую запись
- Проверим, получил ли пользователь предупреждение о запрете на непосредственный ввод записей в регистр
Этот рывок будем делать комбинированно. Начнем с создания макета формы, в конфигураторе, создадим такую запись регистра:
Запустим приложение. Переключимся в Тестер, и в дереве сценариев создадим и зададим как основной тест РедактированиеФормыСогласования
:
И введем такой код:
// Сценарий:
// - Создадим окружение в результате которого будет существовать проведенный документ
// реализация с товаром, для которого указана дата согласования
// - Откроем список регистра Согласование
// - Установим отбор по покупателю и откроем единственную в списке запись
// - Убедимся, что поле Комментарий недоступно для редактирования (условия задачи были описаны здесь)
// - Нажмем галку Отработано, убедимся, что поле Комментарий теперь доступно.
// - Сохраним и закроем форму
// - Откроем её еще раз и убедимся, что поле Комментарий доступно
// - Снимем галку Отработано, затем опять включим и убедимся, что поле Комментарий очищено
// - Попробуем в списке создать новую запись
// - Проверим, получил ли пользователь предупреждение о запрете на непосредственный ввод записей в регистр
Вызвать ( "Общее.Начало" );
ЗакрытьВсё ();
ид = "25CB8B15";
тест = окружение ( ид );
создатьОкружение ( тест );
// ************************************
// Целевой сценарий
// ************************************
// Откроем регистр согласований
Коммандос ( "e1cib/list/РегистрСведений.Согласование" );
список = Здесь ( "Согласование" );
// Установим фильтр по покупателю
покупатель = тест.Покупатель;
Ввести ( "!КлиентФильтр", покупатель );
// Найдем его в списке
Кстроке ( "!Список", "Клиент", покупатель );
// Откроем запись
Нажать ( "!ФормаИзменить" );
Здесь ( Приложение.ПолучитьАктивноеОкно ().ПолучитьОбъект () );
// Проверим, что с галкой, может с прошлого раза осталась включенной
если ( "Истина" = Взять ( "!Отработано" ) ) тогда
Нажать ( "!Отработано" ); // выключим шалку
конецесли;
// Комментарий должен быть недоступен
ПроверитьСтатус ( "!Комментарий", "Доступность", ложь );
// Жмем галку Отработано
Нажать ( "!Отработано" );
// Комментарий должен быть доступен
ПроверитьСтатус ( "!Комментарий", "Доступность" );
// Вводим что-то в комментарий
Установить ( "!Комментарий", "Тестовый комментарий" );
// Нажимаем Записать и закрыть
Нажать ( "!ФормаЗаписатьИЗакрыть" );
// Возвращаемся в список и опять открываем эту запись
Здесь ( список );
Нажать ( "!ФормаИзменить" );
Здесь ( Приложение.ПолучитьАктивноеОкно ().ПолучитьОбъект () );
// Комментарий должен быть доступен
ПроверитьСтатус ( "!Комментарий", "Доступность" );
// Снимаем галку Отработано и устанавливаем заново
Нажать ( "!Отработано" );
Нажать ( "!Отработано" );
// Комментарий должен быть очищен
Проверить ( "!Комментарий", "" );
Нажать ( "!ФормаЗаписатьИЗакрыть" );
// Закроем форму, вернемся в список и попробуем ввести новую запись
Здесь ( список );
Нажать ( "!ФормаСоздать" );
// Проверим обязательное наличие сообщения об ошибке
если ( НайтиСообщения ( "*формируются автоматически*" ).Количество () = 0 ) тогда
Стоп ( "Отсутствует сообщение об ошибке" );
конецесли;
Закрыть ();
// *********************************************
// Процедуры
// *********************************************
Функция окружение ( ИД )
дата = ТекущаяДата ();
п = новый Структура ();
п.Вставить ( "ИД", ИД );
п.Вставить ( "Дата", дата );
п.Вставить ( "Согласование", дата + 172800 ); // Через два дня
п.Вставить ( "ДатаПоступления", дата - 86400 );
п.Вставить ( "Поставщик", "Поставщик " + ИД );
п.Вставить ( "Покупатель", "Покупатель " + ИД );
п.Вставить ( "Склад", "Склад " + ИД );
п.Вставить ( "Товары", определитьТовары ( п ) );
возврат п;
КонецФункции
Функция определитьТовары ( Тест )
ид = Тест.ИД;
список = новый Массив ();
список.Добавить ( новыйТовар ( "Товар1 " + ид, 15, 250, 350, Тест.Согласование ) );
возврат список;
КонецФункции
Функция новыйТовар ( Товар, Количество, Стоимость, Цена, Согласование = неопределено )
п = новый Структура ();
п.Вставить ( "Товар", Товар );
п.Вставить ( "Количество", Количество );
п.Вставить ( "Стоимость", Стоимость );
п.Вставить ( "Цена", Цена );
п.Вставить ( "Согласование", Согласование );
возврат п;
КонецФункции
Процедура создатьОкружение ( Тест )
ид = Тест.ИД;
// *****************************************************
// Проверяем, нужно ли создавать данные для тестирования
// *****************************************************
если ( СозданоОкружение ( ид ) ) тогда
возврат;
конецесли;
// ******************
// Создаем склад
// ******************
п = Вызвать ( "Справочники.Склады.Создать.Параметры" );
п.Наименование = Тест.Склад;
Вызвать ( "Справочники.Склады.Создать", п );
// ******************
// Создаем поставщика
// ******************
п = Вызвать ( "Справочники.Поставщики.Создать.Параметры" );
п.Наименование = Тест.Поставщик;
Вызвать ( "Справочники.Поставщики.Создать", п );
// *******************
// Создаем покупателей
// *******************
п = Вызвать ( "Справочники.Покупатели.Создать.Параметры" );
п.Наименование = Тест.Покупатель;
Вызвать ( "Справочники.Покупатели.Создать", п );
// **************
// Создаем товары
// **************
для каждого товар из Тест.Товары цикл
п = Вызвать ( "Справочники.Номенклатура.Создать.Параметры" );
п.Наименование = товар.Товар;
Вызвать ( "Справочники.Номенклатура.Создать", п );
конеццикла;
// *******************
// Создаем поступление
// *******************
товары = новый Массив ();
для каждого позиция из тест.Товары цикл
товар = Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать.Товар" );
товар.Товар = позиция.Товар;
товар.Количество = позиция.Количество;
товар.Цена = позиция.Стоимость;
товары.Добавить ( товар );
конеццикла;
п = Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать.Параметры" );
п.Товары = товары;
п.Дата = тест.ДатаПоступления;
п.Поставщик = тест.Поставщик;
п.Склад = тест.Склад;
Вызвать ( "Документы.ПоступлениеТоваровУслуг.Создать", п );
// *******************
// Создаем реализацию
// *******************
товары = новый Массив ();
для каждого позиция из тест.Товары цикл
товар = Вызвать ( "Документы.РеализацияТоваровУслуг.Создать.Товар" );
товар.Товар = позиция.Товар;
товар.Количество = позиция.Количество;
товар.Цена = позиция.Цена;
товар.Согласование = позиция.Согласование;
товары.Добавить ( товар );
конеццикла;
п = Вызвать ( "Документы.РеализацияТоваровУслуг.Создать.Параметры" );
п.Товары = товары;
п.Дата = тест.Дата;
п.Покупатель = тест.Покупатель;
п.Склад = тест.Склад;
Вызвать ( "Документы.РеализацияТоваровУслуг.Создать", п );
// ********************************
// Регистрируем созданное окружение
// ********************************
СохранитьОкружение ( ид );
КонецПроцедуры
Комментарии по коду сценария:
- Этот тест был создан путем копирования предыдущего, окружение практически не модифицировалось, я убрал создание потенциального клиента
- Может вызвать интерес такая строка кода:
Здесь ( Приложение.ПолучитьАктивноеОкно ().ПолучитьОбъект () );
. В данном случае показан пример работы с тестируемым приложением через стандартные методы платформы. В переменнойПриложение
, Тестер хранит объектТестируемоеПриложение
, через который можно вызывать все остальные методы платформы в режиме клиента тестирования (подробнее см. API Тестера).
Итак, запускаем тест и убеждаемся, что он падает, так как у нас не реализована логика работы формы. Далее, возвращаемся в конфигуратор, переключаемся в модуль формы записи и вводим такой код:
// *****************************************
// *********** События формы
&НаСервере
Процедура ПриЧтенииНаСервере ( ТекущийОбъект )
оформить ();
КонецПроцедуры
&НаСервере
Процедура оформить ( Зависимость = неопределено )
если ( Зависимость = неопределено или есть ( Зависимость, "Отработано" ) ) тогда
Элементы.Комментарий.Доступность = Запись.Отработано;
конецесли;
КонецПроцедуры
&НаСервере
Функция есть ( Зависимость, Список )
возврат СтрРазделить ( Список, "," ).Найти ( Зависимость ) <> неопределено;
КонецФункции
&НаСервере
Процедура ПриСозданииНаСервере ( Отказ, СтандартнаяОбработка )
если ( Запись.ИсходныйКлючЗаписи.Пустой () ) тогда
ошибка ();
Отказ = истина;
конецесли;
КонецПроцедуры
&НаСервере
Функция ошибка ()
Сообщить ( Нстр ( "ru = 'Записи в данном регистре формируются автоматически, при проведении документа Реализация товаров и услуг'" ) );
КонецФункции
// *****************************************
// *********** Группа Форма
&НаКлиенте
Процедура ОтработаноПриИзменении ( Элемент )
применитьОтработано ();
оформить ( "Отработано" );
КонецПроцедуры
&НаКлиенте
Процедура применитьОтработано ()
если ( не Запись.Отработано ) тогда
Запись.Комментарий = "";
конецесли;
КонецПроцедуры
Затем, переключимся в форму и зададим обработчики событий: ПриЧтенииНаСервере
, ПриСозданииНаСервере
, ОтработаноПриИзменении
. Запустите приложение, запустите тест и убедитесь, что он проходит.
Заключение¶
Мы рассмотрели теоретическую и практическую части применения системы Тестер для организации работы программиста через тестирование управляемого кодом клиентского приложения. Многие выкладки можно найти спорными и даже прямо противоположными учениям авторитетов в области построения процессов тестирования. Не вопреки, а с осознанием этого, готовился данный материал. Основной целью было показать практическое применение методики и средств сценарного тестирования.
Я допускаю, что в каждом разработанном сценарии, кто-то найдет “перебор” по части объема тестируемого функционала, а кто-то “недобор”. Мне остается лишь надеяться, что в этой статье был достигнут баланс между всем надоевшей простотой и малоинтересной сложностью. Не менее важной задачей, было показать, как можно вживить в разработку автоматизированное сценарное тестирование, чтобы оно не казалось аппендиксом или очередной надстройкой над сущностью программирования.