Русский
Русский
English
Статистика
Реклама

Automation testing

AEM Test Automation Create Pages via HTTP Requests

28.04.2021 20:06:12 | Автор: admin

Если вы уже знакомы с АЕМ-ом, смело пропускайте эту часть. Если же нет - вам стоит понять, что же такое этот АЕМ и почему с его тестированием возникают сложности.

AEM - content management system от Adobe (как выразилась коллега - WordPress на стероидах). Что это значит? Мы не создаем непосредственные веб страницы, а работаем над сложной админкой (AEM-author), которая в будущем позволит контенщикам (Editors) создавать эти страницы, используя набор определенных компонентов. Эти страницы будут видны (после publish действия) конечным юзерам (AEM-publish). Так что наша работа, собственно, и заключается в создании этих компонент.

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

И так, что мы имели AEM version 6.4.4.0.Процессы тестирования, установленные задолго до нашего привлечения. Вся ответственность была возложена на автотесты:

  1. Screenshots tests Поскольку AEM content management system => Значит наши стили, да и вообще весь фронт очень важен, ведь это то с чем сталкивается конечный пользователь (возможно расскажу об этом в другой статье).

  2. Web-component tests самые обычные UI тесты с использованием Cypress в качестве основы. Только проверялись не страницы, а компоненты.

  3. Web Performance tests мониторинг производительности наших Web-страниц (с помощью Sitespeed)

Что мы строили корпоративный сайт со всякими свистелками и перделками для отдела маркетинга для привлечения клиентов и увеличения продаж (с покупкой прям на сайте). Перед нами стояла задача разработать возможность едиторам создавать страницы с различными статьями. Создать компоненты, которые бы отображали созданные нами статьи в определенном порядке, плюс возможность применять фильтрацию, основываясь на подвязанных тегах.

Какая сложность возникает при автоматизации тестировании такого функционала? Зависит, где будет происходить фильтрация. В нашем случае всё происходило на стороне бека. Это осложняет тестирование, поскольку, создает невероятное количество комбинаций статей и соответствующих тегов. Они же, в свою очередь, должны где-то хранится.

С одной стороны, все очевидно фильтр подразумевает наличие критериев и соответствующих сущностей. А если добавить сюда возможность создания новых критериев и новых сущностей? Задача резко усложняется. Добавляем итеративную разработку изменения происходят не только в страницах, но и в логике их работы. Всё. Теперь мы не можем полагаться только на статический контент.

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

И так какой же выход?

А что если мы будем создавать страницы на лету?

Создали несколько страниц, пошли проверили нашу функциональность для данной комбинации, создали новые, проверили для них.

Long story short

К сожалению, АЕМ не предоставляет никакой информации о своем API. А если такая информация где-то и есть, я не смог ее найти. Да и тестирование АЕМ-а минимально описанная головная боль (Тестируете АЕМ? Делитесь в комментариях как).

Так что последующие выводы целиком и полностью reverse engineering построенный на запросах отправленных фронтом (AEM-author) на бек.

В моем случае, и я уверен в подавляющем большинстве случаев, - создание страниц в АЕМ-е сведется к следующим действиям:

  1. Создание страницы с помощью темплейта (page template)

  2. Добавление компонент на страницу

  3. Конфигурация компонент

  4. Паблиш страницы

    И, конечно, мы будем:

  5. Удалять страницы


Приступим...

Нам понадобиться dev-tools на вкладке Network.

1. Создание страницы с помощью темплейта

  1. Открываем AEM-author

  2. Sites

  3. Переходим в нужную нам папку

  4. Жмякаем Create

  5. Выбираем нужный нам template

  6. Заполняем интересующие нас поля

  7. Запускаем запись Network запросов в dev-tools

  8. Нажимаем Create

Первый же запрос `${aem-author-URL}/libs/wcm/core/content/sites/createpagewizard/_jcr_content` будет содержать всю необходимую нам информацию.

Тут мы сталкиваемся с первой сложностью. Это POST запрос с FormData. Т.е. запрос не содержит привычный многим тестировщикам body в виде JSON/XML объекта. Вместо этого данные отправляются как Content-Type: application/x-www-form-urlencoded.

Во многих случаях любой JS объект можно легко перевести в данный формат (по-сути, это будет простая url encoded строка key=value записанная через &). Правда, тут нужно быть осторожными, поскольку данный формат не подразумевает, что ключ (key) является уникальным. Т.е. ваша Form Data может содержать tags=Tag1&tags=Tag2 (несколько тегов у страницы, в моем случае).

[Request Example] URL encoded FormData[Request Example] URL encoded FormData[Request Example] Parsed FormData[Request Example] Parsed FormData

Самое важное на что стоит обратить внимание на этих скриншотах:

  • parentPath папка/страницв в AEM-e, в которой будет создана наша страница

  • template путь к теплейту, который будет использован для создания страницы

Ниже идут поля, применимые к моему проекту

  • ./jcr:title имя/тайтл моей страницы (обязательное поле на UI)

  • ./cq:tags тег, который я добавил странице (опциональное поле)

  • ./articleDate, ./articleTimeToRead и :cq_csrf_token

Все остальные поля, опциональны и могут быть опущены в запросах.

Теперь, на счет авторизации. Как видно выше, запросы содержат token. Я использовал cypress.io для написания автотестов, так что для авторизации API запросов просто указывал auth объект с username и password, на ряду с методом, хедером и body. (For more info check Cypress: Request - arguments and http-authentication).

Реализация отправки POST запроса с FormDataРеализация отправки POST запроса с FormData

key takeaways: все запросы на создание новых страниц идут на `${aem-author-URL}/libs/wcm/core/content/sites/createpagewizard/_jcr_content`, parentPath и template присутствуют для всех вариантов создания страниц с помощью темплейта.


2. Добавление компонент на страницу

  1. Находим нужную нам страницу в AEM author

  2. Жмякаем Edit

  3. Выбираем нужную позицию (место) для компоненты

  4. Жмякаем +

  5. Запускаем запись Network запросов в dev-tools

  6. Находим и выбираем нужный нам компонент

Нужный нам запрос `${aem-author-URL}/content/${page-path}/jcr:content/par/${some-url-part}/par/`.

[Request Example] Add Component to the page[Request Example] Add Component to the page

Из важного тут:

  • ./@CopyFrom темплейт (default) конфигурации компоненты (button в моем случае)

  • ./sling:resourceType название и путь к компоненте, которую я добавлял

  • parentResourceType тут я не уверен, судя по всему место, куда добавить компоненту


3. Конфигурация компонент

  1. На нужной нам странице выбираем необходимый компонент

  2. Жмякаем гаечный ключ

  3. Модифицируем конфигурацию данной компоненты

  4. Запускаем запись Network запросов в dev-tools

  5. Жмякаем кнопку Done

Наш запрос первый в списке `${aem-author-URL}/content/${page-path}/_jcr_content/par/${component-name}`.

[Request Example] Configure Component[Request Example] Configure Component

Из важного тут:

  • ./sling:resourceType название и путь к компоненте, которую я добавлял

  • :cq_csrf_token токен, значит нужно использовать auth


4. Паблиш страницы

  1. Находим нужную нам страницу

  2. Запускаем запись Network запросов в dev-tools

  3. Жмякаем Quick Publish -> Publish

4.1.

В данном случае нам нужны первые 2 запроса.

4.1.1. Получение связанных ассетов

Запрос reference.json `${aem-author-URL}/libs/wcm/core/content/reference.json?${url-params}` получаем информацию о ассетах (assets), cвязаных с нашей страницей.

[Request Example] Check Assets related to the published page[Request Example] Check Assets related to the published page

Из важного тут используются query string params. В path указан путь к нашей странице.

В ответе нам придет массив ассетов. Нам понадобятся path`s тех, чей published статус false.

[Responce Example] Check Assets[Responce Example] Check Assets

4.1.2. Публикация страницы и ассетов

Запрос replicate `${aem-author-URL}/bin/replicate` запрос на публикацию нашей страницы и связанных с ней сущностей.

[Request Example] Publish Page and Related Assets[Request Example] Publish Page and Related Assets

Как вы видите, основное на что стоит обратить внимание:

  • cmd: Activate команда для паблиша страницы

  • path пути к нашей странице и к 2м ассетам

4.2.

Поскольку паблишинг страниц асинхронное действие. Необходимо убедиться, что он состоялся.

Для этого нам понадобиться еще один запрос GET `${aem-author-URL}/etc/replication/agents.author/publish_publish/jcr:content.queue.json`. Он вернет нам массив страниц ожидающихпаблишинга. Так что придётся сделать, как минимум еще один запрос, проверить есть ли необходимая нам страница в массивеbody.queue.Опять же искать по path. Если страница все еще присутствует в очереди, придётся повторить проверку один или даже несколько раз (я выставил timeout в 1 секунду, но думаю можно и меньше).


5. Удаление страницы

  1. Находим нужную нам страницу

  2. Выбираем ее

  3. Запускаем запись Network запросов в dev-tools

  4. Жмякаем Delete-> Delete

Наш запрос `${aem-author-URL}/bin/wcmcommand`.

[Request Example] Delete Page[Request Example] Delete Page

Из важного тут:

  • cmd deletePage

  • path путь к странице

  • force: false но я бы рекомендовал ставить true (дабы при удалении не происходило дополнительных проверок)

  • checkChildren: true можно опустить


Итак, подведем итог

Выше описанным способом можно создавать и конфигурировать страницы в АЕМ-е на лету. Основная проблема возникшая у меня разобраться с последовательностью действий и структой отправляемых запросов.

В подавляющем большинстве все они содержат FormData. И тут вам придётся выбрать способ, откуда брать эту информацию (использовать готовые моки или генерировать на лету). Я выбрал второй способ и с помощью билдеров создавал необходимые мне структуры FormData (но это отдельная история).

Для тех кто дочитал аж сюда, несколько странностей вылезших в процессе создания страниц таким способом:

  • AEM заменяет пробелы на `-`. То есть если вы создали страницу с Тайтлом `Bla 1 2 3 4` и не указали специфический путь к ней, тогда АЕМ сделает эту страницу доступной по пути /bla-1-2-3-4

  • Пути страниц всегда будут в lowerCase (см пред. пример)

  • При использовании `_` в тайтле начиная с (приблизительно с 18и символов) АЕМ удалит все последующие `_`. Те если вы создали страницу с тайтлом BlaBla123456789123456_blabla, то доступ к ней будет не по /blabla123456789123456_blabla, а по /blabla123456789123456blabla

  • Для сетапа некоторых добавленных компонент понадобиться их id. Его можно получить в ответе запроса на добавление этой компоненты на страницу.

Подробнее..

Перевод Тестирование инфраструктуры как кода Terraform анализ модульных тестов и сквозной разработки путем тестирования поведен

16.12.2020 20:13:35 | Автор: admin


Для будущих студентов курса Infrastructure as a code in Ansible и всех интересующихся подготовили перевод полезного материала.

Также приглашаем записаться на открытый урок по теме Управление Kubernetes при помощи Kubespray.





С возвращением! Это очередная техническая статья из серии заметок о terraform и kubernetes на тему инфраструктуры как кода, подготовленных компанией Contino.

TL;DR
Размер команды не имеет значения. В любом случае реализация хорошего анализа конфигурации инфраструктуры на базе terraform и сквозного тестирования ее разумности не обязательно должна быть длительным и сложным процессом.

Мне подвернулась интереснейшая задача: изучить, разработать и представить подходящую платформу тестирования с открытым кодом для базы кода terraform в рамках конвейера выпуска инфраструктуры. Принцип контроль качества во всем далеко не нов. Но его реализация зависит от зрелости инфраструктуры организации и допустимого уровня риска до стадии выхода на [сколь-либо] готовый продукт.

Свежий взгляд на сферу тестирования кода для инфраструктуры позволил мне познакомиться с новейшими тестовыми платформами и обновить свои знания.

image

Стоит признать, в начале своего пути я страдал от предубеждения, что подготовка и реализация такого контроля качества корпоративного класса может потребовать значительных трудозатрат.

В конце концов, Hashicorp Terraform имеет достаточные функциональные возможности без дополнительной настройки для проверки и валидации вашей базы кода.

  • Контроль качества кода terraform fmt -check и terraform validate.
  • Предварительный просмотр terraform plan.
  • Построение TFLOG=debug terraform apply для дотошной проверки.

Средства статического анализа кода Terraform


Прочесывание Google выявило весьма обширный перечень потенциально пригодных средств тестирования terraform.

Но сначала пройдемся по списку наших требований.

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

* Отсутствие экземпляров ec2, открытых миру 0.0.0.0/0, и так далее.

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

  • Требование, определенное в большей степени личным опытом, платформа тестирования по возможности должна сокращать инженерные трудозатраты, а не увеличивать их, с учетом имеющихся навыков. Разумеется, я прекрасно понимаю, что после создания такие тестовые платформы в дальнейшем требуют значительных инженерных трудозатрат на поддержание их работы и расширение.

  • Поддержка сообщества важна, когда дело касается программного обеспечения с открытым исходным кодом. Ну и самое последнее, любые выбранные платформы должны быть написаны на распространенном языке программирования, таком как Go или Python. Если понадобится углубиться в платформу и написать собственные методы, найти инженера с опытом работы с одним языком будет, пожалуй, проще, чем с двумя. Любой предыдущий опыт работы с подобными инструментами, который может быть у имеющихся сотрудников группы и организации в целом, также будет полезен. Итак, теперь я готов отправиться на поиски правильного средства тестирования и платформ для выполнения этой работы.

Подборка: анализ и сравнение средств и платформ тестирования Terraform

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



Выбранные средства тестирования


Подытожим. Я старался найти стандартизированный модульный тест для компонентов ресурсов terraform и более настраиваемый набор тестов, которые берут конфигурацию ресурсов для валидации по результатам terraform plan.

Рассмотрев преимущества и недостатки каждой платформы, я остановил свой выбор на инструменте checkov и платформе с очень подходящим названием terraform-compliance обе они написаны на python. Они удовлетворяли всем моим описанным выше требованиям.

Конвейер выпуска инфраструктуры как кода в общих чертах выглядит примерно так.

Основательно покопавшись в этих платформах, я неизбежно пересмотрел собственный опыт и пришел к следующим актуальным заключениям по обсуждаемой теме:

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


Модульное тестирование Checkov от BridgeCrew


www.checkov.io

Checkov это инструмент статического анализа кода для инфраструктуры как кода.

Он сканирует облачную инфраструктуру, подготовленную с помощью Terraform, Cloudformation, Kubernetes, Serverless или шаблонов ARM, и выявляет неправильную конфигурацию с точки зрения безопасности и соблюдения нормативных требований.

Есть несколько модульных тестов, выполняемых по умолчанию при сканировании репозитория кода terraform, которые показывают отклонения от передовых методов например, когда согласно конфигурации безопасности у вас есть виртуальная машина на порту 22, открытом миру (0.0.0.0/0).

Все тесты можно найти по этой ссылке на GitHub.

Начать работать с платформой очень просто.

  • Установите двоичный файл.
  • Инициализируйте каталог terraform командой terraform init.
  • Запустите chechov для этого каталога.

Список всех модульных тестов, выполняемых по умолчанию, можно вывести в командной строке. Кроме того, при работе checkov платформа по умолчанию выдает все пройденные и непройденные модульные тесты. Очень удобно, начать использовать просто. Передовые методы terraform проверяются, но не все. Это фундаментальное отличие.

Chechov с радостью оценит ТОЛЬКО ваш код terraform. Платформа может работать сразу после terraform init. Ей нет дела до вашего terraform plan со всеми преимуществами и недостатками. Платформа выполняет то, что заявлено, а именно статический анализ кода. Помните о возможных последствиях, а также любых соображениях относительно логики для ваших ресурсов.

image

image

Вывод после работы checkov с указанием пройденных и непройденных проверок.


Если вы готовы заниматься углубленной разработкой на Python, то сможете написать дополнительные модульные тесты. Язык разработки платформы входил в число моих требований, поскольку иногда мне приходится анализировать базу кода тестов, чтобы прикинуть, насколько трудно будет [при необходимости] создать подобные дополнительные методы. Этот момент в сочетании с вопросами обслуживания для группы в целом стал основным фактором выбора этой платформы вместо альтернативной, позволяющей получить такой же результат.

Резюмируя, в области статического анализа кода платформа checkov просто великолепна. В частности, если мне нужно, чтобы изначально определенный IP-адрес подсети был помещен в белый список. Но такой вариант не годится для тестов e2e, нуждающихся в отдельной платформе тестирования.

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

Вот здесь-то в игру и вступает вторая платформа тестирования terraform-compliance.

Terraform-compliance


terraform-compliance.com

Terraform-compliance это облегченная платформа тестирования, предназначенная для проверки безопасности и соблюдения нормативных требований в terraform для обеспечения совместимости вашей инфраструктуры как кода с отрицательным тестированием.

Предыстория


Еще раз отмечу, сквозная разработка через тестирование поведения (BDD) стала использоваться как платформа тестирования недавно, подчеркнув потребность в универсальной платформе тестирования. Но это не единственная польза.Простота.

На самом деле, мне кажется, что BDD получает недостаточно внимания. Возможно, вы слышали о разработке посредством тестирования (TDD), которая глубоко пускает корни, прежде всего в среду разработки программного обеспечения. Но именно здесь платформы наподобие BDD упрощают создание дополнительной логики, предлагая обычному специалисту по обслуживанию инфраструктуры более простой, лаконичный и повторяемый способ разработки сквозных настраиваемых тестов без углубленного изучения какого-либо специализированного и нового языка программирования.

И хотя с помощью кода можно описать, по сути, все на свете, в конечном счете все упирается в управляемость, возможности постичь сложность кода (для чего может потребоваться подготовка обширной документации), не говоря уже о поддержке и обслуживании.Здесь можно подробнее познакомиться с BDD.

Cucumber.io это не просто язык, это система, упрощающая работу по тестированию за счет применения подхода WYSIWYG к созданию тестов, пониманию их работы и обслуживанию. Эти примеры определяются до начала разработки и используются в качестве критериев приемки.

Они являются частью определения.

Тестирование с помощью Terraform-Compliance


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

Вот пример такого теста, разработанного с помощью платформы terraform-compliance с применением BDD. Он позволяет выполнять достаточно сложное сквозное тестирование.

Платформа terraform-compliance использует вывод terraform plan. В результате это позволяет формировать полные планы выпусков и тщательно тестировать их. Например, контролировать использование правильной пары ключей шифрования [для вашего поставщика облачных услуг] для учетной записи, среды и т.п. У вас будет большая свобода для творчества, и самое важное работать с платформой очень просто.

Просто ознакомьтесь с приведенными ниже действиями и примерами.

  • Шаг 1. Инициализируйте каталог terraform:# terraform init
  • Шаг 2. Можно быстро сформировать terraform plan следующей командой: #terraform plan -out=plan.out
  • Шаг 3. Напишите несколько тестов. Дело нехитрое уже есть папка с примерами. Давайте пройдемся по моим собственным примерам тестов, приведенным ниже, написанным на основе моего вывода terraform plan.

Это фрагмент плана terraform конфигурации terraform, которая создает EKS с указанной группой запуска. Давайте удостоверимся, что в нашем коде инфраструктуры terraform не применяется instancetype, а используется одобренный вариант a1.xlarge или a1.2xlarge.

Теперь я намеренно изменю его на t2.small, чтобы имитировать непрохождение теста.

Напишем тест, чтобы обеспечить успешную валидацию этого требования.

  • Шаг 4. Давайте заставим terraform-compliance оценить плат с использованием тестовых сценариев: #terraform-compliance -p plan.out -f ./<test-cases-folder>


Выполнение тестов


Пример результата с прохождением и непрохождением

image

Если в нашем коде инфраструктуры Terraform используется правильный instancetype, то все результаты будут зелеными SUCCESS.

Если же наш код инфраструктуры Terraform нарушает требование из-за наличия неправильного instancetype, то результаты будут красными FAIL.

Давайте напишем еще больше тестов

image

Еще несколько простых тестов, взятых из каталога примеров:

image

Если одна ситуация с непрохождением в этом случае пользователь увидит фактическое_значение, которое извлекается и показывается в целях справки и отладки.

Результаты тестов


После выполнения всех тестов отображается удобная итоговая сводка по всем пройденным и непройденным тестам, в которой также указываются пропущенные тесты. Она мне нравится тем, что позволяет написать длинный список тщательных тестов, а также найти в конце четкие сведения о том, какие тесты не были пройдены и когда. Кроме того, в случае непрохождения некоторые тесты могут быть пропущены с указанием тега @warning, как показано в примере ниже.
habrastorage.org/getpro/habr/upload_files/c22/910/cb9/c22910cb95fb4ccc7555d44bd8b5436b

Итоги


Без всякого сомнения, это была великолепная возможность заново взглянуть на некоторые платформы для валидации и тестирования превосходного качества, имеющиеся для кода как инфраструктуры Terraform.

Я получил удовольствие, рассматривая обе эти платформы, и был особенно удивлен простотой интеграции checkov, а также потрясающей валидацией e2e terraform plan и вариантами нестандартного тестирования, которые предлагает terraform-compliance.

Последняя напоминает мне поведение behave, еще одной великолепной платформы тестирования BDD e2e kubernetes, с которой я работал в прошлом.

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

Если вам требуется проверка конфигурации на соответствие передовым методам, когда не требуется terraform plan, то, возможно, checkov это то, что вам нужно. В иных случаях ответом может быть платформа terraform-compliance, имеющая более богатый набор функций для валидации terraform plan. Лучше всего то, что, будучи платформой BDD, terraform-compliance очень проста в освоении.

Модульное тестирование прежде всего. Проще простого. Платформа Checkov от bridgecrewio позволяет выполнять проверку соответствия передовым методам без дополнительной настройки.

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

P.S. В компании Contino реализуется порядочное количество фантастических проектов. Если вам хотелось бы поработать над суперсовременными инфраструктурными проектами или вы ищете серьезные задачи свяжитесь с нами! Мы нанимаем персонал и ищем светлые умы на всех уровнях. Мы в компании Contino гордимся тем, что разрабатываем передовые проекты по трансформации облачных систем, предназначенные как для компаний среднего размера, так и для крупных предприятий.
Узнать подробнее о курсе Infrastructure as a code in Ansible.

Записаться на открытый урок по теме Управление Kubernetes при помощи Kubespray.

Подробнее..

Почему большинство юнит тестов пустая трата времени? (перевод статьи)

17.05.2021 16:18:04 | Автор: admin

Автор: James O Coplien

Перевод: Епишев Александр

1.1 Наши дни

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

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

Итак, тестирование снова вступает в игру. На этот раз - юнит-тестирование на стероидах. Объектно-ориентированное сообщество обнаружило ценность ранней обратной связи, обусловленной возрастающей скоростью техники, а также появлением всё большего числа персональных компьютеров. Дизайн становится более сфокусированным на данных, так как объекты всё больше конструируются исходя из структур этих данных, чем каких-либо свойств своих же методов. Нехватка явно выраженной структуры вызовов усложнила возможность поместить выполнение какой-либо атомарной функции в контекст своего же выполнения. И насколько бы малым ни был шанс для подобной возможности, и тот был отнят полиморфизмом. В результате, интеграционное тестирование оказывается вне, а первоочередную роль принимает на себя юнит-тестирование. Где-то на фоне всё ещё виднелось системное, но, скорее уже как проблема кого-то другого, или, что еще более опасно, как один из вариантов более зрелой версии юнит-тестирования, которое проводилось самими же создателями кода.

Классы превратились в объекты анализа, и, в определенной степени, проектирования. Популярной техникой дизайна стали CRC-карты (обычно представляющие Классы, Ответственности и Кооперацию), каждый класс в которых представлен отдельным человеком. Объектная ориентация начала ассоциироваться с антропоморфным дизайном. Классы, к тому же, превратились в единицы администрирования, дизайна и программирования, а их антропоморфная суть усилила стремление каждого создателя класса его протестировать. Поскольку у некоторых методов класса сохранилась такая же контекстуализация, как и у функции FORTRAN, у программистов возникла необходимость предоставлять контекст перед выполнением метода (помните, что мы не тестируем классы и, даже, не тестируем тестовые объекты, единицей функционального теста является метод). Юнит тесты обеспечивали выполнение сценариев драйверами. Моки - контекст состояния окружения (энва) и других методов, от которых зависел тестируемый метод. При подготовке к тесту, тестовые окружения поставляли необходимые средства для создания каждого объекта в его правильном состоянии.

1.2 Лекарство хуже болезни

Конечно же, юнит-тестирование не является проблемой исключительно объектно-ориентированного программирования, de rigueur (лат. "крайней необходимостью"), скорее всего, его сделала комбинация объектной-ориентированности, эджайла, разработки программного обеспечения, а также рост инструментов и вычислительных мощностей. Как консультант, я часто слышу вопросы о юнит-тестировании, включая следующий от одного из своих клиентов, Ричарда Якобса (Richard Jacobs) из Sogeti (Sogeti Nederland B.V.):

Второй вопрос касается юнит-тестов. Если я правильно припоминаю, вы говорили, что юнит-тесты - это пустая трата времени. Во-первых, я был удивлен. Тем не менее, сегодня моя команда сообщила, что их тесты сложнее, чем сам код. (Это не та команда, которая изначально написала код и юнит тесты. Поэтому некоторые тесты застают их врасплох. Текущая команда более высоко квалифицирована и дисциплинирована.) В таком случае, по моему, это пустая трата времени... Когда я ежедневно программировал, то создавал действительно тестируемый код, однако, почти никогда не писал никакие юнит тесты. При этом я заслуживал признание за свой качественный код и почти безошибочное программное обеспечение. Мне хотелось бы разобраться, ПОЧЕМУ такой вариант работал в моем случае?

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

На практике же, особенности языка программирования затрудняют достижение подобной компактности выражения в тесте, поэтому для проведения полного тестирования, количество строк кода в юнит тестах должно было бы быть на порядок выше, чем в самом тестируемом юните. Немногие разработчики признают, что проводят только выборочное или частичное тестирование, многие же остальные скажут вам, что делают полное тестирование, исходя из некоего своего предполагаемого представления о том, что значит полное. К таким представлениям относится заявление типа Каждая строка кода покрыта, что, с точки зрения теории вычислений, является откровенной чепухой, особенно в понимании того, действительно ли код делает, что должен. Более детально мы обсудим эту проблему ниже. Всё же, большинство программистов, понимают юнит тестирование именно так, изначально обрекая его на провал.

Будьте скромны относительно способностей ваших юнит тестов, кроме тех случаев, когда для тестируемого юнита у вас есть оракул в виде внешних требований. Сомнительно, что юнит тесты способны протестировать более одной триллионной от функциональности любого предоставленного метода в рамках разумного цикла тестирования. Придите в себя.

(Здесь, триллион - не риторический прием, а цифра, основанная на различных возможных состояниях, с учетом того, что средний размер объекта представляет собой четыре слова, и, по консервативной оценке, вы используете 16-битные слова).

1.3 Тесты ради тестов и спроектированные тесты

У меня был клиент из Северной Европы, разработчики которого должны были предоставить 40% покрытия кода, для, так называемого, 1-го уровня зрелости программного обеспечения, 60% для 2-го уровня и 80% для 3-го, хотя были и стремящиеся к 100%. Без проблем! Как вы могли бы предположить, достаточно сложная процедура с ветвлениями и циклами стала бы вызовом, однако, это всего лишь вопрос принципа divide et impera (разделяй и властвуй). Большие функции, для которых 80% покрытие было невозможным, разбивались на множество более мелких, для которых 80% уже было тривиальным. Такой подход повысил общий корпоративный показатель зрелости команд всего лишь за один год, потому как вы обязательно получаете то, что поощряете. Конечно же, это также означало, что функции больше не инкапсулировали алгоритмы. Невозможным оказалось понимание контекста выполняемой строки, точнее тех, которые предшествуют и следуют за ней во время выполнения, поскольку эти строки кода больше не имеют прямого отношения к той, которая нас интересует. Такой переход в последовательности теперь происходил благодаря вызову полиморфной функции - гипер-галактической GOTO. Даже если всё, что вас беспокоит, - это покрытие решений (branch coverage), это больше не имеет значения.

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

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

Задумайтесь на секунду о вычислительной сложности этой задачи. Под 100% покрытием, я подразумеваю проверку всех возможных комбинаций всех возможных ветвлений, проходящих через все методы класса, которые воспроизводят все возможные конфигурации битов данных, доступные этим методам, в каждой инструкции машинного языка во время выполнения программы. Все остальное - это эвристика, о корректности которой нельзя сделать никаких формальных заявлений. Число возможных путей выполнения с помощью функции невелико: скажем, 10. Перекрестное произведение этих путей с возможными конфигурациями состояний всех глобальных данных (включая данные экземпляра, которые для области видимости метода являются глобальными) и формальных параметров в действительности же очень велико. Перекрестное произведение этого числа с возможной последовательностью методов внутри класса представляется счетно-бесконечным. Если вы возьмете несколько типичных чисел, то быстро осознаете, насколько вам повезло, если получите покрытие лучше, чем 1 из 1012.

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

Если вы пишете тест с целью покрытия как можно большего количества возможных сценариев, тогда можете уже выделять ферму машин для прогона тестов и отслеживания последних результатов 24/7.

Помните, однако, что автоматизированный хлам - это всё ещё хлам. И те из вас, у кого есть корпоративная Lean-программа, могли заметить, что основы производственной системы Toyota, которые лежали в основе Scrum, очень сильно противились автоматизации интеллектуальных задач (http://personeltest.ru/away/www.computer.org/portal/web/buildyourcareer/Agile Careers/-/blogs/autonomation). Более эффективно - это постоянно удерживать человека процессе, что становится еще более очевидным при исследовательском тестировании. Если вы собираетесь что-то автоматизировать, автоматизируйте что-нибудь ценное. Автоматизировать необходимо рутинные вещи. Возможно даже, вы получите еще больше прибыли от инвестиций, если автоматизируете интеграционные тесты, тесты для проверки регрессионных багов, а также системные, вместо того, чтобы заниматься автоматизацией юнит тестов.

Более разумный подход уменьшает объем тестового кода за счет формального проектирования тестов: то есть, формальной проверки граничных условий, большего количества тестов белого-ящика и т.д. Для этого необходимо, чтобы программный юнит проектировался как тестируемый. Вот как это делают инженеры по аппаратному обеспечению: разработчики предоставляют контрольные точки, способные считывать значения c J-Tag микросхем, для доступа к внутренним значениям сигналов микросхем - это равносильно доступу к значениям между промежуточными вычислениями, содержащимися в вычислительном юните. Я настоятельно рекомендую делать подобное на системном уровне, на котором должно быть сосредоточено основное внимание тестирования; я никогда не видел, чтобы кто-то достигал подобного на уровне юнита. Без таких приемов вы ограничиваете себя юнит-тестированием черного ящика.

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

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

1.4 Убеждение, что тесты умнее кода, говорит о скрытом страхе или плохом процессе

У программистов существует негласное убеждение о том, что они способны мыслить более ясно (или, скорее, догадываться), во время создания тестов, чем при написании исходного кода, или что в тесте, каким-то образом, содержится больше информации, чем в самом коде. Это просто формальная ерунда. Психология здесь поучительна и важна, потому что именно она, а не какое-то рассчитываемое свойство, больше всего руководит поведением разработчика.

Если ваши кодеры предоставляют в юнит-тестах больше строк, чем в исходном коде, это, вероятно, означает одно из следующего. У них может быть параноидальное отношение к корректности; паранойя вытесняет ясное мышление и инновации, которые служат предзнаменованием высокого качества. Им может не хватать аналитических ментальных инструментов или дисциплины мышления, и хотелось бы, чтобы машина думала за них. Машины хорошо справляются с повторением механических задач, однако дизайн тестов по-прежнему требует тщательного обдумывания. Или может случиться так, что в вашем процессе частая интеграция невозможна из-за плохо налаженных процессов разработки или неподходящих инструментов. Программисты делают всё возможное, чтобы компенсировать подобную ситуацию за счет тестов там, где они хотя бы в какой-то степени обретают контроль над собственной судьбой.

Если у вас большой объем юнит-тестов, оцените обратную связь в процессе разработки. Интегрируйте код чаще; сократите время сборки и интеграции; сократите количество юнит тестирования и перейдите больше к интеграционному.

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

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

Тем не менее, будем честны, ошибки будут всегда. Тестирование никуда не денется.

1.5 У тестов с низким уровнем риска низкая (даже потенциально отрицательная) отдача

Как то я озвучил своему клиенту предположение о том, что множество их тестов могут быть тавтологическими. Предположим, задача какой-то функции - это присвоение X значения 5, и я готов поспорить, что существует соответствующий тест для данной функции, который, после запуска, проверяет, равняется ли X 5. Снова же, хорошее тестирование, основывается на тщательном размышлении, а также базовых принципах управления рисками. Управление рисками строится на статистике и теории информации; если тестировщики (или, по крайней мере, менеджер по тестированию) не обладают хотя бы элементарными навыками в этой области, вы, с большой вероятностью, создаете множество бесполезных тестов.

Разберем тривиальный пример. Цель тестирования - предоставить информацию о вашей программе. (Тестирование само по себе не повышает качество; это делают программирование и проектирование. Тестирование лишь сообщает об упущениях команды в создании правильного проектирования и соответствующей реализации.) Большинство программистов хотят услышать информацию о том, что их программный компонент работает. Поэтому, как только в проекте трехлетней давности была создана первая функция, тут же для нее был написан и юнит тест. Тест ни разу не падал. Вопрос: Много ли информации содержится в этом тесте? Другими словами, если 1 - это успешно выполненный тест, а 0 - упавший, тогда сколько будет информации в следующей строке результатов:

11111111111111111111111111111111

Существует несколько возможных ответов, обусловленных видом применяемого формализма, хотя большинство из них не верны. Наивный ответ - 32, однако, это биты данных, а не информации. Возможно, вы информационный теоретик и скажете, что количество битов информации в однородной двоичной строке равносильно двоичному логарифму длины этой строки, которая в данном случае равна 5. Однако это не то, что я хочу знать: в конце концов хотелось бы понять, сколько информации можно получить после одноразового прогона такого теста. Информация основывается на вероятности. Если вероятность успешного прохождения теста равняется 100%, тогда, по определению теории информации, этой информации нет вообще. Ни в одной из единиц указанной выше строки не содержится почти никакой информации. (Если бы строка была бесконечно длинной, то в каждом тестовом прогоне было бы ровно ноль битов информации.)

Далее, сколько бит информации в следующей строке тестовых прогонов?

1011011000110101101000110101101

Ответ... намного больше. Вероятно, 32. Это означает, что в каждом тесте содержится намного больше информации. Если мы изначально не способны предсказать, пройдет ли тест успешно или нет, тогда каждый запуск теста содержит полный бит информации, и добиться чего-то лучшего вы не сможете. Видите ли, разработчики любят поддерживать тесты, которые проходят успешно, потому что это подогревает их эго и уровень комфорта. Однако, информация поступает от упавших тестов. (Конечно же, мы могли бы взять другую крайность:

00000000000000000000000000000000

в которой, фактически, нет никакой информации, в том числе, даже о процессе улучшения качества.)

Если вы хотите сократить объем тестов, первое, что следует сделать, - это обратить внимание на те, которые ни разу за год не упали, и рассмотреть вопрос об их удалении. Такие тесты не предоставляют вам никакой информации, или, по крайней мере, очень мало информации. Ценность производимой ими информации может не оправдывать затрат на поддержку и выполнение таких тестов. Эти тесты - первые претенденты для удаления, и не имеет значения, это юнит тесты, интеграционные или системные.

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

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

Если у вас есть подобные тесты - это второй претендент на удаление.

Третий набор для удаления - тавтологические тесты. Я сталкиваюсь с ними чаще, чем вы можете себе представить, особенно среди последователей, так называемой, разработки через тестирование (TDD). (Кстати, проверка this на ненулевое/не пустое (non-null) значение при входе в метод, не является тавтологической, и может быть очень информативной. Однако, как и в случае с большинством юнит тестов, лучше сделать ассершн, чем пичкать свой тестовый фреймворк подобными проверками. Подробнее об этом ниже.)

Во многих компаниях, единственные тесты с бизнес-ценностью - это те, в основании которых лежат бизнес-требования. Большинство же юнит тестов основываются на фантазиях программистов о том, как должна работать функция: на их надеждах, стереотипах, а иногда и желаниях, как все должно было бы быть. У всего этого нет подтвержденной ценности. В 1970-х и 1980-х годах существовали методологии, опирающиеся на прослеживаемость (tracebility), и стремящиеся сократить системные требования вплоть до уровня юнитов. В общем, это NP-трудная (нелинейная полиномиальная) задача (если только вы не выполняете чисто процедурную декомпозицию), поэтому я очень скептичен в отношении всех, кто говорит, что способен её решить. В итоге, единственный вопрос, который следовало бы задавать каждому тесту: Если тест упадет, какое из бизнес-требований будет нарушено? В большинстве случаев, ответ: Я не знаю. Если вы не понимаете ценность теста, тогда, теоретически, он может иметь нулевую ценность для бизнеса. У теста есть стоимость: поддержка, время вычислений, администрирование и так далее. Значит, у теста может быть чистая отрицательная ценность. И это четвертая категория тестов, которые необходимо удалять. Такие тесты, не смотря на их способность что-то проверять, в действительности ничего не проверяют.

Если вы не можете объяснить, как сбой какого-либо юнит теста отражается на рисках, следует подумать о возможном удалении такого теста. При отсутствии формальных критериев корректности, существуют более эффективные техники устранения изъянов в качестве, такие как исследовательское тестирование и методы Монте-Карло. (Они великолепны, и я рассматриваю их как отдельную категорию в отличие от того, о чем рассказываю здесь.) Не используйте юнит тесты для подобных проверок.

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

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

1.6 Сложное - сложно

Существует следующая дилемма: большая часть интересных показателей о качестве определенных программ находиться в распределении результатов тестирования, несмотря на то, что традиционные подходы к статистике, всё же, предоставляют ложную информацию. Так, в 99,99% всех случаев тест может быть успешным, но однажды упав за десять тысяч раз, он убьет вас. Опять же, заимствуя аналогию из мира железа, для уменьшения вероятности ошибки до сколь угодно низкого уровня, вы можете всё проектировать с учетом заданной вероятности отказа или же провести анализ наихудшего случая (WCA). Специалисты по аппаратному обеспечению обычно используют WCA при проектировании асинхронных систем для защиты от сбоев в сигналах, выходящих за пределы проектных параметров: один сбой на 100 миллионов раз. В области аппаратного обеспечения, сказали бы, что коэффициент качества (FIT rate) такого модуля равняется 10 - десять отказов на триллион (Failures In a Trillion).

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

Приятно читать статью, проливающую свет на причину моего успеха (и остальной части моей команды). Возможно, Вы уже знаете, что я инженер по авионике, чья карьера началась с разработки встраиваемого программного обеспечения, и, отчасти, разработки оборудования. И вот с таким образом мышления, ориентированным на особенности работы оборудования, я начал тестировать свое программное обеспечение. (Команда состояла из четырех человек: 3-х инженеров-электриков из Делфтского университета (включая меня, в качестве специалиста по авионике) и одного инженера-программиста (из Гаагского университета). Мы были очень дисциплинированы в разработке систем безопасности для банков, пенитенциарных учреждений, пожарных, полицейских участков, служб экстренной помощи, химических заводов и т.д. В каждом из случаев всё должно было правильно заработать с первого раза.)

Обычно, при наличии разумных предположений, вы можете провести WCA аппаратного обеспечения, из-за легко прослеживаемых причинно-следственных связей: можно взять схему и разобраться в причинах изменения состояний в элементах памяти. Состояния в машине фон Неймана изменяются в результате побочных эффектов (side effects) выполнения функции, и, как правило, отследить причину такого изменения нереально, даже если некое состояние является достижимым. Объектная ориентация еще больше усугубляет ситуацию. Невозможно понять, какая из инструкций последней изменила состояние, используемое программой.

Большинство программистов убеждены, что построчное покрытие исходного кода, или, по крайней мере, покрытие ветвлений является вполне достаточным. Нет. С точки зрения теории вычислений, покрытие наихудшего случая означает анализ всевозможных комбинаций в последовательностях работы машинного языка, при котором гарантируется достижение каждой инструкции, а также - воспроизведение каждой возможной конфигурации битов данных в каждом из значений счетчика команд выполняемой программы. (Недостаточна и симуляция состояния среды выполнения только лишь для модуля или класса, содержащего тестируемую функцию или метод: как правило, любое изменение в каком-либо месте может проявиться в любом другом месте программы, а поэтому, потребует повторного тестирования всей программы. Формальное доказательство предложено в статье: Перри и Кайзера (Perry and Kaiser), Адекватное тестирование и объектно-ориентированное программирование (Adequate Testing and Objectoriented Programming), Журнал объектно-ориентированного программирования 2 (5), январь 1990 г., стр. 13). Даже взяв небольшую программу, мы уже попадаем в такое тестовое окружение, количество комбинаций в котором намного превышает количество молекул во Вселенной. (Мое определение понятия покрытие кода - это процент всех возможных пар, {Счетчик команд, Состояние системы}, воспроизводимых вашим набором тестов; все остальное - эвристика, которую, очевидно, вам сложно будет как-либо обосновать). Большинство выпускников бакалавриата смогут распознать проблему остановки (Halting Problem) в большинстве вариантов подобных задачах и поймут, что это невозможно.

1.7 Меньше - это больше или вы не шизофреник

Вот еще одна проблема, которая имеет особое отношение к первоначальному вопросу моего клиента. Наивный тестировщик пытается извлечь множество данных из результатов тестирования, при этом постоянно поддерживая все существующие тесты или даже добавляя новые; это приводит к точно такой же ситуации, в которой оказался мой клиент, когда сложность тестов (объемы кода или какие-только-хотите-метрики) начинает превосходить сложность исходного кода. Тестируемые классы - это код. Тесты - это код. Разработчики пишут код. Когда разработчики пишут код, они допускают около трех ошибок, непосредственно влияющих на систему, на каждые тысячу строк кода. Если бы мы случайным образом выбрали участки кода с подобными ошибками у моего клиента, включая тесты, то обнаружили бы, что в тестах содержиться код, который приводит к неправильным результатам чаще, чем реальный баг, останавливающий выполнение кода!

Некоторые мне говорят, что подобное не имеет к ним отношения, поскольку они уделяют значительно больше внимания тестам, чем исходному коду. Во-первых, это просто вздор. (Меня действительно смешат утверждающие, что, с одной стороны, они способны забывать о своих ранее сделанных предположениях во время создания изначального кода, и, с другой, те, кто может привнести свежий и независимый взгляд во время тестирования. Как первые, так и вторые должны быть шизофрениками.) Посмотрите, что делают разработчики при запуске тест-сьютов: они их запускают, но не думают (кстати, это же относится и к большей части Agile манифеста). На моей первой работе в Дании был проект, в значительной степени построенный на XP методологии и юнит тестировании. Я всячески пытался собрать билд на своей локальной машине, и после долгой борьбы с Maven и другими инструментами, наконец-то, мне это удалось. Каким же было разочарование, когда я обнаружил, что юнит-тесты не проходят. Пришлось обратиться к своим коллегам, которые сказали: О, так тебе нужно запустить Maven с вот этим флагом, он отключает вот эти тесты - из-за изменений эти тесты уже не работают, поэтому их необходимо отключить.

Если у вас 200, 2000, или 10 000 тестов, вы не будете тратить время на тщательное исследование и (кхе-кхе) рефакторинг каждого из них каждый раз, когда тест падает. Самая распространенная практика, которую я наблюдал, работая в стартапе еще в 2005 году, - это просто переписать результат старых тестов (ожидаемый результат или результаты вычислений такого теста) новыми результатами. С психологической перспективы, зеленый статус - это вознаграждение. Современные быстрые машины создают иллюзию возможности замены мышления программиста; их скорость намекает на исключение моей необходимости мыслить. Ведь, в любом же случае, если клиент сообщит об ошибке, я, в свою очередь, сформулирую гипотезу о ее действительной причине, внесу изменения, исправляющие поведение системы, и, в результате, с легкостью смогу себя убедить, что функция, в которую я добавил исправление, теперь работает правильно. То есть я просто переписываю результат выполнения этой функции. Однако, подобное - просто лженаука, основанная на колдовстве, связь с которым - причинность. В таком случае, необходимо повторно запустить все регрессионные и системные тесты.

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

1.8 Вы платите за поддержку тестов и качество!

Суть в том, что код - это часть вашей системной архитектуры. Тесты - это модули. Тот факт, что кто-то может не писать тесты, не освобождает его от ответственности заниматься проектированием и техническим обслуживанием возрастающего количества модулей. Одна из методик, которую часто путают с юнит-тестированием, но использующая последнее в качестве техники - это разработка через тестирование (TDD). Считается, что она улучшает метрики сцепления и связности (coupling and coherence), хотя, эмпирические данные свидетельствуют об обратном (одна из статей, опровергающих подобное представление на эмпирических основаниях принадлежит Янзену и Саледиану (Janzen and Saledian), Действительно ли разработка через тестирование улучшает качество проектирования программного обеспечения? IEEE Software 25(2), март/апрель 2008 г., стр. 77 - 84.) Еще хуже то, что таким образом, в качестве запланированного изменения, вы уже вводите связанность (coupling) между каждым модулем и сопровождающими их тестами. У вас появляется необходимость относиться к тестам так же как и к системным модулям. Даже если вы удаляете их перед релизом, это никак не сказывается на необходимости их обслуживать. (Подобное удаление может быть даже достаточно плохой идеей, но об этом дальше.)

Я обратил внимание, что подавляющая часть юнит-тестов, особенно созданных с помощью JUnit, представляет собой замаскированные утверждения (assertions). Когда же я создаю какое-нибудь восхитительное программное обеспечение, то засыпаю его подобными утверждениями, описывая ожидаемые мной результаты, и по отношению к тем, кто вызывает мои функции, и в отношении самих функций, которые обязаны что-то предоставить своим клиентам. Такие проверки дорабатываются в том же артефакте, где находится и остальная часть моего кода. Множество энвайерментов предоставляют средства административной стерилизации этих тестов во время релиза.

Более профессиональный подход - оставить эти асершены в коде даже после релиза, автоматически отправлять отчеты об ошибках от имени конечного пользователя и, возможно, пытаться перезапускать приложение каждый раз, когда подобная проверка провалена. В одном из ранее упомянутых стартапов, мой босс настаивал, чтобы мы такого не делали. Я указал, что отрицательный результат проверок означает: в программе что-то пошло совсем не так, и, скорее всего, данная программа выдаст неправильный результат. Даже мельчайшая ошибка в создаваемом нами продукте, может обойтись клиенту в 5 миллионов долларов дополнительных доработок. На что он ответил: Для компании намного важнее избегать видимости чего-то неправильно сделанного, чем останавливаться еще до получения ошибочных результатов. Я ушел из этой компании. Возможно, сегодня вы один из её клиентов.

Превратите юнит-тесты в утверждения (assertions). Используйте их для обеспечения отказоустойчивости архитектуры высокодоступных систем. Это решает проблему поддержки множества дополнительных программных модулей, которые оценивают ход выполнения (программы) и проверяют правильность (её) поведения; это одна часть юнит-тестирования. Другая - это драйвер, выполняющий сам код: в этом отношении, рассчитывайте, на свои стресс-, интеграционные и системные тесты.

Почти последнее, существуют такие юнит-тесты, которые повторяют системные, интеграционные или другие виды тестов. На заре вычислений, когда компьютеры были медленными, вместо того, чтобы дожидаться запуска системных тестов, юнит-тесты предоставляли разработчику более быструю обратную связь о том, сломало ли их изменение код. Сегодня, когда появились более дешевые и мощные компьютеры, этот аргумент кажется менее убедительным. Каждый раз, внося изменения в свое приложение Scrum Knowsy, я тестирую его на системном уровне. Разработчики должны непрерывно интегрироваться и, так же непрерывно проводить тестирование системы, а не сосредотачиваться на своих юнит-тестах и откладывать интеграцию, даже на час. Так что избавляйтесь от юнит-тестов, которые дублируют то, что уже делают системные тесты. Если системный уровень обходится слишком дорого, создайте наборы интеграционных тестов. Рекс (Rex) считает, что следующим большим скачком в тестировании будет разработка таких юнит, интеграционных и системных тестов, которые устраняют случайные упущения и дублирование.

Проверьте свои тестовые наборы на предмет репликации; вы же способны профинансировать это в рамках своих гибких (Lean) программ. В первую очередь, создавайте системные тесты с хорошим функциональным покрытием (а не покрытием кода) - помните, что правильная реакция на неправильные входные данные или другие непредвиденные условия является частью ваших функций.

Последнее: однажды я от кого-то услышал оправдание в необходимости создания (особого) юнит-теста, из-за невозможности проверить некий блок кода с помощью внешнего интерфейса инструмента для тестирования. Если учесть, что подобные интерфейсы хорошо спроектированы и способны воспроизводить различное, наблюдаемое в реальном мире, поведение программы, и вы нашли такой код, к которому не достучаться из вашей тестировочной системы, тогда... удалите этот код! Серьезно, размышление о коде с позиции системных тестов может быть отличным способом для обнаружения мертвого кода. Это даже более ценно, чем поиск ненужных тестов.

1.9 Это процесс, глупец или лихорадка зеленого статуса

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

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

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

Системные тесты почти сразу же погружают вас в подобную позицию размышления. Разумеется, вам все еще нужна будет более подробная информация, для этого на помощь приходит отладка (debugging). Отладка - это использование инструментов и устройств, помогающих изолировать ошибку. Отладка - не тестирование. Во-первых, она представляет собой ad-hoc (интуитивную активность) и, во-вторых, выполняется на основании последовательного перехода от ошибки к ошибке. Юнит тесты могут быть полезным инструментом отладки. На собственном опыте я обнаружил, что лучше всего работает комбинация различных инструментов, среди которых наиболее эффективными являются наборы с невалидными данными и доступ к глобальному контексту, включающий все значения данных и случайную трассировку стека.

1.10 Подводим итоги

Вернемся к моему клиенту из компании Sogeti. Вначале, я упоминал его высказывание:

Когда я ежедневно программировал, то создавал действительно тестируемый код, однако почти никогда не писал никакие юнит тесты. При этом я заслуживал признание за свой качественный код и почти безошибочное программное обеспечение. Мне хотелось бы разобраться, ПОЧЕМУ такой вариант работал в моем случае?

Возможно, Ричард - это один из тех редких людей, которые знают, как думать самому, вместо того, чтобы позволять компьютеру думать за него - будь то проектирование системы или дизайн более низкого уровня. Я чаще наблюдаю подобную ситуацию в Восточноевропейских странах, где отсутствие общедоступного компьютерного оборудования заставляло людей мыслить. Просто не хватало компьютеров. Когда я впервые посетил Сербию в 2004 году, студенты из ФОН (факультета информатики) могли получить доступ к компьютеру для выхода в интернет один раз в неделю. Расплата за ошибку высока: если запуск вашего кода не отработает, придется ждать еще неделю, чтобы повторить свою попытку.

К счастью, я вырос именно в такой культуре программирования, мой код записывался на перфокартах, которые отдавались оператору для установки в очередь машины, а затем, через сутки, собирались результаты. Такой формат действительно заставлял вас или же задуматься - или же, потерпеть неудачу. У Ричарда из Sogeti было аналогичное воспитание: у них была неделя на подготовку кода и всего один час на его запуск. Всё должно было делаться правильно с первого раза. В любом случае, обдуманный проект должен оценивать возможные риски, связанные с затратами, и устранять их по одному в каждой итерации, уделяя особое внимание постоянно растущей ценности. Одна из моих любимых циничных цитат: Я считаю, что недели программирования и тестирования могут сэкономить мне часы планирования. Что меня больше всего беспокоит в культуре раннего провала (fail-fast), так это не столько понятие провала, сколько слово раннее. Много лет назад мой босс Нил Халлер мне сказал, что отладка - это не то, что вы делаете, сидя перед своей программой с отладчиком; это то, что вы делаете, откинувшись на спинку стула и глядя в потолок, или обсуждение ошибки с командой. Однако многие, якобы ярые приверженцы эджайл методологий, ставят процессы и JUnit выше людей и взаимодействий.

Лучший пример, услышанный мной в прошлом году, был от моей коллеги, Нэнси Гитинджи (Nancy Githinji), управлявшей вместе со своим мужем IT-компанией в Кении; сейчас они оба работают в Microsoft. Последний раз, посещая свой дом (в прошлом году), она познакомилась с детьми, которые проживают в джунглях и пишут программы. Они могут приезжать раз в месяц в город, чтобы получить доступ к компьютеру и апробировать свой код. Я хочу нанять этих детей!

Мне, как стороннику эджайла (да и просто из принципа), немного больно признавать, что Рекс оказался прав, как, впрочем-то это было и ранее , достаточно красноречиво сказав: В этой культуре раннего провала (fail fast) есть нечто небрежное, она побуждает швырнуть кучу спагетти на стену, особо даже не задумываясь отчасти, из-за чрезмерной уверенности в заниженных рисках, предоставляемых юнит-тестами. Культура раннего провала может хорошо работать при очень высокой дисциплине, подкрепленной здоровым скептицизмом, однако редко можно встретить такое отношение в динамичном IT-бизнесе. Иногда ошибки требуют обдумывания, а последнее требует больше времени, чем результаты, достигаемые ранним провалом. Как только что напомнила моя жена Гертруда: Никто не хочет, чтобы ошибки затягивались на долго

Если вы наймете профессионального менеджера или консультанта по тестированию, он сможет помочь вам разобраться в вопросах более масштабной картины тестирования: интеграционным и системным тестированием, а также подходящим для них инструментарием и процессами. Это важно. Но не забывайте о позиции продакт овнера, бизнес-аналитика или менеджера в Scrum: управление рисками находится в центре их деятельности, и, возможно, именно поэтому Джефф Сазерленд говорит, что заказчик должен разрабатывать (а в лучшем случае и проектировать) системные тесты для использования в качестве входных данных до или во время планирования спринта.

Что касается Интернета: грустно и откровенно страшно, что там так много всего. Много советов, но очень мало из них подкреплено теорией, данными или даже моделью того, почему необходимо довериться тому или иному совету. Хорошее тестирование немыслимо без скептицизма. Относитесь скептически к себе: измеряйте, доказывайте, делайте повторные попытки. Ради всего святого, отнесись ко мне скептически.

Пишите мне свои комментарии на jcoplien@gmail.com с копией Рексу вначале этого письма.

В заключение:

  • Сохраняйте регрессионные тесты до года, большинство из них должны быть тестами системного уровня, а не юнит-тестами.

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

  • Исключая предыдущее заявление, если у X есть определенная бизнес-ценность и вы можете протестировать X системным или же юнит-тестом, используйте системный: контекст - это всё.

  • Разрабатывайте тест более тщательно, чем код.

  • Превратите большинство юнит-тестов в утверждения (assertions).

  • Удалите тесты, которые за год ни разу не падали.

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

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

  • Будьте скромны в отношении способностей тестов. Тесты не улучшают качество: это делают разработчики.

Подробнее..

Перевод 7 способов повысить эффективность автоматизации тестирования в Agile разработке

25.09.2020 20:20:19 | Автор: admin
Перевод статьи подготовлен в преддверии старта курса Java QA Engineer.





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

Когда мы говорим о мобильных и веб-проектах, большинство из которых построено с использованием той или иной формы Agile (или гибкой) методологии разработки, и всех частях жизненного цикла разработки программного обеспечения, будь то разработка, развертывание или даже тестирование все регулируется одними и теми же принципами быстрого вывода на рынок.

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

В каждом Agile-спринте на разработчиках лежить задача создавать некоторое количество новых функций, а тестировщикам же приходится тестировать все приложение. Это подразумевает тестирование не только новых функций, но также и всех старых, чтобы убедиться, что новая итерация разработки не испортила старый продукт.

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

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

Зачем автоматизация Agile разработке?


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

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

Поломанный код из-за частых сборок


Изменения кода в Agile разработке очень распространены, они часто приводят к сбоям в коде, особенно во время интеграции. Непрерывная интеграция и непрерывное развертывание подразумевают непрерывное тестирование. Внедрение автоматизированного тестирования с вашими конвейерами CI/CD позволит легко справиться с этой ситуацией.

Неправильное покрытие тестами


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

Узкие места в производительности


Когда мы пытаемся улучшить функциональность веб-приложения, мы в конечном итоге усложняем его. Кроме того, из-за большого объема кода страдает производительность приложения. Таким образом, инструменты автоматизированного нагрузочного тестирования могут быть полезны при выявлении узких мест (Bottlenecks) при отслеживании производительности вашего веб-приложения.

Недостаточное тестирование API


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

Сложность тестирования на мобильных устройствах


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

Преимущества использования автоматизации тестирования в Agile разработке


Автоматизация тестирования приносит пользу Agile разработке несколькими способами, например, обеспечивает скорость, производительность, надежность, программируемость, возможность повторного использования и многое другое. Некоторые существенные преимущества автоматизированного тестирования в Agile разработке включают в себя:

Ускорение выполнения тестов


Автоматизация тестирования в гибкой методике позволяет реализовать непрерывную интеграцию, что может помочь команде QA быстрее приступать к тестированию.

Гарантированное качество


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

Повторное выполнение


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

Улучшение качества общения и совместной работы


Инструмент автоматизации не только улучшает качество продукта, но также улучшает взаимодействие и сотрудничество между командами, позволяя им делиться друг с другом процессом тестирования.

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

Способы повышения эффективность автоматизации тестирования в Agile разработке


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

1. Проведение параллельного тестирования


Одна из основных причин недостижения желаемых результатов в гибком тестировании с автоматизацией заключается в том, что гибкая разработка это непрерывная доставка с несколькими короткими итерациями в конвейере разработки и развертывания. Из-за этого QA-команды часто проводят короткие и частые спринты регрессионного тестирования. Эти небольшие циклы тестирования означают, что теперь тестировщикам становится сложнее находить, исправлять и тестировать продукты с каждой итерацией.

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

2. Разработка качественных тестов


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

  • Точность.
  • Обслуживаемость.
  • Портативность.
  • Целостность.
  • Версионность.
  • Производительность.


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

3. Интеграция DevOps


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

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

4. Выбирайте инструмент автоматизации с умом


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

Чтобы упростить вам поиск инструментов автоматизации, мы перечислили несколько функций и возможностей, которые должны быть у каждого инструмента автоматизации, независимо от того, какой тип тестирования вы выполняете.

  • Прежде всего, инструмент автоматизации должен быть совместим с ОС, а это означает, что он должен поддерживать почти все операционные системы, которые используются пользователями для доступа к вашему веб-приложению.
  • Обеспечьте поддержку в широком диапазоне браузеров, чтобы вы могли быстро протестировать кроссбраузерность вашего веб-приложения на целевых браузерах.
  • Инструмент должен соответствовать всем требованиям команды тестирования.
  • Он должен иметь удобный пользовательский интерфейс, простую навигацию и функциональность, чтобы даже новички могли легко использовать его для автоматизации тестирования.
  • Должен предлагать интегрированную поддержку с универсальными инструментами, такими как инструменты управления тестированием, инструменты отслеживания ошибок, непрерывное развертывание и т. д.
  • Он входит в ваш бюджет и предоставляет все функции в соответствии с ценой.
  • Может без проблем поддерживать набор регрессионного тестирования.


5. Считайте автоматизацию частью разработки


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

  • Автоматизация не имеет смысла, если вы пытаетесь автоматизировать каждый тест или использовать автоматизацию для каждой поставляемой функции. Это неправильный подход к использованию автоматизации. Фактически, вы должны использовать автоматизацию для отраженных изменений, которые происходят после внедрения новой функции или в случае регрессионного тестирования, когда вам необходимо выполнять тесты повторно.
  • 100% покрытия кода не существует. Программный код никогда не может быть полностью покрыт автоматизированным или ручным тестированием. Даже если вы охватили весь код, это не определяет качество или актуальность тестов. Для повышения качества и производительности выберите индивидуальное транслирование тестов, что доказало свою практическую ценность при автоматическом тестировании. Но он по-прежнему не определяет стопроцентное тестовое покрытие вашего приложения, поскольку вы не можете автоматизировать весь процесс даже при переходе один-к-одному.

Один из лучших подходов к более эффективному автоматическому тестированию в Agile разработке делать тестовые примеры небольшими и компактными. Небольшие тестовые примеры не только легко выполнить, они так же позволяют быстро вносить изменения в соответствии с требованиями регрессии.

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

6. Кроссбраузерное тестирование и кроссплатформенное тестирование с самого старта


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

Обеспечение кроссбраузерной или кроссплатформенной совместимости всего продукта на более поздних этапах является очень ресурсоемкой и трудоемкой затеей. Выполняйте кроссбраузерное тестирование вашего приложения с первого дня.

Если вы скажете, что инвестирование в инфраструктуру тестирования будет очень дорогостоящим для стартапа, я буду первым, кто с вами согласится. Инфраструктура внутреннего тестирования означает, что вам придется купить компьютер Mac, компьютер Windows, устройства Android, устройства iOS и т. д. и т. д. Стоимость достигает тысяч долларов.

Но здесь и проявляется вся сила облака. Вы можете воспользоваться помощью платформ кроссбраузерного тестирования таких как LambdaTest, Testingbot, Browserling и т. д., чтобы ускорить тестирование. Вы получаете огромный список браузеров, в которых вы можете запускать свои тесты, а также масштабируемую облачную инфраструктуру, которую вам не нужно поддерживать, самим масштабировать или развивать.

7. Обеспечьте всестороннюю прозрачность процесса тестирования


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

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

8. Постоянный мониторинг среды разработки


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

Ошибка не всегда должна исходить от приложения; иногда это может происходить из-за любой формы сетевых проблем между устройствами, средой разработки, конфигурациями и т. д.

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

Это всего лишь несколько методов или советов, которым можно следовать, чтобы сделать автоматизацию гибкого тестирования более эффективной. А если вы впервые внедряете автоматическое тестирование в Agile разработку, то вам может помочь облачный пакет тестирования LambdaTest.

Заключение


Agile подходы подталкивают компании к созданию более совершенной среды разработки программного обеспечения. Чтобы идти в ногу с Agile разработкой, необходимы не только знания, но и ресурсы, чтобы следовать последним тенденциям и инновациям в ней, а автоматизированное тестирование одно из основных требований Agile разработки.

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

Подробнее..

Гена против Сандро история автоматизации одной сетевой партии в Героях 3

25.12.2020 08:10:45 | Автор: admin


Сандро не мог поверить своей удаче. Прошло уже две недели после катастрофы с прыжком в экспериментальный портал, который забрал в небытие всю его немаленькую армию. Лишь один захолустный скелет чудом избежал этой же участи.


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


Должно быть, судьба решила вознаградить Сандро за все его страдания, потому что теперь он стоял на окраине двух вкусных, многолюдных деревень и не мог поверить своим глазницам. Если бы у него на черепе осталось бы хоть немного мускулов, он бы даже попробовал улыбнуться. Целых две деревни с крестьянами и никакой охраны, ни одного даже завалявшегося копейщика.


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


Сандро не подозревал, что исход всех его приключений давно предопределён. Вся его история окончится через несколько минут (хоть для него это будет казаться целой неделей) ведь именно столько занимает прогон автотеста кроссплатформенной игровой партии по сети в Героях 3. Действиями Сандро управляет платформа Testo, которая готова прогонять его историю снова и снова.


Будучи разработчиком этой самой платформы Testo, я решил под Новый Год немного повеселиться и соединить своё профессиональное произведение и любимую игру, в которой затерялись тысячи часов моей жизни. И вот что из этого получилось.


Disclaimer

Уважаемые друзья! В этой статье, помимо привычных технических вещей, вы также найдёте историю противостояния некроманта Сандро и никудышного рыцаря Гены в сеттинге нашей любимой и обожаемой игры Герои меча и магии 3. Эта история изобилует большим количеством художественных вымыслов относительно игровых механик этого замечательного произведения игростроения. Пожалуйста, не стоит относиться к этим вымыслам слишком серьёзно: я точно так же, как и вы, люблю и ценю Героев 3, и лишь предлагаю взглянуть на привычные всем вещи с новой, забавной стороны. Любые совпадения случайны, я отказываюсь от любых намёков на какие-либо реальные вещи в реальном мире.


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


Всего ещё год назад Гена был самым несчастным рыцарем во всей Эрэфии. После выпуска из рыцарского военного ВУЗа его по распределению отправили служить на передовую. У Гены никогда не было склонности к военному искусству (он с трудом мог отличить обычного грифона от королевского), но всегда был талант сомелье. Он и поступил-то в военный ВУЗ только по указке своих родителей рыцарей в пятидесятом поколении. Они же помогли замять резонансное дело, когда Гена умудрился пропить всех вверенных ему грифонов в одной из таверн. О военной карьере, понятное дело, речи больше идти не могло, и Гену вместо виселицы отправили от греха подальше дослуживать свой рыцарский контракт в самые дальние далёкие тылы, которые только были в Эрэфии охранять замок Балашов и его окрестности.


Сложно в это поверить, но на новом месте Гена освоился лучше некуда: всего за месяц он познакомился со всеми местными малочисленными гарнизонами (благо его талант сомелье к этому очень располагал, как и десятки бочек вина в погребах замка) и узнал у местных патрулей, что защищать Балашов ему, фактически, не от кого: последнее враждебное существо (волка) в этих краях видели аж 2 года назад.


Вся культурная жизнь окрестностей Балашова крутилась вокруг вечного противостояния двух местных деревень: Вилларибо и Виллабаджо, которое длилось вот уже двадцать лет. Их борьба неизменно выступала источником ставок и единственным развлечением для местного населения. Солдатам было запрещено приближаться к этим деревням после того случая, когда один гарнизон решил сжульничать на ставках и притащил из крепости катапульту в Виларибо, чтобы местные крестьяне могли дальше закидывать тухлые тыквы в сторону Виллабаджо.


Всё это мало беспокоило Геннадия, ведь пол года назад он обнаружил гостеприимное логово феечек совсем недалеко от крепости. В этом заведении он чувствовал себя по настоящему счастливым: в дали от всяких проблем, в окружении бочек вина и очаровательных феечек, он наконец-то чувствовал, что нашел применение всем своим природным талантам. В последний раз в замке Гена был месяц назад. Забрав из погреба аж шесть бочек с вином и убедившись, что в окрестностях всё также ничего не происходит, он укатил обратно к своей сладкой жизни. Жизни, которую так ненавидел Сандро, старательно собирая по всем округам ресурсы для своего преобразователя скелетов.


Начальные условия


Для запуска Героев я решил использовать проект VCMI. В этом замечательном открытом проекте все ключевые компоненты Героев 3 были с нуля переписаны на С++. Проект является кроссплатформенным, поэтому с его помощью вы можете играть в Героев по сети на Макбуке с другом, который едет в метро и сидит в своём телефоне на Андроиде. Как раз-таки этой кроссплатформенностью я и воспользовался для этой статьи.


Похождения Сандро и Гены происходят вот на такой карте, которую я накидал в редакторе за 15 минут (все ссылки доступны в конце статьи):



Гена находится в синем углу ринга, отдыхая в гостях у феечек, тогда как Сандро в красном углу только что закончил строительство простенькой базы и готов отправиться на сбор крестьян.



Игра происходит по сети в режиме "человек против человека". Сандро выбрал Ubuntu Desktop 20.04 в качестве машины для запуска, а Гена решил не рисковать и придерживаться проверенного варианта с Windows 7. Сандро на правах более опытного пользователя создаёт конференцию и выступает в качестве сервера, а Гене лишь нужно подключиться к Сандро по сети.


За развёртывание, настройку стенда, установку VCMI и прогон всей игровой партии отвечает платформа Testo. Возможно, вы уже видели на Хабре краткое описание этой платформы, или примеры автотестов для антивируса Dr. Web (без малейшего доступа к исходникам). Помимо тестов, Testo можно применять и для автоматического развёртывания интересных виртуальных стендов (например, контроллер домена AD вместе с рабочей станцией).


Краткое описание Testo

Testo это новый фреймворк для системных (End-To-End) автотестов, который позволяет автоматизировать взаимодействие с виртуальными машинами с помощью скриптов на специальном, простом для понимания языке. Testo построен на распознавании объектов на экране с помощью нейросетей, поэтому для его работы не требуется устанавливать специальные агенты внутрь виртуальных машин.


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



Благодаря механизму кеширования в Testo, все эти тесты вовсе не нужно будет прогонять с нуля каждый раз. Поэтому самые долгие и неинтересные тесты *install и *configure (установка и первичная настройка ОС) прогонятся вовсе только один раз, после чего вечно будут закешированы. Разбирать эти тесты я не буду, но желающие смогут найти исходники этих тестов в ссылках в конце статьи.


С остальными тестами и интересными моментами из них мы познакомимся чуть позднее, а сейчас самое время узнать, как продвигаются дела у Сандро.




Вилларибо небрежно прочитал Сандро, входя под покровом ночи в первую деревню. Несмотря на поздний час, в ближайшем к Сандро доме горел свет. Открыв калитку, Сандро услышал громкое тявканье из будки, но достаточно было одного взгляда в ту сторону, чтобы тявканье сменилось жалобным воем, а затем и вовсе стихло.


Сам дом оказался тоже незапертым. Не то, чтобы это могло стать препятствием, конечно же. Но так эффект от бесшумного появления Сандро в комнате с кучей крестьян оказался максимально удовлетворительным для него. Двадцать с лишним пар глаз мгновенно уставились на него. Весёлые разговоры мгновенно смолкли, оставив лишь гнетущую тишину. Сандро любил гнетущую тишину как и всё гнетущее. Прошептав своим замогильным голосом Вы пойдёте со мной, Сандро развернулся и неспешно вышел обратно во двор. Он знал, что ни один тщедушный смертный не посмеет ему перечить, и что эти крестьяне теперь в его власти до самой своей смерти. Да и после смерти тоже. Особенно после смерти.


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


Установка VCMI


Похоже, дела у Сандро идут вполне неплохо.


Что ж, первые тесты прошли (где-то за кадром), пришло время заняться установкой VCMI. За это отвечают два теста: ubuntu_install_vcmi и win7_install_vcmi. В них происходит установка самого дистрибутива VCMI и копируются почти все необходимые файлы с оригинальными данными Героев 3 в папки VCMI (Чтобы VCMI мог их подхватить). Точнее, копируются все файлы, кроме папки Maps (в которой лежит только одна карта, скриншот которой вы видели выше). Эта папка будет скопирована чуть позже. Вот так выглядит этот процесс на примере Ubuntu:



Уважаемые читатели, автор в курсе про правило не выкладывать код скриншотами, но тесты написаны на языке Testo-lang, и Хабр не поддерживает подсветку этого языка. Я считаю, что лучше уж выложить скриншот, зато код не будет сливаться в одно месиво. Ссылки на все тесты доступны в конце статьи, вы всегда можете увидеть их текстовое представление там.

Пробежимся вкратце по тесту:


  1. Сам VCMI устанавливается из репозитория ppa:vcmi/ppa.
  2. Нужно один раз запустить VCMI Launcher, чтобы он создал свои служебные папки в ~/.local/share.
  3. Копируем с хоста папки Data, Mp3, Mods/vcmi в служебные папки VCMI. Папки Data и Mp3 содержат файлы с данными оригинальных Героев. Папка Mods/vcmi содержит мод vcmi, который позволяет устанавливать нормальное разрешение экрана.
  4. Копируем файл vcmi_settings.json с настройками VCMI, чтобы не нужно было прокликивать настройки с помощью мышки.

А что же с папкой Maps? Почему бы её тоже сразу не скопировать? Не буду вас перегружать сейчас деталями этого решения, вы сами всё увидите ближе к концу статьи.


Для Windows процесс выглядит примерно так же (разве что дистрибутив копируется с хоста, а не из Интернета), останавливаться отдельно не буду.




Некромант? Здесь, в Балашове? Что вообще происходит? эти и многие другие мысли наперебой мелькали в голове у Гены, пробиваясь сквозь пелену худшего похмелья в его не такой уж и долгой жизни.


Он сидел в комнате для совещаний, закрыв лицо руками, и не верил своим ушам. Помимо него в комнате находилось ещё двое: крестьянин из Вилларибо и единственный штатный монах во всём Балашове по имени Станислав. Станислав, в отличие от Гены, был отправлен в Балашов на пенсию, а не чтобы не отсвечивать. Навоевался монах в свои годы, и просто хотел на старости лет отдохнуть.


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


Откуда он тут взялся? это оказался единственный вопрос, который был способен сгенерировать Генин разум в этой ситуации.


Я не знаю, да это и неважно. Важно лишь то, что все крестьяне из обоих деревень скоро станут скелетами, если уже не стали. Это больше 400 скелетов. С этой армией он наверняка захочет захватить наш замок, Станислав был мрачен, но непоколебим.


И что нам теперь делать?


Нам нечем защищаться. У нас нет войск. Все, кто могут держать оружие, слишком далеко они не успеют вернуться в замок до атаки некроманта.


Так и как же быть?


Достраивать защитные и тренировочные сооружения тоже не получится уж лучше стать скелетом, чем потом объясняться с руководством почему мы всё это построили без согласования.


И какие у нас варианты?


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


Так каков в итоге план?


Мы построим гильдию магов первого уровня и выдадим тебе книгу заклинаний. С этой книгой ты пойдёшь на срочные курсы повышения магической квалификации. За пару дней тебе придётся стать мало-мальски достойным магом. Пока ты будешь просвещаться, мы достроим гильдию магов до приемлемого уровня. Если нам повезёт с заклинаниями, то у нас будет шанс.


Замыленные месяцем возлияний мозговые шестерёнки в голове у Гены наконец-то со скрипом прокрутились, и он с ужасом осознал: ему придётся изучать магию. МАГИЮ! Магию изучают ЗУБРИЛ! Сам же Гена всегда был свято уверен, что жизнь слишком коротка, чтобы тратить её на изучение каких-то ветхих непонятных книг.


Гена был потрясён и обескуражен до глубины души. В мгновение ока он забыл про некроманта, про орды скелетов и про залежи вина в погребах. Заикаясь, Гена выдавил:


Я, я не могу изучать м-магию. Мне нельзя магию! Мне нельзя доверять книгу заклинаний, я даже грифонов в таверне умудрился пропить! Нам лучше сдать замок и эвакуироваться!


Мы не можем сдать замок, потому что идти нам некуда. Вокруг глушь на сотни километров. Некромант нас нагонит и тогда шансов у нас никаких. К тому же магия это не так сложно, как кажется. Сегодня отдыхай и приведи себя в порядок, а завтра с утра сразу же отправляйся к Игорю он живёт недалеко от замка. Передашь ему письмо от меня, он введёт тебя в курс дела.


Запуск сетевой игры


За запуск сетевой игры отвечает тест launch_game. Это первый тест, куда "сходятся" отдельные ветки Ubuntu и Windows 7. Это и понятно, ведь для запуска сетевой игры требуется наличие в тесте обоих машин (ранее мы могли всё делать по отдельности). Ubuntu (Сандро) выступает в качестве сервера, а Windows (Гена) в качестве клиента. Так что тест выглядит следующим образом:


1) Копируем папку Maps с хоста на Ubuntu и запускаем VCMI. Создаём новую конференцию для игры по сети.


Сниппет


2) Копируем папку Maps с хоста на Windows 7 и запускаем VCMI. Подключаемся к конференции по IP-адресу.


Сниппет


3) Каждый игрок выбирает себе начальный бонус, после чего игра запускается.


Сниппет


Можно заметить, что этот тест выглядит как простое сочетание кликов по тексту и по изображениям (все изображения я заранее вырезал и упаковал в файлы, путь к которым теперь указывается в действиях mouse click).


Но дальше начинается сама игра. И если вы думаете, что вся партия будет выглядеть как простой набор прокликиваний по куче картинок, то вы заблуждаетесь. Нет, здесь начинается самая интересная часть нашей статьи. Благодаря небольшому тюнингу языка и специальным нейросетям я смог научить платформу Testo неплохо ориентироваться в мире иконок и моделей Героев 3.




Просто поставь подпись в ведомости вот здесь, пробормотал Игорь, теперь давай сюда свою книгу, сейчас мы её разблокируем


Гена молча протянул свою свежую и абсолютно пустую книгу заклинаний. В гильдии магов ему пришлось отдать за неё 500 золотых и даже проставили какие-то картинки в неё, но он не имел ни малейшего понятия, как ей пользоваться.


Игорь достал из кармана какую-то печать и проштамповал все страницы книги, после чего протянул книгу обратно со словами Ну всё, теперь можешь использовать заклинания третьего уровня.


В смысле могу?! Я же ничего не знаю! Я не умею в МАГИЮ!


Конечно можешь. Я же только что проштамповал книгу печатью базовой мудрости.


Гена просто молчал.


Ты что, вообще не знаешь, как работает магия?


Больше молчания.


Вот ё-моё, понабирают рыцарей по объявлению. Давай объясню. Это твоя личная книга заклинаний. В неё можно заносить заклинания, но для это требуются специальные инструменты, которые есть только в гильдиях магов, поэтому новые заклинания в книгу можно заносить только там. Вот, тебе уже даже какие-то заклинания сюда проставили.


По умолчанию в книгу можно проставлять только слабенькие заклинания, 1-2 уровней. Чтобы заносить заклинания помощнее, нужно разблокировать книгу специальными печатями. Станислав попросил меня проставить тебе печать базовой мудрости, так что вот теперь и заклинания третьего уровня можно заносить.


А ещё есть печати, которые усиливают заклинания определённой школы. Например, у тебя есть заклинание Замедления. Станислав тут в письме просит, чтобы я тебе проставил печать экспертного знания магии земли. Но я этим не занимаюсь, это тебе дальше по дороге надо пройти.


Гена никогда ещё не получал так много информации в такие сжатые сроки. Но он знал, что ради родного Балашова (и родных феечек) он должен стать лучше! Он должен попробовать вникнуть!


А пользоваться то этой книгой как?


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


На слове малограмотные Гена непроизвольно сглотнул слюну. Слово аутентификация он решил просто проигнорировать.


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


Гена сомневался, что он хоть один вопрос сможет осилить, но решил снова промолчать.


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


Гене нравилось слово шпаргалка, но не нравилось слово зубрить. Зубрить шпаргалки это же почти и есть Учиться! Но выбора у Гены не было, так что он спрятал в рюкзак свою книгу заклинаний и поплёлся проставлять оставшиеся печати.


Похождения на карте и не только



Начинается игра, и возникает вопрос: а как автоматизировать действия героев на карте? Сначала я пошёл довольно топорным путём: каждый пункт назначения Сандро и Гены был оформлен в виде изображения-шаблона, который затем передавался на вход действию mouse click img. То же самое касалось любых других действий: нажатие иконок, постройка зданий и так далее. То есть я применял довольно прямолинейный (и распространённый) способ управлять действиями на экране виртуалки поиск изображения по образцу.


А потом количество заготовок стало зашкаливать, да и не все они хорошо работали: например, при попытке кликнуть по нужной звёздной оси поиск образа не всегда отрабатывал как нужно, ведь в этом объекте присутствует анимация:



Так что я подумал, а почему бы не сделать отдельную нейросеть для работы исключительно с объектами в Героях 3? За счёт того, что этой нейросети придётся работать с ограниченным набором объектов, она сможет довольно качественно их распознавать, даже несмотря на анимацию, вариативность цветов и так далее.


А чтобы этой нейросетью можно было удобно пользоваться, я решил добавить новую возможность в язык Testo-lang. Теперь действия wait и mouse click помимо текста и картинок поддерживают возможность ожидания и кликов по объектам Героев!


Вот так выглядил один из фрагментов теста до преобразований:



А вот так после:



Стало ли удобнее? На первый взгляд может показаться, что разница минимальна, но это не так: за каждой картинкой в img скрывается ручная работа: требуется сделать скриншот виртуальной машины и вырезать оттуда требуемый шаблон. Поиск с помощью homm3 работает на основе нейросетей, так что никаких действий с моей стороны не требуется только пользуйся. К тому же нейросети берут на себя заботы по возможной анимированности искомых объектов и помогут ещё в ряде случаев (подробности будут дальше).


Уважаемые читатели! Функционал платформы Testo по поддержке работы с объектами Героев 3 я выполнил в режиме Proof Of Concept и исключительно just for fun. Этот функционал очень сильно недоделан и не подходит для полноценного использования с Героями 3. В конце статьи доступна экспериментальная сборка Testo, на которой вы сможете запустить примеры, но доделывать полноценный вариант я не решился из-за сомнительной ценности и больших трудозатрат.

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




Сандро скомандовал последней порции крестьян пройти обряд улучшения (как он это называл). Люди, один за одним, отправились в сторону преобразователя скелетов. В их глазах была вселенская тоска от понимания своей участи (в конце концов, все они видели, что происходит с их предшественниками), но никто не посмел возразить властному некроманту.


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


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


Обучение геройской сети


Итак, мы решили создать нейросеть, чтобы облегчить себе прокликивание различных объектов. С чего же нам начать? Создание любой нейросети начинается с данных для обучения. В нашем случае мы решаем задачу, которая называется object detection. Соответственно, для обучения нам понадобятся скриншоты Героев, для каждого скриншота должен быть список объектов, которые изображены на нём, их классы (например, "герой" или "город") и их координаты (так называемый bounding box, сокращённо bbox). Причем данных нам понадобится много: гигабайты, а ещё лучше десятки гигабайт. Как же нам создать такой большой датасет? Давайте рассмотрим основные варианты:


  1. Можно вручную наснимать 100 тысяч скриншотов Героев и так же вручную их разметить. По понятным причинам этот вариант отметаем сразу.
  2. Мы можем воспользоваться тем фактом, что VCMI движок перед отрисовкой очередного кадра и так знает, где какие объекты находятся. То есть теоретически мы можем подправить исходники VCMI таким образом, чтобы он нагенерил нам большое количество скриншотов и сохранил информацию об объектах куда-нибудь в файл, например. Исходники VCMI довольно хорошего качества, однако реализовать такой план не так-то просто, потому что логика отрисовки объектов тесно переплетена с механикой игры. Нельзя просто так взять и сказать "отрисуй мне вот эту карту". К сожалению, придётся отказаться от этого плана из-за слишком больших трудозатрат.
  3. Есть ещё один компромиссный вариант, который подходит для большинства задач класса object detection. Идея заключается в том, чтобы вручную разметить относительно небольшое количество скриншотов (допустим 100 штук), а затем разнообразить их путём отрисовки в случайных местах тех объектов, которые мы собирается потом детектить. Разумеется, дополнительно можно применить и другие способы искажения изображений: кадрирование, масштабирование, отражение слева-направо, изменения контраста и яркости, инверсия цветов и т.д. Этот вариант вполне рабочий, а главное его можно реализовать всего за пару дней.

Сказано сделано. Разметить 100 скриншотов не представляет никакой сложности, особенно, если использовать подходящий инструмент разметки. Можно использовать любой из свободных инструментов, например LabelImg. Но я использую собственный интрумент разметки на базе Electron и KonvaJS. Использование своего инструмента удобнее с той точки зрения, что его легче затачивать именно под решение своих задач.



Итак, с разметкой разобрались, теперь нужно определиться с тем, какие объекты мы будем рисовать поверх скриншотов. Я решил, что нейросети стоит применять только там, где плохо подходит стандартный и уже встроенный в Testo детект изображений (wait img). Поэтому для определения зоны ответственности для новой нейросети я руководствовался следующими принципами:


  1. В первую очередь, конечно, это анимированные объекты, потому что иначе действие wait img просто не будет работать. К таким объектам относятся, например, анимированные иконки построек в меню города.
  2. Объекты, у которых может быть разный фон (иначе понадобится вырезать новую картинку на каждый новый фон). Пример такого объекта иконка героя или любого другого объекта на карте.
  3. Объекты, у которых может быть много вариаций. Например кнопка ОК. У неё в игре есть где-то 5-6 разных вариантов отрисовки. Вырезать 5-6 картинок это не проблема, но каждый раз подбирать, какая же картинка лучше подходит в данном конкретном случае нет никакого желания.

Для того, чтобы извлечь оригиналы иконок объектов с альфа каналом я использовал проект lodextract. На вход ему подаются .lod файлы из оригинальной версии игры, а на выходе он выдаёт неимоверно большое количество .png картинок (десятки тысяч), например, вот таких:



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



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


Процесс искажения изображений я разделяю на два этапа. Наиболее сложные операции, в том числе отрисовка объектов поверх скриншота, я делаю до обучения нейросети. Результат этого этапа сохраняется на диск, получается около 100Гб данных. Приятным бонусом в такой схеме является то, что можно любым удобным способом открывать файлы на диске и проверять, что данные сгенерировались правильно. Второй этам происходит "на лету" уже во время обучения сети. Сюда входят простые операции, такие как изменение контраста или инверсия цветов.


Таким образом мы плавно подошли к обучению нейросети. Для начала нам нужно определиться с её архитектурой. Я выбрал yolov3-tiny, но на самом деле это не так уж важно. Для нашей очень простой задачи, впринципе, подойдёт любая архитектура, выполняющая детект объектов. Гораздо бОльшее значение здесь имеет датасет. Нейросети имеют удивительное свойство "выдавать результат" несмотря на их небольшой размер или неоптимальную архитектуру. Главное не давать сети зацикливаться на косвенных признаках объектов. Например, нейросеть может подумать, что коричневые пиксели это учёный (scholar), если на скриншотах в датасете больше не встречается объектов коричневого цвета. Поэтому так важно иметь большой и качественный датасет. А архитектура нейросети это второстепенный вопрос.


Я не буду останавливаться детально на архитекруте Yolo, тем более, что есть подробная статья на эту тему. Обратим внимание только на входные и выходные данные.



На вход сети подаётся набор картинок, а на выходе для каждой картинки мы получаем по несколько тысяч векторов (для больших картинок, типа FullHD). Это одна из особенностей работы нейросетей: мы в какой-то степени использум брутфорс, нежели какую-ту изощренную логику. Каждый вектор состоит из следующих значений:


  • вероятность, что этот вектор описывает какой-то объект на картинке (objectness score). Для большинства векторов это значение будет около нуля.
  • координаты центра объекта (x, y), а также его ширина и высота (w, h)
  • набор значений, описывающих вероятности того, что этот объект принадлежит к каждому из классов (class scores)

То есть выход нейросети требует ещё некоторой постобработки. Нужно отфильтровать вектора с низким objectness score, а также объединить вектора, которые указывают на один и тот же объект. Эти операции не добавляются в модель нейросети, потому что они плохо подходят под архитектуру GPU (у нас получится размерность выходных данных всё время разная). К этому вопросу мы вернёмся чуть позже, а пока предлагаю мельком взглянуть на код нейросети, написанный с ипользованием фреймвока PyTorch:


Код модели
import torchimport torch.nn as nnimport torch.nn.init as initimport torch.nn.functional as Ffrom dataset import classes_namesdef Conv(in_ch, out_ch, kernel_size, activation='leaky'):    seq = nn.Sequential(        nn.Conv2d(in_ch, out_ch, kernel_size=kernel_size, padding=kernel_size//2),        nn.BatchNorm2d(out_ch),    )    if activation == 'leaky':        seq.add_module("2", nn.LeakyReLU(0.1))    elif activation == 'linear':        pass    else:        raise "Unknown activation"    return seqdef MaxPool(kernel_size, stride):    if kernel_size == 2 and stride == 1:        return nn.Sequential(            nn.ZeroPad2d((0, 1, 0, 1)),            nn.MaxPool2d(kernel_size, stride)        )    else:        return nn.MaxPool2d(kernel_size, stride)# Псевдо-слой сети, служит для конкатенации выходов из нескольких других слоёв class Route(nn.Module):    def __init__(self, layers_indexes):        super().__init__()        self.layers_indexes = layers_indexesclass Upsample(nn.Module):    def __init__(self, scale_factor, mode="nearest"):        super().__init__()        self.scale_factor = scale_factor        self.mode = mode    def forward(self, x):        x = F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode)        return xnum_classes = len(classes_names)# Псевдо-слой сети, служит для нормализации выходов предыдущего слоя и # преобразования x,y,w,h в пикселиclass Yolo(nn.Module):    def __init__(self, anchors):        super().__init__()        self.anchors = anchors        self.mse_loss = nn.MSELoss()        self.bce_loss = nn.BCELoss()        self.obj_scale = 1        self.noobj_scale = 100    # Вычесление intersection over union двух bbox-ов    def bbox_wh_iou(self, w1, h1, w2, h2):        inter_area = torch.min(w1, w2) * torch.min(h1, h2)        union_area = w1 * h1 + w2 * h2 - inter_area        return inter_area / (union_area + 1e-16)    def forward(self, x, img_w, img_h):        B, C, H, W = x.shape        num_anchors = len(self.anchors)        prediction = x.view(B, num_anchors, num_classes + 5, H, W) \            .permute(0, 1, 3, 4, 2) \            .contiguous() # преобразуем к формату (B, num_anchors, H, W, num_classes + 5)        # размеры одной ячейки (ширина и высота) в пикселях        stride_x = img_w / W        stride_y = img_h / H        # номера столбцов и строк        grid_x = torch.arange(W).repeat(H, 1).view([1, 1, H, W]).to(x.device)        grid_y = torch.arange(H).repeat(W, 1).t().view([1, 1, H, W]).to(x.device)        # размемы якорей (ширина и высота) в пикселях         anchor_w = x.new_tensor([anchor[0] for anchor in self.anchors]).view((1, num_anchors, 1, 1))        anchor_h = x.new_tensor([anchor[1] for anchor in self.anchors]).view((1, num_anchors, 1, 1))        # преобразуем x,y,w,h в пиксели        pred_x = (prediction[..., 0].sigmoid() + grid_x) * stride_x        pred_y = (prediction[..., 1].sigmoid() + grid_y) * stride_y        pred_w = prediction[..., 2].exp() * anchor_w        pred_h = prediction[..., 3].exp() * anchor_h        # приводим вероятности к диапазону [0, 1]        pred_conf = prediction[..., 4].sigmoid()        pred_cls = prediction[..., 5:].sigmoid()        # из матрицы делаем список        # это позволит объединить вместе выходы Yolo разных размеров        return torch.cat(            (                pred_x.view(B, -1, 1),                pred_y.view(B, -1, 1),                pred_w.view(B, -1, 1),                pred_h.view(B, -1, 1),                pred_conf.view(B, -1, 1),                pred_cls.view(B, -1, num_classes),            ),            -1,        )class Model(nn.Module):    def __init__(self):        super().__init__()        # размеры якорей        anchors_1 = [(81,82), (135,169), (344,319)]        anchors_2 = [(10,14), (23,27), (37,58)]        # список всех слоёв сети        self.module_list = nn.ModuleList([            Conv(3, 16, 3),            MaxPool(2, 2),            Conv(16, 32, 3),            MaxPool(2, 2),            Conv(32, 64, 3),            MaxPool(2, 2),            Conv(64, 128, 3),            MaxPool(2, 2),            Conv(128, 256, 3),            MaxPool(2, 2),            Conv(256, 512, 3),            MaxPool(2, 1),            Conv(512, 1024, 3),            #############            Conv(1024, 256, 1),            Conv(256, 512, 3),            Conv(512, (num_classes + 5) * len(anchors_1), 1, activation='linear'),            Yolo(anchors_1),            Route([-4]),            Conv(256, 128, 1),            Upsample(2),            Route([-1, 8]),            Conv(128 + 256, 256, 3),            Conv(256, (num_classes + 5) * len(anchors_2), 1, activation='linear'),            Yolo(anchors_2)        ])    def forward(self, img):        layer_outputs = []        yolo_outputs = []        x = img        # просто применяем очередной слой к выходу от предыдущего слоя        # (или объединяем выходы нескольких слоёв в случае с Route)         for module in self.module_list:            if isinstance(module, Route):                x = torch.cat([layer_outputs[i] for i in module.layers_indexes], 1)            elif isinstance(module, Yolo):                x = module(x, img.shape[3], img.shape[2])                yolo_outputs.append(x)            else:                x = module(x)            layer_outputs.append(x)        return torch.cat(yolo_outputs, 1)

У нас получилась модель, которая на диске занимает примерно 35Мб. На самом деле это перебор для такой простой залачи, как детект объектов в Героях. Я уверен, что можно безболезненно уменьшить размер модели где-то до 5Мб, при этом не потеряв в точности детекта. Но на это нет времени, история Гены и Сандро ждёт своего продолжения. Двигаемся дальше.


Имея датасет и код нейросети, обучить её не составяет труда. Запускаем обучение и уходим погулять часа на 3.



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


Сеть обучена, замечательно. Однако возникает резонный вопрос, как это дело интегрировать в конечный продукт (если он не на питоне)? Самый простой, наверное, способ это слинковаться с libpytorch. Более того, мы могли бы вместо того, чтобы писать код нейросети на питоне написать его сразу на С++ и даже получить некоторый прирост производительности, благо PyTorch предоставляет C++ frontend. Однако, не очень то хочется тащить за собой весь PyTorch, ведь он даже в заархивированном виде весит целый гигабайт. Поэтому я использую OnnxRuntime. Он позволяет сократить размер дистрибутива в два раза, а также увеличить производительность работы нейросетей. Как следует из названия, этот проект позволяет загружать обученные модели нейросетей в формате onnx и запускать их, так что нам для начала нужно экспортировать нашу модель в этот формат:


model = Model()model.load_state_dict(torch.load("path_to_model.pt", map_location=torch.device('cpu')))model.eval()x = torch.randn(1, 3, 480, 640)output = model(x)torch.onnx.export(model, x, "model.onnx",    input_names=["input"],    output_names=["output"],    dynamic_axes={        'input': {            2: 'height',            3: 'width'        },        'output': {            2: 'height',            3: 'width'        }    },    opset_version=11)

Я указал здесь параметр dynamic_axes, это позволит подавать на вход сети картинки любого размера. Вообще, с экспортом в формат onnx нужно быть очень осторожным. Когда мы пишем код модели на питоне то мы используем привычные нам циклы, условия и переменные. А формат onnx описывает граф с вершинами и ребрами. Сконвертировать одно в другое это совершенно нетривиальная задача. Убедиться в том, что экспорт прошел успешно, можно с помощью просмотрщика формата onnx, например с помощью Netron. Но скорее всего, если что-то пойдёт не так, PyTorch выдаст предупреждение. Экспортировав модель в формат onnx, мы можем загрузить её из С++/C#/Java/NodeJS. Ниже пример для питона:


import onnxruntimeimport numpyort_session = onnxruntime.InferenceSession("model.onnx")x = numpy.rand(1, 3, 480, 640)ort_inputs = {"input": x}ort_outs = ort_session.run(None, ort_inputs)

Вот здесь как раз можно выполнить постобработку результатов работы нейросети. Векторы с низким objectness score просто отбрасываем, а к остальным применяем алгоритм Non-Maximum Suppression. Давайте, наконец, запустим нашу свежеобученную нейросеть и посмотрим, как она работает:



Я обучал нейросеть детектить только те объекты, которые мне нужны для теста, поэтому на скриншоте выше подсвечены не все объекты. Ура! Вроде всё работает, а значит мы можем автоматизировать похождения Гены и Сандро по карте:



Битва


Гена стоял на стене замка и в тайне надеялся, что он до сих пор находится в страшном алкогольном сне. За свою недолгую военную карьеру Гена так и не успел повидать не то, что настоящего некроманта, но даже простого скелета. Сейчас же скелетов перед ним было столько, что они даже не помещались в одно поле зрения.


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


Перед боем Станислав рассказал Гене, как нужно использовать магию: сначала необходимо притормозить скелетов с помощью замедления (и не забывать следить за тем, чтобы эффект заклинания не кончился), а затем использовать заклинание Уничтожить нежить до тех пор, пока Гене будет хватать знаний. Знаний у Гены конечно прибавилось после всех шпаргалок с ответами, которые он добывал всю неделю. К тому же перед боем он хлебнул воды из какого-то магического фонтана, после чего у него на удивление сильно обострилась память, так что ответы из шпаргалок прочно сидели у него в голове.


Но скелетов было так много, что даже обостренный ум Гены мог не выдержать. Всё могло решиться в самых мелочах.




Сандро был уверен в своих силах, как никогда. Он чувствовал, что гарнизон города состоит всего из нескольких фей, которые ни в коей мере не смогут оказать никакого сопротивления его армии мертвецов. Пора было начинать бой. Сандро скомандовал катапульте открыть огонь по стенам замка.


Ну вот мы и добрались до кульминации эпического противостояния молодого безалаберного рыцаря и умудрённого тысячелетиями некроманта.



Битва будет протекать по такому сценарию:


  1. Сандро будет атаковать на автопилоте.
  2. Гена кастует экспертное замедление и перемещает феечек наверх карты.
  3. Пока Сандро медленно продвигает своих скелетов вперёд, Гена кастует "Уничтожение нежити".
  4. Через 5 ходов Гена обновляет замедление и перемещает феечек в низ карты.
  5. Гена продолжает кастовать "Уничтожение нежити" пока у него не кончится мана или пока не кончатся скелеты.

Тест может закончиться тремя исходами:


  1. Бой заканчивается поражением Гены меньше, чем за 13 ходов провал.
  2. Бой длится 13 ходов и больше провал.
  3. Гена побеждает быстрее, чем за 13 ходов успех.

Вот так это выглядит в виде теста:



Часть действий я инкапсулировал в виде макросов cast_slow_routine и blue_battle_routine, чтобы тест не выглядел слишком загромождённым.


Исход битвы, пожалуй, не буду спойлерить лучше посмотрите сами.



Гена с ужасом смотрел в книгу заклинаний. До сих пор ему удавалось отвечать на все всплывающие вопросы, и ряды скелетов уменьшались прямо на глазах. Гена уже предвкушал свою первую в жизни победу, да ещё какую! Воистину эпическую! Гена уже начал задумываться о том, какую вечеринку он закатит после этой победы, какая слава его ждёт. Его не смущали ни разрушенные стены замка, ни скелеты, которые уже были внутри города. Ещё буквально одно заклинание и всё будет кончено. Так он думал пол минуты назад.


Сколько раз за один ход королевский грифон может ответить на атаку противника? гласила надпись на книге. Это был первый вопрос, которого не было в шпаргалках Гены.


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


Гена стал судорожно подбирать правильный ответ. Книга допускала 2 неверные попытки, после чего заклинание блокировалось на какое-то время. У Гены этого времени не было: оставшиеся скелеты уже ломились к нему на стену замка. От фей тоже было мало проку: они забились в угол и ждали, когда Гена, наконец, прочтёт завершающее заклинание.


Варианты 2 и 3 раза не понравились книге заклинаний.




Гена стоял со связанными руками перед преобразователем скелетов и думал, что же пошло не так. Как победа умудрилась выскользнуть из его рук? Если бы только он был немного умнее Если бы он хоть немного слушал на лекциях своих преподавателей, которые не раз и не два рассказывали про этих проклятых грифонов Было ли бы этого достаточно чтобы победить злого Сандро? Эх, чего уж теперь гадать, пора шагать в преобразователь.


А где же хеппи энд?


Действительно, не можем же мы завершить такую эпичную сагу на такой негативной ноте. Новый год же! Как мы увидели, Гена вполне себе уверенно шёл к успеху, пока у него неожиданно закончилась мана. Может быть, надо просто сделать Гену изначально несколько "умнее"? Так давайте! Откроем редактор карт и добавим Гене немного "знаний":



Ну а теперь я наконец то могу ответить на вопрос, почему же я в тестах копирую папку Maps не в тесте install_vcmi, а в тесте launch_game. Как только мы поменяли файл Villaribo_and_Villabadjo_lose.h3m, Testo сбросит кеш того теста, где этот файл задействован (т.е. launch_game). Потерявший кеш тест (и его потомки) запустится заново. Если бы я копировал Maps в тестах install_vcmi, то именно эти тесты потеряли бы кеш. А значит, пришлось бы заново прогонять установку vcmi и копирование других папок (Data, Mp3 и прочее).


Я просто построил дерево тестов таким образом, чтобы редактирование карты не запускало повторную установку vcmi.



А вот и хэппи энд подъехал! Оказывается, Гене всего лишь нужно было лучше слушать преподавателей в военном ВУЗе!


Заключение


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


Но в каждой шутке есть доля шутки, как говорится. Тестирование игр это всегда очень сложная и болезненная тема для разговоров, а про автотесты сетевых игр и вовсе говорить не приходится. В этой статье я хотел показать, что не всё так плохо, и что Testo вполне может справиться с такой задачей, пусть и при необходимости некоторых доработок.


Репозиторий со всеми необходимыми сценариями и артефактами (в том числе с экспериментальной сборкой Testo) можно найти вот тут:


https://github.com/testo-lang/testo-articles/tree/master/HOMM3

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru