Сквозное тестирование с Joomla! и Cypress — мои первые шаги и мысли

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

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

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

вступление

«Хотя это правда, что качество нельзя проверить, столь же очевидно, что без тестирования невозможно разработать что-либо качественное» - [Джеймс А. Уиттакер]

До того, как я впервые столкнулся с Cypress, я и представить себе не мог, что барьеры, часто мешавшие моему тестированию, на самом деле будут несколько отодвинуты в сторону. которые возникли из-за отсутствия тестирования!Теперь я убежден, что тесты, которые:

  • максимально приближенный по времени к программированию,
  • автоматически,
  • часто - в идеале после каждого изменения программы

    вносить больше, чем они стоят И более того: тестирование может быть даже забавным.

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

Кому следует прочитать этот текст?

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

Немного теории

Волшебный треугольник

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

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

 

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

Наоборот, экономия затрат приведет к снижению качества и/или задержке завершения.

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

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

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

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

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

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

Технический долг

Затраты и выгоды

В литературе вы найдете разгромные статистические данные о шансах на успех программных проектов.Мало что изменилось в той негативной картине, которая уже была зафиксирована в исследовании А. В. Фейла в 1990-х годах. , было определено отклонение стоимости по сравнению с первоначальным планом: 70% проектов показали отклонение стоимости не менее 50%!

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

Чем больше я получаю опыта, тем больше прихожу к выводу, что крайние взгляды не есть хорошо.Решение почти всегда находится посередине.Также избегайте крайностей в программных проектах и ​​ищите золотую середину.Не обязательно иметь 100% план. Но вы также не должны наивно начинать новый проект. Хотя управление программным проектом и особенно оценка стоимости являются важной темой, я не буду больше утомлять вас этим в этом тексте. Цель этой статьи — показать, как E2E тестирование может быть интегрировано в практический рабочий процесс разработки программного обеспечения.

Интегрируйте тестирование программного обеспечения в свой рабочий процесс

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

Относительные затраты на устранение неполадок на разных этапах проекта

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

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

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

Непрерывная интеграция (CI)
Непрерывная интеграция тестов

Представьте себе следующий сценарий: вот-вот выйдет новая версия популярной системы управления контентом. Все, что разработчики внесли в команду с момента последнего релиза, теперь впервые используется вместе. Напряжение растет! работа? Будут ли все тесты успешными - если проект вообще будет интегрировать тесты. Или релиз новой версии придется снова откладывать и впереди ждут нервные часы исправления ошибок? Кстати, перенос даты релиза тоже не хорошо для имиджа программного продукта!Ни один разработчик не любит испытывать такой сценарий.Гораздо лучше знать в любой момент, в каком состоянии находится программный проект в данный момент?Код, не вписывающийся в существующий, следует интегрировать только после они были «сделаны по размеру».Особенно во времена, когда все чаще и чаще приходится устранять пробелы в безопасности, проект всегда должен иметь возможность создать выпуск!И здесь в игру вступает непрерывная интеграция.

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

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

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

Разработка через тестирование (TDD)

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

Тесты TDD помогут вам правильно написать программу .

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

Если вы пройдете часть упражнений в конце текста, вы можете попробовать это. Сначала создайте тест, а затем напишите код для ядра Joomla. Затем отправьте все вместе в виде PR на Github. Если бы все делали это , Joomla будет иметь идеальное тестовое покрытие.

Развитие, управляемое поведением (BDD)

BDD — это не очередная техника программирования или методика тестирования, а своего рода передовая практика разработки программного обеспечения.BDD идеально используется вместе с TDD.В принципе, Behaviour-Driven-Development означает тестирование не реализации программного кода, а выполнения - т. е. поведение программы Тест проверяет, выполняется ли спецификация, т. е. требование заказчика.

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

Что я имею в виду под этим: "Напишите правильную программу"? Бывает, что пользователи видят вещи иначе, чем разработчики. Примером может служить рабочий процесс удаления статьи в Joomla. Снова и снова я встречаю пользователей, которые нажимают на значок статуса в корзина и удивляются. Пользователь обычно интуитивно предполагает, что элемент теперь удален безвозвратно, но он переключается с корзины на активацию. Для разработчика нажатие на значок статуса - это изменение статуса, переключение во всех остальных представлениях. Почему в корзине должно быть иначе?Для разработчика функция реализована без ошибок.Джумла работает корректно.Но на мой взгляд функция не та в том месте,потому что большинство пользователей описывали/запрашивали ее совсем по другому .

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

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

Проект Joomla представил BDD в проекте Google Summer of Code . Была надежда, что пользователи, не имеющие знаний в области программирования, смогут более легко участвовать, используя Gherkin . Этот подход не применялся последовательно. В то время Joomla использовала Codeception в качестве Инструмент тестирования С Cypress разработка BDD также возможна для разработки BDD.

Планирование

Типы тестов
  • Модульные тесты: модульный тест — это тест, который независимо тестирует наименьшие программные модули.
  • Интеграционные тесты. Интеграционный тест — это тест, который проверяет взаимодействие отдельных модулей.
  • E2E-тесты или приемочные тесты: приемочный тест проверяет, выполняет ли программа задачу, определенную в начале.
Стратегии

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

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

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

Обычный подход к созданию тестов сегодня — снизу. Если вы предпочитаете разработку программного обеспечения, основанную на поведении, вам следует использовать противоположную стратегию. Вам следует использовать стратегию «сверху вниз». на этапе проектирования.

Стратегии тестирования: тестирование сверху вниз и тестирование снизу вверх.

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

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

Тестовая пирамида Майка Кона

Сколько тестов какого типа должно быть реализовано?Тестовая пирамида Майка Кона описывает концепцию использования автоматизированных тестов программного обеспечения.Пирамида состоит из трех уровней, структурированных в соответствии с частотой использования и релевантностью.‍

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

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

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

Требования

Какое оборудование вам понадобится для работы над следующей практической частью?

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

Какими знаниями должны обладать лично вы?

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

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

Настройка

Настройка Cypress с Joomla!

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

Шаги по настройке локальной среды:

Клонируйте репозиторий в корень вашего локального веб-сервера:

$ git clone https://github.com/joomla/joomla-cms.git

Перейдите в папку joomla-cms:

$ cd joomla-cms

Согласно дорожной карте Joomla, следующая основная версия 5.0 будет выпущена в октябре 2023 года. Чтобы быть в курсе, я использую эту версию разработки здесь.

Переходим на ветку 5.0-dev :

$ git checkout 5.0-dev

Установите все необходимые пакеты композитора:

$ composer install

Установите все необходимые пакеты npm:

$ npm install

Дополнительную информацию и помощь по настройке рабочей станции см. в статье документации Joomla "Настройка рабочей станции для разработки Joomla" . Для Cypress информация есть на сайте cypress.io . Но на данном этапе в этом нет необходимости. Joomla все настраивает самостоятельно. Вам просто нужно настроить свои индивидуальные данные через файл конфигурации joomla-cms/cypress.config.js.

Настройте свои индивидуальные данные.Для этого вы можете использовать шаблон joomla-cms/cypress.config.dist.jsв качестве ориентации.В моем случае этот файл выглядит так:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  fixturesFolder: 'tests/cypress/fixtures',
  videosFolder: 'tests/cypress/output/videos',
  screenshotsFolder: 'tests/cypress/output/screenshots',
  viewportHeight: 1000,
  viewportWidth: 1200,
  e2e: {
    setupNodeEvents(on, config) {},
    baseUrl: 'http://localhost/joomla-cms',
    specPattern: [
      'tests/cypress/integration/install/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/administrator/**/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/site/**/*.cy.{js,jsx,ts,tsx}'
    ],
    supportFile: 'tests/cypress/support/index.js',
    scrollBehavior: 'center',
    browser: 'firefox',
    screenshotOnRunFailure: true,
    video: false
  },
  env: {
    sitename: 'Joomla CMS Test',
    name: 'admin',
    email: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.',
    username: 'admin',
    password: 'adminadminadmin',
    db_type: 'MySQLi',
    db_host: 'mysql',
    db_name: 'test_joomla',
    db_user: 'root',
    db_password: 'root',
    db_prefix: 'j4_',
  },
})

Конкретно я добавил каталог tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}в specPattern массив, потому что хочу сохранить тест для модулей там позже.Затем я изменил имя пользователя и пароли, потому что я также хочу протестировать установку вручную и лучше запомнить самоназначенные.Я использую контейнер Docker как базу данных.Поэтому я изменил сервер базы данных и данные доступа.И, наконец, мне пришлось установить корневой URL-адрес http://localhost/joomla-cmsмоей установки Joomla.

Используйте кипарис

Через веб-браузер

Позвоните npm run cypress:openчерез CLI в корневой каталог Joomla. Через некоторое время откроется приложение Cypress. Ранее мы создали файл. То, joomla-cms/cypress.config.dist.jsчто это обнаружено, видно из того факта, что тестирование E2E указано как настроенное.

Приложение Cypress открывается после вызова 96;npm run cypress:open96;.

Здесь вы можете выбрать, хотите ли вы запускать тесты E2E и какой браузер вы хотите использовать.Для примера я выбрал вариант «Начать тестирование в Firefox».

Тесты E2E в приложении Cypress: выберите браузер для использования.

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

Набор тестов Joomla в Firefox через приложение Cypress.

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

Выполняется тест установки Joomla.

Попробуйте, если вы используете as, db_host: 'localhost',вы можете протестировать установку и, таким образом, правильно настроить Joomla для работы над следующей частью этого текста.

Если вы, как и я, используете внешний источник (не lcoalhost, я использую docker-контейнер) как db_host, тест для такого рода установки еще не готов, В этом случае возникает вопрос безопасности в процедуре установки, которая еще не рассматривался в тестах. В этом случае установите Joomla вручную с информацией, введенной в файл joomla-cms/cypress.config.js. Следующие тесты будут использовать настройки из этого файла конфигурации, например, для входа в область администрирования Joomla. Таким образом, разработчик теста делает не нужно заботиться о вводе данных для входа. Соответствующие имя пользователя и пароль всегда используются автоматически из файла конфигурации.

Обезглавленный

По умолчанию cypress runзапускает все тесты без головы.Следующая команда выполняет все уже закодированные тесты и сохраняет скриншоты в каталоге /joomla-cms/tests/cypress/output/screenshotsв случае ошибки.Выходной каталог был задан в cypress.config.jsфайле.

$ npm run cypress:run

Другие команды CLI

Есть и другие полезные команды, которые не реализованы в виде скриптов в package.jsonпроекте Joomla, я их запускаю через npx [docs.npmjs.com/commands/npx].

кипарис проверить

Команда cypress verifyпроверяет правильность установки Cypress и возможность его запуска.

$ npx cypress verify

✔  Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
Информация о кипарисе

Команда cypress infoвыводит информацию о Cypress и текущей среде.

$ npx cypress info
Displaying Cypress info...

Detected 2 browsers installed:

1. Chromium
  - Name: chromium
  - Channel: stable
  - Version: 113.0.5672.126
  - Executable: chromium
  - Profile: /.../snap/chromium/current

2. Firefox
  - Name: firefox
  - Channel: stable
  - Version: 113.0.1
  - Executable: firefox
  - Profile: /.../snap/firefox/current/Cypress/firefox-stable

Note: to run these browsers, pass : to the '--browser' field

Examples:
- cypress run --browser chromium
- cypress run --browser firefox

Learn More: https://on.cypress.io/launching-browsers

Proxy Settings: none detected
Environment Variables: none detected

Application Data: /.../.config/cypress/cy/development
Browser Profiles: /.../.config/cypress/cy/development/browsers
Binary Caches: /.../.cache/Cypress

Cypress Version: 12.8.1 (stable)
System Platform: linux (Ubuntu - 22.04)
System Memory: 4.08 GB free 788 MB
кипарисовая версия

Команда cypress versionвыводит установленную двоичную версию Cypress, версию пакета Cypress, версию Electron, используемую для создания Cypress, и версию объединенного узла.

$ npx cypress version
Cypress package version: 12.8.1
Cypress binary version: 12.8.1
Electron version: 21.0.0
Bundled Node version: 16.16.0

Документация Cypress содержит более подробную информацию.

Написание первого собственного теста

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

Получить обзор

Обучение на уже разработанных тестах

В разрабатываемой версии CMS Joomla уже есть тесты Cypress, они находятся в папке /tests/System/integrationТе, кто любит учиться на примерах, найдут здесь подходящее введение.

Импорт кода для повторяющихся задач

Разработчики Joomla работают над проектом NodeJs joomla-cypress , который предоставляет тестовый код для общих тестовых случаев, которые импортируются во время установки версии CMS для разработчиков с npm installиспользованием

  • package.jsonи через
  • файл поддержки /tests/System/support/index.jsФайл поддержки определяется в конфигурации cypress.config.js.
// package.json
{
  "name": "joomla",
  "version": "5.0.0",
  "description": "Joomla CMS",
  "license": "GPL-2.0-or-later",
  "repository": {
    "type": "git",
    "url": "https://github.com/joomla/joomla-cms.git"
  },
...
  "devDependencies": {
    ...
    "joomla-cypress": "^0.0.16",
    ...
  }
}

Примером может служить нажатие кнопки на панели инструментов. Например, Cypress.Commands.add('clickToolbarButton', clickToolbarButton)команда clickToolbarButton()становится доступной в пользовательских тестах, а нажатие cy.clickToolbarButton('new')кнопки Newимитируется. Код, необходимый для этого, показан в фрагменте кода ниже.

// node_modules/joomla-cypress/src/common.js
...
const clickToolbarButton = (button, subselector = null) => {
  cy.log('**Click on a toolbar button**')
  cy.log('Button: ' + button)
  cy.log('Subselector: ' + subselector)

  switch (button.toLowerCase())
  {
    case "new":
      cy.get("#toolbar-new").click()
      break
    case "publish":
      cy.get("#status-group-children-publish").click()
      break
    case "unpublish":
      cy.get("#status-group-children-unpublish").click()
      break
    case "archive":
      cy.get("#status-group-children-archive").click();
      break
    case "check-in":
      cy.get("#status-group-children-checkin").click()
      break
    case "batch":
      cy.get("#status-group-children-batch").click()
      break
    case "rebuild":
      cy.get('#toolbar-refresh button').click()
      break
    case "trash":
      cy.get("#status-group-children-trash").click()
      break
    case "save":
      cy.get("#toolbar-apply").click()
      break
    case "save & close":
      cy.get(".button-save").contains('Save & Close').click()
      break
    case "save & new":
      cy.get("#save-group-children-save-new").click()
      break
    case "cancel":
      cy.get("#toolbar-cancel").click()
      break
    case "options":
      cy.get("#toolbar-options").click()
      break
    case "empty trash":
    case "delete":
      cy.get("#toolbar-delete").click()
      break
    case "feature":
      cy.get("#status-group-children-featured").click()
      break
    case "unfeature":
      cy.get("#status-group-children-unfeatured").click()
      break
    case "action":
      cy.get("#toolbar-status-group").click()
      break
    case "transition":
      cy.get(".button-transition.transition-" + subselector).click()
      break
  }

  cy.log('--Click on a toolbar button--')
}

Cypress.Commands.add('clickToolbarButton', clickToolbarButton)
...

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

// /node_modules/joomla-cypress/src/user.js
...
const doAdministratorLogin = (user, password, useSnapshot = true) => {
  cy.log('**Do administrator login**')
  cy.log('User: ' + user)
  cy.log('Password: ' + password)

  cy.visit('administrator/index.php')
  cy.get('#mod-login-username').type(user)
  cy.get('#mod-login-password').type(password)
  cy.get('#btn-login-submit').click()
  cy.get('h1.page-title').should('contain', 'Home Dashboard')

  cy.log('--Do administrator login--')
}

Cypress.Commands.add('doAdministratorLogin', doAdministratorLogin)

...
Общие задачи в индивидуальной среде

В каталоге /tests/System/supportвы найдете общие задачи в индивидуальной среде. Чтобы их можно было легко использовать повторно, они импортируются через файл поддержки /tests/System/support/index.jsПримером часто повторяющейся задачи является вход в административную область, которая обрабатывается в файле /tests/System/support/commands.jsиспользуя функцию doAdministratorLogin.

cypress.config.jsВ следующем коде также показано, как в тестах используется информация из конфигурации, Cypress.env('username')которой присваивается значение свойства usernameвнутри группы env.

Кроме того, здесь мы можем увидеть, как перезаписывать команды.Перезаписывает Cypress.Commands.overwrite('doAdministratorLogin' ...),код, который мы только что видели в пакете.Преимущество joomla-cypressзаключается в том, что пользователь и пароль автоматически используются из индивидуальной конфигурации.

// /tests/System/support/commands.js
...
Cypress.Commands.overwrite('doAdministratorLogin', (originalFn, username, password, useSnapshot = true) => {
  // Ensure there are valid credentials
  const user = username ?? Cypress.env('username');
  const pw = password ?? Cypress.env('password');

  // Do normal login when no snapshot should be used
  if (!useSnapshot) {
    // Clear the session data
    Cypress.session.clearAllSavedSessions();

    // Call the normal function
    return originalFn(user, pw);
  }

  // Do login through the session
  return cy.session([user, pw, 'back'], () => originalFn(user, pw), { cacheAcrossSpecs: true });
});
...

Установить собственное расширение Joomla

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

Установите собственное расширение Joomla.

После установки вы можете найти ссылку на представление компонента Foo в левой боковой панели бэкенда Joomla.

Вид примера компонента в бэкенде Joomla.

Теперь у нас есть настроенная тестовая среда и код для тестирования.

Первый собственный тест

Крючки

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

Cypress предоставляет несколько типов хуков , включая хуки beforeи after, которые запускаются до или после тестов в группе тестов, и beforeEachхуки afterEach, которые запускаются до или после каждого отдельного теста в группе described. Пример кода в файле tests/System/integration/administrator/components/com_foos/FoosList.cy.jsвызывает выполнение входа в систему перед каждым тестом в describedблоке test com_foos features.

Теперь мы начнем с практической части и создадим файл tests/System/integration/administrator/components/com_foos/FoosList.cy.jsсо следующим фрагментом кода перед написанием первого продуктивного теста. Наш первый пример должен успешно войти в бэкэнд перед любым тестом! Мы проверим это после создания первого теста.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

})

Примечание. Хуки, реализованные в файле /tests/System/support/index.js, применяются к каждому тестовому файлу в наборе тестов.

Успешный тест

Компонент, который мы установили для тестирования, содержит три элемента Astridи NinaСначала Elmarмы проверяем, успешно ли были созданы эти элементы.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

  it('list view shows items', function () {
    cy.visit('administrator/index.php?option=com_foos')

    cy.get('main').should('contain.text', 'Astrid')
    cy.get('main').should('contain.text', 'Nina')
    cy.get('main').should('contain.text', 'Elmar')

    cy.checkForPhpNoticesOrWarnings()
  })
})

Примечание. Функция, checkForPhpNoticesOrWarnings()которую вы найдете в файле /node_modules/joomla-cypress/src/support.js.

Мы получаем элемент DOM mainс помощью команды Cypress get

Вы должны найти свой только что созданный тест FooList.cy.jsв списке доступных тестов на левой боковой панели.Если это не так, пожалуйста, закройте браузер и запустите npm run cypress:openснова.

Joomla запускает тест собственного расширения.

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

Вид после успешного выполнения теста.

Неудачный тест

Добавьте строку cy.get('main').should('contain.text', 'Sami')в тестовый файл, чтобы запуск завершился неудачно. Элемента с таким именем нет. После сохранения тестового файла Cypress замечает изменение. После каждого изменения Cypress автоматически перезапускает все тесты в тестовом файле.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

  it('list view shows items', function () {
    cy.visit('administrator/index.php?option=com_foos')

    cy.get('main').should('contain.text', 'Astrid')
    cy.get('main').should('contain.text', 'Nina')
    cy.get('main').should('contain.text', 'Elmar')
    cy.get('main').should('contain.text', 'Sami')

    cy.checkForPhpNoticesOrWarnings()
  })
})

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

Вид после провала теста.

Запуск только одного теста в файле

Наше демонстрационное расширение содержит более одного макета. Добавьте тест для тестирования макета пустого состояния. Поскольку теперь у нас есть два теста в этом файле, Cypress всегда будет запускать оба теста каждый раз, когда мы сохраняем файл. Мы можем использовать так, чтобы только один .only()тест выполняется:

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
    beforeEach(() => {
        cy.doAdministratorLogin()
    })

    it('list view shows items', function () {
        cy.visit('administrator/index.php?option=com_foos')

        cy.get('main').should('contain.text', 'Astrid')
        cy.get('main').should('contain.text', 'Nina')
        cy.get('main').should('contain.text', 'Elmar')

        cy.checkForPhpNoticesOrWarnings()
    })

    it.only('emptystate layout', function () {
        cy.visit('administrator/index.php?option=com_foos&view=foos&layout=emptystate')
        cy.get('main').should('contain.text', 'No Foo have been created yet.')
    })
})

Во время разработки это очень удобно.

Специальные атрибуты теста

Теперь нам нравится тестировать фронтенд для нашего компонента, делаем это в отдельном файле /tests/System/integration/site/components/com_foos/FooItem.cy.js.

Большую часть времени мы используем класс CSS для получения элементов в тестах Joomla. Хотя это вполне допустимо и будет работать, на самом деле это не рекомендуется. Почему бы и нет? Когда вы используете классы CSS или идентификаторы, вы привязываете свои тесты к вещам, которые со временем, скорее всего, изменятся. Классы и идентификаторы предназначены для дизайна, макета и иногда с помощью JavaScript для управления, которые можно легко изменить. Если кто-то изменит имя класса или идентификатор, ваши тесты больше не будут работать. Чтобы сделать ваши тесты менее хрупкими и Cypress рекомендует создавать специальные атрибуты данных для ваших элементов специально для целей тестирования.

Я буду использовать data-testатрибут для элементов. Сначала я добавлю атрибут data-test="foo-main"в производственный код.

// /components/com_foos/tmpl/foo/default.php

\defined('_JEXEC') or die;
?>
<div data-test="foo-main">
Hello Foos
</div>

Затем я тестирую производственный код, ища атрибут [data-test="foo-main"].

// tests/System/integration/site/components/com_foos/FooItem.cy.js
describe('Test com_foo frontend', () => {
  it('Show frondend via query in url', function () {
    cy.visit('index.php?option=com_foos&view=foo')

    cy.get('[data-test="foo-main"]').should('contain.text', 'Hello Foos')

    cy.checkForPhpNoticesOrWarnings()
  })
})
Тестирование пункта меню и некоторые мысли о событиях, ожиданиях и лучших практиках

Теперь мне нравится тестировать создание пункта меню для нашего компонента.Я делаю это в отдельном файле.Этот /tests/System/integration/administrator/components/com_foos/MenuItem.cy.jsкод сложный и показывает много особенностей.

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

const testMenuItem = {
  'title': 'Test MenuItem',
  'menuitemtype_title': 'COM_FOOS',
  'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
}

Далее вы видите весь код файла MenuItem.cy.js:

// tests/System/integration/administrator/components/com_foos/MenuItem.cy.js

describe('Test menu item', () => {
  beforeEach(() => {
    cy.doAdministratorLogin(Cypress.env('username'), Cypress.env('password'))
  })

  it('creates a new menu item', function () {
    const testMenuItem = {
      'title': 'Test MenuItem',
      'menuitemtype_title': 'COM_FOOS',
      'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
    }

    cy.visit('administrator/index.php?option=com_menus&view=item&client_id=0&menutype=mainmenu&layout=edit')
    cy.checkForPhpNoticesOrWarnings()
    cy.get('h1.page-title').should('contain', 'Menus: New Item')

    cy.get('#jform_title').clear().type(testMenuItem.title)

    cy.contains('Select').click()
    cy.get('.iframe').iframe('#collapse1-heading').contains(testMenuItem.menuitemtype_title).click()
    cy.get('.iframe').iframe('#collapse1-heading').contains(testMenuItem.menuitemtype_entry).click()

    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_list')
    cy.clickToolbarButton('Save & Close')
    cy.wait('@item_list')
    cy.get('#system-message-container').contains('Menu item saved.').should('exist')

    // Frontend
    cy.visit('index.php')
    cy.get('.sidebar-right').contains(testMenuItem.title).click()
    cy.get('[data-test="foo-main"]').should('contain.text', 'Hello Foos')
    cy.checkForPhpNoticesOrWarnings()

    // Trash
    cy.visit('administrator/index.php?option=com_menus&view=items&menutype=mainmenu')
    cy.searchForItem(testMenuItem.title)
    cy.checkAllResults()
    cy.clickToolbarButton('Action')
    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_trash')
    cy.clickToolbarButton('trash')
    cy.wait('@item_trash')
    cy.get('#system-message-container').contains('Menu item trashed.').should('exist')

    // Delete
    cy.visit('administrator/index.php?option=com_menus&view=items&menutype=mainmenu')
    cy.setFilter('published', 'Trashed')
    cy.searchForItem(testMenuItem.title)
    cy.checkAllResults()
    cy.on("window:confirm", (s) => {
      return true;
    });
    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_delete')
    cy.clickToolbarButton('empty trash');
    cy.wait('@item_delete')
    cy.get('#system-message-container').contains('Menu item deleted.').should('exist')
  })
})
  • В этом коде вы можете увидеть пример тестирования чего-то и последующего удаления всего - таким образом восстанавливая начальное состояние. Таким образом вы можете повторять тесты столько раз, сколько хотите. Без восстановления начального состояния второй запуск теста завершится неудачей, потому что Joomla не может хранить два одинаковых элемента.

Примечание. Тест должен быть:

  • повторяемый.
  • Конкретно это означает, что он должен тестировать ограниченную проблему, и код для этого не должен быть слишком обширным.
  • независимо от других тестов.
  • И вы можете увидеть, как использовать перехваченный маршрут, определенный с помощью cy.intercept()[^docs.cypress.io/api/commands/intercept] в качестве псевдонима, а затем дождаться маршрута, определенного как псевдоним с помощью cy.wait().

При написании тестов для таких приложений возникает соблазн использовать случайные значения, такие как cy.wait(2000);в cy.waitкоманде. Проблема с таким подходом в том, что хотя это может хорошо работать в разработке. Однако это не гарантирует, что всегда будет работать. Почему? Потому что базовая система зависит от вещей, которые трудно предсказать, поэтому всегда лучше точно определить, чего вы ждете.

  • В коде также показано, как дождаться оповещения и подтвердить его.
cy.on("window:confirm", (s) => {
  return true;
});
  • И последнее, но не менее важное: тестовый код содержит встроенные функции Cypress и типичные для Joomla функции, которые могут быть повторно использованы разработчиками расширений, например, cy.setFilter('published', 'Trashed')или cy.clickToolbarButton('Save & Close')функции, в которых можно найти решения для отдельных тестов в целом и которые часто нужны разработчикам Joomla в частности. .
Смешивание асинхронного и синхронного кода

Команды Cypress асинхронны, то есть они возвращают не значение, а generateего.Когда мы запускаем Cypress, он не выполняет команды сразу, а считывает их последовательно и ставит в очередь.Если вы смешиваете асинхронный и синхронный код в тестах, вы могут получить неожиданные результаты. Если вы запустите следующий код, вы получите ошибку вопреки ожиданиям. Конечно, вы также ожидали, что это mainText = $main.text()изменит значение mainText. Но mainText === 'Initial'в конце все еще действует. Почему? Cypress сначала выполняет синхронный код в в начале и в конце.Только потом вызывает асинхронную часть внутри then().То есть переменная mainTextинициализируется и сразу после этого проверяется, если она изменилась - что конечно не так.

let mainText = 'Initial';
cy.visit('administrator/index.php?option=com_foos&view=foos&layout=emptystate')
cy.get("main").then(
  ($main) => (mainText = $main.text())
);

if (mainText === 'Initial') {
  throw new Error(`Der Text hat sich nicht geändert. Er lautet: ${mainText}`);
}

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

cy.get('main').then(function(e){
  console.log(e.text())
})
console.log('Cypress Test.')
Стабы и шпионы

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

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

Следующий пример показывает a spyи a stubв действии. С помощью const stub = cy.stub()мы создаем stubэлемент и определяем на следующем шаге, что falseвозвращается для первого вызова и trueдля второго. С помощью cy.on('window:confirm', stub)мы используем stubдля использования window:confirm'. На следующем шаге мы создаем cy.spy(win, 'confirm').as('winConfirmSpy')элемент с Spyпомощью , который наблюдает за вызовом 'window:confirm', Теперь мы проверяем, что при первом вызове удаление категории отклоняется, а при втором вызове подтверждается, При этом гарантируется, stubчто мы можем точно ожидать, какие возвращаемые значения будут доставлено, 'window:confirm'инкапсулировано, @winConfirmSpyпомогает убедиться, что функция была действительно вызвана и как часто она вызывалась.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
...
const stub = cy.stub()

stub.onFirstCall().returns(false)
stub.onSecondCall().returns(true)

cy.on('window:confirm', stub)

cy.window().then(win => {
  cy.spy(win, 'confirm').as('winConfirmSpy')
})

cy.intercept('index.php?option=com_categories&view=categories&extension=com_foos').as('cat_delete')
cy.clickToolbarButton('empty trash');

cy.get('@winConfirmSpy').should('be.calledOnce')
cy.get('main').should('contain.text', testFoo.category)


cy.clickToolbarButton('empty trash');
cy.wait('@cat_delete')

cy.get('@winConfirmSpy').should('be.calledTwice')

cy.get('#system-message-container').contains('Category deleted.').should('exist')
...

Если нужно просто установить фиксированное значение для 'window:confirm'вызова, следующий код выполнит эту работу.

cy.on("window:confirm", (s) => {
  return true;
});

заключение

В этой статье вы познакомились с базовой теорией и практическими особенностями E2E-тестирования с помощью Cypress. Я использовал установку Joomla, чтобы продемонстрировать, как писать различные тесты, чтобы убедиться, что компонент Joomla на веб-сайте работает должным образом. Я также показал, как настроить Cypress. Test Runner в файле cypress.json и как использовать настраиваемые команды Cypress.Это было сделано с использованием простых примеров.

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

An online collaborative community manual for Joomla! users, developers or anyone interested in learning more about Joomla! Currently, we have more than 9,000 articles written, maintained, and translated by our Joomla! community members. 

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