Добрый день, меня зовут Решитко Дмитрий, я работаю в компании C.T.Consultants. И сегодня мы в очередной раз поговорим о тестировании.
Проблема непонимания подходов к тестированию
Почему тема доклада называется «Молчание Best Practices»?
После прочтения массы литературы и изучения каких-то документов в интернете все равно остается полное непонимание того, как вообще подходить к тестированию:
- Сколько должно быть баз?
- Какие должны быть базы?
- Что там делать – куда выгружать, загружать?
- Какая вообще должна быть структура теста?
Все это происходит из-за того, что тема тестирования очень обширная и одновременно сложная.
У тестирования есть много задач – они зависят и от инструмента, и от методологии, которая применяется, и от многих других аспектов. Поэтому то, что вы видите в литературе – это, как правило, какие-то простые тесты, где нужно сложить два числа или нажать на одну кнопку, или посчитать стоимость билета со скидкой и т.д. А когда мы начинаем пробовать применять приемы тестирования в задачах заказчика (в ERP, в тех же бизнес-процессах) – все это, естественно, очень быстро уходит из-под ног. Мы не понимаем вообще – кликать, не кликать, писать, не писать.
Кроме этого, часто тестирование завязано на какую-то определенную методологию:
- Если это, предположим, TDD, то многие опытные в этом специалисты вообще говорят, что само тестирование при таком подходе является побочным эффектом, а локомотивом разработки является именно методология.
- Если речь идет о BDD (это тоже технология разработки программного обеспечения, куда входит процесс тестирования), то там тоже возникает много вопросов – особенно, когда люди только начинают работать. И какие-то конкретные сценарии, конкретные вопросы, как правило, могут замыливаться под предлогом того, что надо сначала понять, какая задача у заказчика и т.д.
Это – несколько аспектов проблем, которые не позволяют легко войти в тему тестирования.
Базы данных
Давайте перейдем к понятию базы данных.
Когда вы уже прочитали всю информацию по тестированию, скачали все возможные инструменты и обработки (например, лично я разрабатывал Тестер, может быть, вы его уже скачивали, смотрели), то начинаете мыкаться, как слепые котята, не понимая – что, где, откуда и куда.
Так или иначе, давайте будем говорить конкретно, что любое тестирование подразумевает под собой определенный набор баз данных – классы баз данных.
- Первый класс – это начальная база данных, где хранится минимальный набор данных. Если вы разрабатываете какое-то тиражное решение, у вас эта база должна существовать. Там может быть какой-то минимальный набор единиц измерений, валют, какие-то классификаторы. Если вы разрабатываете решение под проект конкретного клиента, где не предусмотрено различных вариантов включения/выключения функциональных опций (условия определены конкретнее), но сама бизнес-логика может меняться чаще, чем в типовом решении, то в качестве начальной базы, конечно же, может выступать какая-то уже настроенная база клиента. Начальная база, как правило, хранится где-то в общедоступном месте, чтобы разработчики и специалисты по тестированию могли ее в любой момент взять и начать запускать на ней тесты.
- Тестовая база (она же рабочая) – это там, где запускаются тесты. Фактически это просто рабочая база, где ведется каждодневная работа, где вы нажимаете F7, F5 и создаете там ваши данные, запускаете сценарии.
- И есть еще эталонная база – к ней много вопросов. Под эталонной базой часто подразумевают ту базу, с которой нужно свериться.
Иногда путают эталонную и начальную, потому что они в какой-то мере перекликаются. Но желательно это разделять:
- Начальная база – это то, на что будут опираться ваши тесты. Вы знаете, что в ней есть как минимум один пользователь, какое-то основное подразделение, какие-то три единицы измерения – тот минимальный набор данных, который вы наверняка проверять не будете.
- А эталонная база данных – это, скорее всего, та база, где воспроизведены сценарии. Эти сценарии сформировали какие-то правильные проводки, и эта база данных используется для того, чтобы потом с ней сверялись тесты.
Я – противник использования эталонной базы в каждодневном процессе разработки приложений через тестирование. Я думаю, что эталонная база подходит в данном случае не очень хорошо, потому что с практической точки зрения в эталонной базе мы можем проверять только бизнес-логику. К эталонной базе нужно соответствующим образом подключаться, выковыривать оттуда какие-то движения, смотреть, правильные они или нет, и сверяться с той базой, где вы тестируете. Если происходит какая-то ошибка, мы выдаем информацию о том, что у нас данные различаются, формируем какой-то отчет, и вроде все хорошо.
Но проблема в том, что:
- эталонную базу нужно держать в форме, потому что в ней от релиза к релизу могут добавляться какие-то реквизиты, изменяться движения, и нужно постоянно проверять, чтобы эталонная база такой и оставалась;
- а другая проблема в том, что эталонную базу нужно каждый раз восстанавливать. Когда что-то падает, нужно быстро получать ситуацию, исправлять ее и прогонять эти тесты повторно. А в случае, если это большая база, например ERP, то процесс ее восстановления либо разворачивания какого-то образа виртуальной машины занимает продолжительное время. А чем больше времени этот процесс занимает, тем сильнее процесс тестирования растягивается во времени, и продуктивность, на мой взгляд, теряется.
Хотя многие компании все-таки используют эталонную базу.
Каким должен быть сценарный тест
Плавно переходим к тому, каким должен быть сценарный тест.
- Первое важное утверждение – сценарный тест должен быть коротким, точнее, у него должна быть короткая целевая часть – именно то, что вы проверяете. Понятно, что если вы тестируете какой-нибудь расчет себестоимости, вам необходимо будет подготовить какие-то поступления, перемещения и списания, сделать номенклатурную спецификацию, и, если взять этот большой тест в целом, то он, конечно, будет длинным. Но именно целевая часть теста должна быть короткой. Целевая часть в данном случае – это тот этап, где вы открываете журнал документов расчета себестоимости выпуска, находите нужный документ, формируете его движения и смотрите, совпадают ли они с движениями, которые вы сохранили у себя в инструменте тестирования (не в эталонной базе) либо в коде вашего теста. Что именно должно проверяться, вы решаете сами (либо бизнес-аналитик решает). Как правило, контролируется сумма полей проводок и каких-то ключевых регистров, после чего можно уверенно говорить, проходит тест или не проходит. Целевая часть теста должна быть короткой.
- Любой инструмент тестирования, который вы будете использовать, позволяет разбивать сложные сценарии на подсценарии. Но тут тоже нужно понимать – какова природа сложности сценария. Если мы говорим о расчете себестоимости, то это – сложный сценарий, но он самодостаточный в том плане, что у нас есть короткая часть, которая проверяет какую-то бизнес-логику. Но часто ребята путаются и делают сложный сценарий, где проверяют разные тестовые случаи. Например, для сценария по расчету заработной платы делают тестовые случаи: «Расчет заработной платы от оклада по часам», «Расчет заработной платы от оклада по дням», «Расчет заработной платы, если сотрудник был в отпуске», «Расчет заработной платы, если сотрудника уволили в середине месяца» и т.д. И получается, что тест один – «Расчет заработной платы», а там внутри сидит очень много других тестов. Так делать не стоит, потому что если упадет какой-то из тестов на третьем уровне, то программисту, чтобы внести исправления и проверить прохождение только этого тестового случая, приходится ждать, когда пройдет весь этот тест. Либо ждать ночного прогона. А если мы говорим о том, что тестирование проводится только ночью или о том, что его отдельно проводят тестировщики и т.д., то значит, что мы не интегрировали процесс тестирования в разработку. А это – часть той мысли, которую я хотел бы донести до сообщества: тестирование должно исходить от разработки и дальше уже развиваться по мере сложности самого бизнеса и задач, которые перед ним стоят.
- О том, что большие сценарии сложно изменять и отлаживать, это, я думаю, всем понятно, просто многие об этом забывают, потому что когда они находятся на этапе создания сценария, они сфокусированы на том, чтобы сделать этот сценарий как можно быстрее. А потом упускают тот момент, когда сценарий нужно все-таки менять. Когда он эволюционирует – тогда уже начинаются проблемы и вопросы
- Далее – сценарный тест должен быть не только коротким, но и структурированным.
- Неважно, какую структурность теста вы выберете, но она должна быть – это потом позволит вам легко читать любой тест, который написан вами и вашими коллегами.
- Тесты падают, мы для этого их и пишем, чтобы они когда-нибудь, в конце концов, упали. Если они имеют определенную структуру, то падение теста в конкретной части структуры сразу же дает вам понимание, где возникла проблема. Например, если ваш сценарный тест подготавливает тестовые данные, и он падает на какой-то конкретной строчке кода (либо на шаге, если вы используете интерфейсное накликивание сценарного теста), то вы уже понимаете, где конкретно произошла ошибка. Например, вы увидите, что проблема возникла не в самом расчете себестоимости, а в подготовке данных (что-то там пошло не так). Это тоже важное правило, потому что если у тестов будет различная структура, их потом будет тяжело сопровождать.
Несколько тезисов по поводу связанности и очередности тестов – может быть, даже запуска.
- Связность тестов – очень часто задаваемый вопрос и возникающая в связи с этим путаница. Здесь подвох следующий – мы как программисты всегда любим что-то оптимизировать, складывать какие-то общие куски кода в процедуры, модернизировать код с тем, чтобы он был как можно более объектным, модульным и т.д. С тестами ситуация похожая, но тут нужно знать меру. Если мы будем делать из системы тестирования какую-то глобальную модульность, это может сыграть с нами злую шутку. Например, при написании теста нужно избегать ситуаций, где динамически по запросу с переданными параметрами создается контрагент или номенклатура, используя какой-нибудь вспомогательный тест, который открывает какую-то форму списка, с путем к сценарию, который будет создавать какая-то обертка. Потому что когда такой тест начинает падать, вы начнете путаться, не поймете, откуда ноги растут.
- И также с очередностью – это очень частое заблуждение. Например, у вас есть тест на документ поступления материалов (для многих этот тест нерелевантный, потому что поступление материалов уже сделала фирма «1С», но я его здесь приведу, просто чтобы было понятно, о чем речь). А потом, предположим, через три дня вам приходит задача на создание документа «Перемещение материалов». И вы думаете: «У меня уже есть тест на документ поступления, с ним все в порядке, я просто сейчас его запущу первым, а вторым я запущу тест на перемещение, который я сейчас пишу». Логика прямая, но со временем она сыграет с вами злую шутку, потому что, во-первых, тест, который вы делали на поступление, вообще не знает о том, что вы потом будете делать перемещение. А во-вторых, тест на поступление привязан к какому-то бизнес-процессу, который может эволюционировать. Когда он будет эволюционировать, вы будете дорабатывать это поступление. И у вас тест на перемещение уже будет падать, потому что он не будет понимать, что там при поступлении, оказывается, были не рубли, а доллары. Или, например, в поступлении появились услуги, которые распределяются. Нельзя организовывать очереди из тестов и формировать их мысленную взаимосвязь. Это потом сложно анализировать, потому что уже непонятно – по какой причине тест упал, потому что поступление эволюционировало? Поэтому рекомендуется оставлять в покое тест поступления. Скопируйте из него только ту часть, которая нужна для создания тестовых данных, упрощая все, что можно упростить. За этой частью вы будете следить, и это даст вам большую свободу в выборе того, что будет происходить.
Поэтому тут с очередностью будьте очень аккуратны – не злоупотребляйте.
Алгоритм сценария
Я уже упоминал определенные тезисы про то, что сценарный тест должен быть структурированным. Теперь – сам алгоритм, его структура (блочность).
Я буду отталкиваться от структуры, которая, мне кажется, покрывает большую часть сценариев, которые могут быть. Это:
- определение входных данных;
- создание окружения;
- и целевая часть.
Это – три больших блока практически любого сценария, с которым нам приходится иметь дело при создании бизнес-приложений.
- Определение входных данных – это какая-то структура, которую вы набиваете параметрами, отвечающими за входные данные для вашего теста.
- Создание окружения – это та часть теста, в которой вы, согласно входным данным, начинаете создавать ваше тестовое окружение.
- А целевая часть – это непосредственно то, что вы проверяете. Она должна быть максимально короткой.
Если переложить сценарий на ручное тестирование – вы начали делать задачу, создали для себя элементы справочника, сформировали нужные документы, все подготовили, что-то в избранное добавили, чтобы быстрее там бегать мышкой, и начинаете итерацию:
- внесли изменения в конфигурацию, сохранили (нажали F7), запустили режим 1С:Предприятие;
- входите в избранное, открываете нужный документ, проводите его;
- смотрите движения, проверяете их правильность;
- закрываете.
Так вот, целевая часть – это именно та часть, которой вы занимаетесь, когда все остальное у вас готово. Эта целевая часть желательно должна быть короткой.
Если у вас есть тест для документа поступления, а потом вам необходимо сделать тест печатной формы документа поступления, то тут, казалось бы, целевая часть раздвинулась. Но она не раздвинулась, она изменилась. Оставьте в покое тест для документа поступления. Сделайте еще один тест на его основе и переместите то, что было раньше в целевой части теста в создание окружения. А целевой частью станет уже печатная форма. Вам уже не нужно будет открывать документ, проводить и формировать печатную форму, вы уже будете знать – документ проведен, находится в состоянии, которое вам нужно. Вы будете прямо из журнала без открытия формы нажимать на кнопку печати, получать печатную форму и сверять ее с печатной формой, которая находится у вас в инструменте тестирования (не в эталонной базе).
Входные данные
Я много говорил о тестовых данных, об их подготовке. И теперь поговорим о подходах и о сломанных копьях на этот счет.
Нам надо запускать тест, а тесту нужны тестовые данные. Вопрос – как их создавать? Это больная тема. Есть несколько подходов.
- Тестовые данные можно создавать и хранить в какой-то базе, а потом эту базу поднимать, запускать на ней тест и проверять – успешно прошло или нет. Это первый подход.
- Второй подход – мы можем хранить тестовые данные в каком-то шаблоне и потом оттуда восстанавливать. Данные для шаблона можно сериализовать, использовать конвертацию данных или генератор тестовых данных. Эти подходы успешно используются, работают, вы можете экспериментировать, пробовать их в своих решениях.
- Но мне предыдущие два подхода не нравятся. Мне нравится подход, когда тестовые данные создаются динамически при помощи самого инструмента тестирования. Мы об этом поговорим чуть позже.
А сейчас я хотел бы акцентировать ваше внимание на том, что необходимо научиться выделять, какие данные будут являться тестовыми. Надо попробовать и выбрать для себя ту стратегию, к которой вы будете прибегать. Например, ребята часто создают элементы справочника – «Товар №1, 5 штук в упаковке», или договор контрагента «Ромашка руб» или «Ромашка $». Они думают, что эти данные будут тестовыми, их не нужно будет каждый раз создавать на лету, и это позволит экономить время, понимать, что к чему. Но это все тоже плохо заканчивается. Рано или поздно появится тест, который будет брать этого контрагента «Ромашку» и проверять какое-то поступление либо реализацию без договора или, например, будет изменять валюту. И если этот тест упадет, восстановления этих данных уже не произойдет. И следующие тесты, которые будут опираться на то, что там должны быть доллары, дружно попадают.
Желательно так не делать. Желательно четко осознавать, какие тестовые данные вам нужны.
Создание окружения
Создание окружения. Мы плавно переходим к очень большому вопросу – как создавать тестовые данные.
Я уже говорил, что предпочитаю создавать тестовые данные на лету. Давайте остановимся на этом подробнее.
- Любой инструмент тестирования, по идее, позволяет формировать служебные сценарии, которые по переданным параметрам создают какие-то необходимые вам данные.
- Но возникает вопрос – как достигается уникальность этих тестовых данных? Когда мы храним тестовые данные в макете или в базе, то они один раз развернулись, а потом их надо каким-то образом подтирать, чтобы под следующие тесты создавать новые элементы. Мы не можем использовать транзакции, потому что не все данные можно хранить на клиенте – если нам нужно проверить отчет, нам нужно, чтобы данные уже были в базе, мы не можем это все откатить. Плюс, если возникнет какая-то ошибка, а мы данные откатим, то – как проверить, что это за ошибка, если данных уже нет? Потом будет сложнее воспроизводить этот сценарий, чтобы получить ошибку. А если вы используете динамическое формирование тестового окружения, вы можете использовать какой-нибудь уникальный идентификатор, который позволяет формировать ваш инструмент тестирования. И ваши данные будут каждый раз формироваться заново.
- Понятно, что база, на которой вы производите тестирование, рано или поздно превращается в хлам – там постоянно создаются какие-то новые элементы справочников и т.д. Но давайте говорить честно – даже если вы тестируете и разрабатываете вручную, ваша база все равно превращается в хлам. Рано или поздно вы все равно поменяете ее на реальную клиентскую базу или опять на начальную – это процесс неизбежный.
Целевая часть
Итак, мы определили входные параметры нашего теста, поговорили о том, как можно создавать тестовые данные, а теперь давайте поговорим о целевой части.
- Немаловажный момент – почему я использую именно такую структуру сценария? У меня всегда было непреодолимое желание запускать тесты как можно быстрее. Скорость – это очень важно. Это не просто слово. Это то, что непосредственно втягивает тестирование в саму разработку, делая ее непрерывной – тестирование не вылетает из вашего цикла программирования. Поэтому необходимо сделать так, чтобы тестовые данные либо создавались быстро, либо создались только один раз, чтобы потом вы уже на базе созданных тестовых данных выполняли только целевую часть. Например, если мы говорим о тестировании какой-то печатной формы, то у нас, когда мы запускаем тест, не должно каждый раз создаваться тестовое окружение со справочниками (с товарами, услугами, контрагентом, договором и т.д.). Мы один раз подождали, когда все это создастся в уникальном виде, а потом просто перезапускаем. И у нас открывается документ, нажимается кнопка, мы получаем печатную форму и таким образом проводим тестирование – очень оперативно.
- Это действительно помогает, потому что, когда вы вручную тестируете какой-то один объект, вы зомбируетесь, делая одно и то же. И когда вам нужна следующая задача, вам сложно перестроиться. Чтобы немного сделать эту ментальную разгрузку, тесты очень сильно помогают. Поэтому с одной стороны – F5 в конфигураторе, с другой стороны – F5 в инструменте тестирования. Вы запускаете, выполняется тест. И если вы этот тест организовали правильно, то выполняется только целевая часть. Не запускается выгрузка, загрузка, еще что-то, вы получаете фактически только то, что вы бы делали вручную.
- Я уже говорил о том, что прогон должен быть быстрым, и тест нужно запускать по кусочкам (полный прогон запустим потом).
Есть еще одна проблема, с которой сталкиваются начинающие тестировщики: тестовое окружение создавать сложно. Вам нужно набить руку для того, чтобы создавать тестовые данные, чтобы создать библиотеку из ваших тестов и создать тест-методы для того, чтобы вы могли вызывать параметризированные тесты с созданием необходимого вам окружения. На то, чтобы все это происходило у вас автоматически, нужно потратить месяц, может быть, два.
Приемы тестирования №1
Давайте перейдем к приемам тестирования.
- Каждый программист, который думает о тестировании, изначально представляет себе, что он сейчас все прокликает, и этого будет достаточно. Но это только начало – очень многое кликами не протестировать.
- Например, вы не можете проверить кликами – доступен элемент или не доступен, если что-то появляется или исчезает на форме в зависимости от значения перечисления в каком-то справочнике. Поэтому, если у вас стоит такая задача, будьте готовы к тому, что придется писать либо код, либо добавлять какие-то специальные шаги, которые будут проверять доступность или видимость.
- Проверка движений документов. Многие пытаются делать проверку движений запросом – либо подключаются к базе по COM, либо через веб-сервис, либо делают какое-то расширение и через это расширение начинают что-то воротить. Все это прекрасно работает – с этим проблем нет, это экономит время. Но есть маленький нюанс – когда тест начинает падать (а он рано или поздно начнет падать), воспроизводить вы его будете уже в пользовательском режиме. У вас появляется дистанция – разрыв между тем, что упало, и как оно упало. И поскольку вы будете воспроизводить эту ситуацию, то время, которое вы сэкономили, вы все равно вернете. Потому что вам все равно нужно будет увидеть эту ситуацию глазами, а для этого придется действовать как пользователь. Поэтому при проверке печатной формы или отчета бизнес-логику и результат нужно проверять именно в пользовательском режиме – так, как его будет проверять пользователь. Не делайте обходные пути. Это тяжело, но это нужно делать. Это потом сэкономит вам время.
Приемы тестирования №2
Какие еще есть приемы тестирования.
Если у вас правильно организованы тесты, вы потом можете создавать их один на базе другого, подменяя условия. Например, вы можете в тесте поменять валюту, количество, можете вместо товара добавить услугу и т.д. Если тесты у вас организованы правильно, вам потом будет проще с ними работать.
Рефакторинг
- Тесты не должны брать в заложники развитие функциональности. Довольно часто получается, что мы пишем тесты, а потом начинаем бояться менять функциональность, потому что боимся, что тесты начнут падать. Если нет нормального инструмента рефакторинга, или тесты организованы очень плохо, то получается «за что боролись, на то и напоролись».
- И наоборот – мы боимся писать тесты, потому что у нас могут быть проблемы с функциональной частью. Это очень взаимосвязанные вещи, поэтому, чем быстрее вы можете рефакторить и изменять тесты, тем лучше.
- Экспериментируйте, пробуйте заворачивать тестирование на сам процесс разработки, покрывайте ошибки, покрывайте какие-то пограничные случаи или отклонения (то, что сверху сценарием не спустится).
Ссылки на бесплатные инструменты тестирования
- https://github.com/grumagargler/tester
- https://github.com/Pr-Mex/vanessa-automation
- https://github.com/silverbulleters/add
- https://github.com/ivanov660/TestingTool-3
- https://github.com/DoublesunRUS/ru.capralow.dt.unit.launcher
Надеюсь, вас эта тема заинтересовала.
****************
Данная статья написана по итогам доклада (видео), прочитанного на конференции INFOSTART EVENT 2019.