Практика автоматизированного тестирования программы WeChat Mini

Апплет WeChat тестовое задание

Автор статьи: IMWeb Команда IMWeb Источник исходного текста:Сообщество IMWebВоспроизведение запрещено без согласия

1. Origin — зачем вам автоматизировать тестирование небольших программ

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

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

Может ли эта повторяющаяся работа выполняться программой автоматически?

В веб-разработке было много автоматизированных решений для таких задач тестирования, таких как Selenium и Puppeteer, Идеи в основном одни и те же, все они позволяют браузеру автоматически выполнять клики, ввод и другие операции на странице в указанном месте. порядок, а затем отображать страницу после операции.Тестовый вывод (утверждение) получается путем сравнения его с желаемым результатом. Есть ли в апплете решение, которое может автоматизировать операции и предоставлять информацию о странице для утверждений в соответствии с этой идеей? Ради безопасности, лежащей в основе WeChat, среда мини-программ была относительно закрытой, оставляя мало места для работы разработчиков, а автоматизированные операции практически невозможно реализовать, но она появилась в конце мая.miniprogram-automatorИнструменты, дающие надежду разработчикам небольших программ.

Во-вторых, судьба - начальная тестовая минипрограмма-автомат

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

  • MiniProgram: получение информации об апплете (стек страниц, системная информация, содержимое страницы), управление апплетом (переход по страницам, переключение вкладок, вызов методов).
  • Page: Получить информацию о странице (путь, элемент, данные, структура), страница управления (набор данных рендеринга, метод вызова)
  • Element: Получить информацию об элементе (атрибут, стиль, содержимое, положение), управлять элементом (щелчок, длительное нажатие, метод вызова).

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

Не много ерунды, прочитайте документ и используйте его:

Ø Вызовите командную строку инструмента разработчика, чтобы открыть проект, и укажите порт службы автоматизации.

PS D:\programs\内测\微信web开发者工具> ./cli.bat --auto D:\weApp\testMiniprogram --auto-port 9420
Initializing...
idePortFile: C:\Users\billcui\AppData\Local\微信开发者工具\User Data\Default\.ide
starting ide...
IDE server has started, listening on http://127.0.0.1:35510
initialization finished
Open project with automation enabled success D:\weApp\testMiniprogram

Эта линия команды должна обратить внимание на:

  1. Документация требует, чтобы номер версии инструмента разработчика был выше, чем v1.02.1906042, предпочтительно последняя внутренняя бета-версия инструмента.Я успешно запустил его в v1.03.1906062;
  2. Перед запуском этой команды вам нужно открыть меню инструментов разработчикаНастройки -> Настройки безопасности -> Сервисный порт;
  3. Порт автоматизации не зависит от служебного порта (например, 35510, напечатанный терминалом, на самом деле является служебным портом), вы должны увидетьOpen project with automation enabled success D:\weApp\testMiniprogramЭта строка сообщает об успешном открытии порта автоматизации (9420).

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

Ø npm i miniprogram-automator --save-devУстановите SDK, создайте test.js, внедрите в код инструмент минипрограммы-автоматизатора и подключите порт автоматизации

const automator = require('miniprogram-automator');

const miniProgram = automator.connect({
  wsEndpoint: 'ws://localhost:9420',
})

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

const automator = require('miniprogram-automator');

const miniProgram = automator.connect({
  wsEndpoint: 'ws://localhost:9420',
}).then(async miniProgram => {
  // 从首页重启
  const page = await miniProgram.reLaunch('/pages/index/index');
  // 从页面获取bottom-button组件
  const button = await page.$('bottom-button');
  // 打印出button的wxml信息
  console.log(await button.wxml());
}).catch(e => {
  console.log('catch a error', e);
});

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

// index.spec.js
const automator = require('miniprogram-automator');

describe('课堂小程序自动化测试', () => {
  let miniProgram;
  // 运行测试前调用
  beforeAll(async () => {
    miniProgram = await automator.connect({
      wsEndpoint: 'ws://localhost:9420',
    });
  });
  // 运行测试后调用
  afterAll(() => {
    miniProgram.disconnect();
  });
  // 测试内容
  it('nohost检测', async () => {
    const page = await miniProgram.reLaunch('/pages/index/index');
    const nohostButton = await page.$('nohost');
    expect(nohostButton).toBeNull();
  });
});

бегатьjest index.spec.js, если компонент nohost не существует на странице, тест проходит, и результат такой, как показано на рисунке:

3. Yuanju-Применение автоматизированного тестирования в классном апплете WeChat

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

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

Ниже приводится страница сведений о курсе апплета WeChat Tencent Classroom в качестве примера, чтобы подробно объяснить, как реализовать автоматическое тестирование в реальных проектах:

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

если дляНеприобретенная страница сведений о платном курсе без рекламных акцийЦели теста следующие:

  1. На кнопке должно быть написано «Купить сейчас». Нажмите кнопку «Купить», чтобы перейти на страницу оплаты.
  2. Нажмите кнопку пробной версии, чтобы воспроизвести пробное видео в обычном режиме.
  3. Нажатие на видео курса не может воспроизводиться, если курс не был куплен

Для реализации этого теста вx.spec.jsВ файле сначала нужно внедрить минипрограмму-автомататор по вышеописанным шагам, и подключить проект апплета WeChat, открывший порт автоматизации в beforeAll. (Код повторяться здесь не будет, см. предыдущую главу) Давайте посмотрим непосредственно на код тестового контента.

  1. Отображение кнопки и проверка страницы перехода по клику
   // 打开页面,通过url传参
   const page = await miniProgram.reLaunch(`/pages/course/course?cid=${commonPayCid}`);
   // 获取按钮组件信息
   const basicApplyButton = await page.$('.basic--buy');
   // 判断按钮显示内容
   expect(await basicApplyButton.wxml()).toContain('立即购买'); 
   // 模拟点击按钮
   await basicApplyButton.tap();
   // 等待页面跳转
   await page.waitFor(1500);
   // 获取当前页面路径
   const currentPage = await miniProgram.currentPage();
   // 判断跳转后路径是否正确
   expect(currentPage.path).toContain('pages/order/order');
   // 跳转回来
   await miniProgram.navigateBack();

В настоящее время минипрограмма-автоматизатор предоставляет два метода получения компонентов на странице:page.$()а такжеpage.?()

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

Нижняя кнопка страницы сведений о курсе на самом деле является настраиваемым компонентом, а также содержит вложенные настраиваемые компоненты.Давайте посмотрим на структуру wxml нижней кнопки:

Красная коробка - это цель, которую вы хотите получить, попробуйте пройти ее напрямую.page.$('.bottom-btn')илиpage.$('.buy')Все возвращаемые значения не определены, так как же их получить? Давайте сначала посмотрим, как выглядит внутренняя часть нижней кнопки.

const basicApplyButton = await page.$('bottom-button');
console.log(await basicApplyButton.wxml());

Получите нижнюю кнопку и напечатайте ее строку wxml, посмотрите:

// 输出实际上是字符串,为了方便显示格式化了一下
<view class="bottom-button--bottom-button-space" wx:nodeid="17">
    <view class="bottom-button--bottom-button-wrapper" wx:nodeid="261">
        <basic is="components/discount-button/components/basic/basic" wx:nodeid="262">
            <view wx:nodeid="263">
                <view class="basic--bottom-button-container" wx:nodeid="264">
                    <view class="basic--bottom-btn basic--buy" wx:nodeid="265">立即购买</view>
                </view>
            </view>
        </basic>
    </view>
</view>

Что-то нашел! Когда апплет фактически запущен, имя класса внутри пользовательского компонента имеет префикс с именем компонента, попробуйте еще раз.page.$('.basic--buy')Было обнаружено, что он был успешно получен, поэтому, хотя на поверхности минипрограмма-автомат может работать только и получать контент на странице, внутренняя структура пользовательского компонента на самом деле существует в той или иной форме на странице.

Далее смотрим на прыжок, можно напрямую получить соответствующий компонент и вызвать его.tap()Метод для моделирования щелчка. Следует отметить, что, поскольку для открытия новой страницы требуется много времени, нажав на инструмент разработчика WECHAT Applet, вам нужно пока ждать, пока страница загружается, в противном случае страница не имеет Подпрыгнул, когда текущий путь страницы достигается дальше. Не удается получить новый путь страницы. Время ожидания может быть дано немного большее и более безопасное значение, основанное на опыте.

  1. Нажмите кнопку пробной версии, чтобы воспроизвести пробное видео в обычном режиме.
const player_video = await tapTcplayer(page, '.player-task');
expect(await player_video.wxml()).toContain('video-current-time'); // 试学

Из-за ограничений инструментов разработчика WeChat, «облако по запросу» будет понижено до воспроизведения tcplayer.Основные компоненты внутри tcplayer на самом деле<video>Компоненты, структура wxml выглядит следующим образом:

Как узнать, успешно ли воспроизведено видео?

Давайте сначала получим строку wxml успешно воспроизведенного видеокомпонента в соответствии с описанным выше методом.

   "<video class="component-video-video--player_video" controls="" danmu-list="[]" initial-time="0" object-fit="contain" poster="https://10.url.cn/qqc..." src="http://113.96.98.148/vedu.tc.qq.com/AtmkzyWCuq..." autoplay="" wx:nodeid="446"><div class="video-container" wx:nodeid="447"><div class="video-bar full" style="opacity: 1;" wx:nodeid="457"><div class="video-controls" wx:nodeid="458"><div class="video-control-button pause" wx:nodeid="459"><div parse-text-content="" class="video-current-time" wx:nodeid="460">00:02<div class="video-progress-container" wx:nodeid="462"><div class="video-progress" wx:nodeid="463"><div style="left: -21px;" class="video-ball" wx:nodeid="464"><div class="video-inner" wx:nodeid="465"><div parse-text-content="" class="video-duration" wx:nodeid="466">06:09<div class="video-fullscreen" wx:nodeid="468"><div style="z-index: -9999" class="video-danmu" wx:nodeid="453"></video>"

Потрясенный! Родной<video>Внутри компонента находится<div>, мы также можем заметить, что ключевой класс: video-current-time имеет внутреннее значение 00:02, разве это не текущий ход воспроизведения? Его можно просто использовать, чтобы судить, было ли видео воспроизведено успешно или нет, вот и все!

Сравнение показало, что при сбое воспроизведения div с классом video-current-time вообще не появлялся, поэтому он напрямую оценивался по тому, содержит ли он video-current-time.

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

При нажатии на не пробный урок видео не воспроизводится. Поскольку на странице отображается только обложка, когда видео не воспроизводится, прикрепить<video>компонента, поэтому оценка toBeNull() может быть выполнена непосредственно с результатом получения видеокомпонента. Объедините весь приведенный выше код следующим образом:

async function tapTcplayer(page, className = '.task-item') {
     const taskItem = await page.$(className);
     await taskItem.tap();
     await page.waitFor(3000);
     const playercover = await page.$('.player-cover');
     const player_video = await playercover.$('.component-video-video--player_video');
     return player_video;
   }
   it('付费课程详情页按钮显示、跳转、点播、试学功能测试', async () => {
       const page = await miniProgram.reLaunch(`/pages/course/course?cid=${commonPayCid}`);
       const basicApplyButton = await page.$('.basic--buy');
       expect(await basicApplyButton.wxml()).toContain('立即购买'); // 按钮显示
       await basicApplyButton.tap();
       await page.waitFor(1500);
       const currentPage = await miniProgram.currentPage();
       expect(currentPage.path).toContain('pages/order/order');
       await miniProgram.navigateBack();
       const player_video = await tapTcplayer(page);
       expect(player_video).toBeNull(); // 未报名不能播放视频
       const player_video_new = await tapTcplayer(page, '.player-task');
       expect(await player_video_new.wxml()).toContain('current'); // 试学
     }, 20000);

Видно, что сначала тестировалась функция воспроизведения курса, а затем тестировалась функция пробного обучения.Почему?

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

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

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

Результат запуска тестового скрипта следующий:

На данный момент реализованы следующие тестовые функции:

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

Завершение функциональных тестов в контрольном списке выглядит следующим образом: показатель завершения составляет 65%.

точка обзора автоматизированный тест Примечание
Удалять ли плагин nohost служба поддержки
Отображается ли домашняя страница нормально служба поддержки
Вход в апплет домашней страницы ПК нормальный? еще нет Авторизация информации не может быть выполнена автоматически
Платежная способность Android нормальная? еще нет Не удается получить информацию внутри веб-просмотра
Отображается ли страница категории нормально служба поддержки
Вы можете войти в систему в обычном режиме? еще нет Авторизация информации не может быть выполнена автоматически
Отображается ли учебная программа нормально, и отображается ли нормально ход обучения/статус прямой трансляции служба поддержки быть улучшенным
Может ли страница сведений о курсе отображаться нормально служба поддержки
Сканировать код/поделиться - это обычный апплет вызова еще нет Инструменты разработчика не поддерживаются
Может ли прямая трансляция платного занятия нормально воспроизводиться (Shangyun и Tencent Video) еще нет Инструменты разработчика не поддерживают прямую трансляцию
Можно ли нормально воспроизводить прямую трансляцию бесплатного класса (Shangyun и Tencent Video) еще нет Инструменты разработчика не поддерживают прямую трансляцию
Можно ли нормально воспроизводить запись бесплатного занятия (Shangyun и Tencent Video) Частичная поддержка Понизить уровень инструментов разработчика до tcplayer
Можно ли нормально воспроизводить запись и трансляцию платных занятий (Shangyun и Tencent Video) Частичная поддержка Понизить уровень инструментов разработчика до tcplayer
Можно ли нормально играть в пробное задание служба поддержки
Воспроизводится ли видео на странице сведений нормально служба поддержки
Отображение, связанное с маркетинговыми инструментами, нормальное? служба поддержки
Может ли логика платежа быть завершена нормально еще нет Не удается получить информацию внутри веб-просмотра
Фильтр категорий нормальный? служба поддержки быть улучшенным
Можно ли его нормально искать и список нормально отображается служба поддержки быть улучшенным
Сохраняется ли локальное время загрузки в пределах 1 с служба поддержки

4. Продолжение судьбы — обнаруженные проблемы и функциональные ограничения

  1. Для получения компонентов на странице можно использовать толькоpage.$()илиpage.?()метод, проверенный селектор поддерживает только имя компонента и имя класса. Внутренние элементы пользовательского компонента нельзя получить напрямую, и перед именем класса необходимо добавить префикс. На страницах реального проекта используется большое количество кастомных компонентов, что очень неудобно судить о внутренней структуре кастомных компонентов.wxml()Метод печатает внутреннюю структуру пользовательского компонента, чтобы подтвердить фактическое состояние внутренних подкомпонентов. И не может вызывать методы внутри пользовательских компонентов.
  2. Функция моментального снимка Jest — очень хороший способ тестирования компонентов или страниц с относительно фиксированной структурой, но здесь есть подводные камни. Содержимое снимка для сравнения в апплете обычно представляет собой строку, напечатанную методом wxml компонента, но фактический результат, возвращаемый методом wxml, может быть другим во время выполнения.Компонент может быть автоматически добавлен с атрибутом wx:nodeid, но иногда он возвращает символ. Если он не добавлен в строку, это приведет к сбою проверки моментального снимка.
  3. В настоящее время его можно протестировать только в среде инструментов разработчика, поэтому функция прямой трансляции не может быть протестирована, а облачный VOD будет автоматически понижен до Tencent Video VOD, а прямая трансляция не может быть протестирована. (После того, как инструмент будет обновлен для поддержки отладки реальной машины, он должен быть улучшен)
  4. Такие функции, как вход в систему и сканирование кода, не могут быть протестированы, поскольку автоматизированные инструменты управления не могут сканировать и нажимать всплывающее окно авторизации.
  5. <web-view>Компоненты не получают никакой внутренней информации и не могут быть автоматизированы.

Надеюсь, что эти проблемы будут решены в будущем~~