Как написать лучшие тесты для операций перетаскивания в браузере

1656556579 kak napisat luchshie testy dlya operaczij peretaskivaniya v brauzere

Рональд Рэй

Сохраняя его агностику фреймворка

1*0QbvXqleQASAZ0oaAfY66w
Фото Эша Эдмондса на Unsplash

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

Отчасти это связано с тем, насколько сломанным и непоследовательным является HTML5 Drag and Drop API. Это заставило многих авторов библиотек создать собственные уникальные подходы к проблеме, часто очень отличные друг от друга. Это значит, что реализовать только такую ​​функциональность в вашей программе может быть достаточно сложно, а для неопытного разработчика может быть еще сложнее написать соответствующие автоматизированные тесты.

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

— Петро-Поль Кох

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

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

Начальный подход

Ладно, скажем, мне нужно создать таблицу, которая имеет строки, которые можно перетаскивать, выглядеть примерно так (кстати, не отвлекайтесь на реализацию):

1*U6CwIMHmeOqoMJxOZke4sQ

Как видите, я использую классику react-dnd библиотека известного ныне Дана Абрамова. Функция готова, и как мы будем тестировать ее на этом этапе? Если вы заглянете в документацию, вы найдете аккуратный раздел «Тестирование», который, вероятно, заставит глаза сиять.

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

Так что в последнем предложении предыдущего абзаца я бросил вам много странных понятий: декорированный компонент, бэкенд, тестовый сервер, бэкенд HTML5… что? Это все внутренние основные понятия react-dnd и dnd-core, все связано с тем, как это работает под капотом. Само связанное руководство признает это и утверждает, что из-за этого наименее документирована часть библиотеки.

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

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

  1. Чтобы проверить эту функцию, я должен знать, как эта библиотека, в частности, работает внутри и детали ее реализации.
  2. Чтобы проверить эту функцию, я также должен быть знаком с тем, как работает тестирование бэкенда, что мне не обязательно знать, чтобы создать функцию перетаскивания с помощью этой библиотеки. Это означает, что у меня есть еще один набор документации для использования, а также целое другое измерение проблем, с которыми я мог бы столкнуться, и которые не обязательно будут совместно использоваться с обычным сервером HTML5, который я использовал бы для своей программы.
  3. Тот факт, что у меня есть комплексный набор тестов прохождения, который использует этот подход, не обязательно гарантирует мне, что он действительно работает так, как я ожидаю с точки зрения пользователя. Подумайте: в моих тестах и ​​дикой природе функциональность будет работать с совершенно другими внутренними элементами. И несмотря на самые лучшие намерения сопровождающих, этот подход не обязательно хорошо масштабируется для остальной экосистемы JS и может дать вам ложное чувство безопасности.
  4. Если я когда-нибудь решу изменить подход к функциональным возможностям и вместо этого использовать другую библиотеку или написать ее сам, все мои тесты вдруг устареют, и мне придется их переписывать заново.

Теперь, не поймите меня неправильно – прекрасно, что они приложили все усилия, чтобы создать «тестовый сервер», чтобы можно было проверить функциональность без DOM. Это, непременно, полезно и имеет свое место. Но это не то, что я бы рекомендовал из-за проблем, которые я только что перечислил.

То, что я хочу, это следующее:

  1. Набор тестов, гарантирующий наивысшую меру, что функциональность работает должным образом (невозможно достичь 100% уверенности без сквозных тестов, не то, на чем я сосредотачиваюсь на данный момент). Это означает, что я хочу подтвердить точное поведение функциональности с точки зрения пользователя в моих тестах.
  2. Я могу изменить или изменить реализацию функции (включая любую библиотеку ниже) в любое время с минимальным влиянием на тесты.
  3. Мне не нужно знакомиться с реализацией функций, чтобы писать тесты.
  4. Для написания этих тестов мне нужно использовать свои уже имеющиеся и знакомые знания об Интернете и веб-API в целом.

Двигаясь вперед

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

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

ОТ ОТ ОТВЕТСТВЕННОСТИ: Я не говорю, что вы никогда не должны писать модульные тесты или тесты таким образом. Конечно, каждый сценарий и проблема разные. Наденьте колпачок для мышления и решайте, что лучше всего в каждом конкретном случае!

Ладно, как нам это сделать? Просто, просто задайте себе вопрос: что бы сделал пользователь с моей программой, чтобы использовать функцию перетаскивания, и как поведет себя браузер, когда это произойдет? Когда вы получите ответ, просто запрограммируйте его в тесте, используя обычные API DOM, доступные вам jsdom! Давайте посмотрим, как будет выглядеть тест действия перетаскивания вниз для нашего конкретного примера. jest:

const getTableCells = () =>
  Array.from(mountNode.querySelectorAll("tr td:nth-of-type(1)"));
const createBubbledEvent = (type, props = {}) => {
  const event = new Event(type, { bubbles: true });
  Object.assign(event, props);
  return event;
};
const tableCells = getTableCells();
const startingNode = tableCells[0];
const endingNode = tableCells[2];
startingNode.dispatchEvent(
  createBubbledEvent("dragstart", { clientX: 0, clientY: 0 })
);
endingNode.dispatchEvent(
  createBubbledEvent("drop", { clientX: 0, clientY: 1 })
);
expect(getTableCells().map(cell => cell.textContent)).toEqual([
  "Bob",
  "Clark",
  "Alice",
]);
react-dnd-integration-testing-sample.js

Нет абсолютно ничего react-dnd или даже связан с React в этом фрагменте, и он даже не использует Simulate React Test Utils. Это значит, что я мог бы даже вообще изменить свою библиотеку пользовательского интерфейса/фреймворк на что-то вроде Angular (хортов, даже Backbone, кто-нибудь?), и этот тест все равно будет иметь смысл и работать, как ожидалось.

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

(Кстати, эксперты отрасли открыто не поощряют использование Simulate. Кроме того, если вы потратили более 5 минут на любую из проблем системы событий Enzyme, связанных с GitHub, вы увидите то же мнение самих авторов. даже были комментарии о его удаление в будущих версиях).

Есть лишь несколько вещей, которые не обязательно очевидны:

  • События должны освещаться, что не стандартно, когда мы создаем их вручную с помощью конструктора, поэтому нам нужно указать это явно. Это связано с тем, как работает система делегирования событий React. Вы можете подумать, что это деталь продажи, но это не обязательно так. События все равно появляются в обозревателе, когда они вызваны реальным взаимодействием.
  • Нам нужно установить clientX и clientY свойства события, поскольку они используются для определения направления перетаскивания. Опять же с другой реализацией могут быть другие свойства событий или другие методы, которые вам придется исправить, чтобы они работали (например .getBoundingClientRect()). Например, если реализация использовала что-то вроде .offsetX, .movementX, .top или любые другие свойства, связанные с размером, положением и движением.

И в этом все. Мы решили все мои проблемы и добились всех целей, которые поставили перед собой. Посредством нескольких строк кода можно сделать тестовое покрытие этого репо на 100% достаточно легко.

1*86rgI6XsYdNw7lRs16Fz_w
Разве это не красиво?

Последние мнения

Не стесняйтесь изучить весь набор тестов здесь. Есть несколько дополнительных вещей, чтобы получить охват до 100%, так что обязательно проверьте это. Обратите внимание, что я написал все в одном тесте только для краткости.

Еще что-то я хотел бы вспомнить. Новый разработчик мог войти в код для тестов и точно знать, что происходит. Представьте себе, если бы тесты использовали react-dnd ориентированные тесты с использованием всевозможных внутренних концепций и деталей… это было бы огромной стеной перед их лицом и могло стать существенным препятствием для их способности своевременно участвовать в тестах. В этот момент им придется пойти читать react-dnd документация, dnd-core и react-testing-backendисходный код… ага!

Я хочу оставить вам эту публикацию в блоге Софи Альперт, менеджера React Core Team в Facebook, которая описывает, как они могли успешно передать совместимое с API полную перезапись внутренних компонентов React с версии 15 до версии 16 безопасно без изменения. Спойлер: комплексные наборы тестов подтвердили функциональность библиотеки с посторонней точки зрения вместо того, чтобы сосредоточиться на деталях реализации или изолированных модульных тестах.

Что действительно смешно, так это то, что на июль 2018 года все фрагменты из официальной документации React использовали устаревшую версию 0.14, и оказалось, что они работали точно так же в версии 16.x. Это лишь показывает, какую прекрасную работу они проделали, поддерживая обратную совместимость, и это было бы невозможно без этих хорошо написанных и целенаправленных тестов!

React 16: Взгляд внутрь API-совместимой переписки нашей библиотеки интерфейса
Опубликовано на Web React 16: Взгляд внутрь API-совместимой перезаписи нашей библиотеки интерфейса React упрощает…
code.fb.com

Бонус: несколько советов, как понять, как эмулировать поведение браузера

Если есть какие-то другие функции, которые вы хотите протестировать таким образом, но вы не уверены, как именно ведет себя браузер, когда это реализуется, я предлагаю вам просмотреть Google Chrome monitorEvents API. Это безумно полезно в таких сценариях, особенно когда вы не уверены, что происходит. Я сам использовал это таким образом, чтобы исследовать форму событий, запускаемых во время перетаскивания:

monitorEvents(document.body, [
  'mousedown',

  'mousemove',

  'dragstart',

  'dragenter',

  'dragover',

  'drop',

  'dragend',

  'mouseup',
  
  // …
  
])

В общем, было бы очень полезно, если бы вы просто получили консоль разработчика браузера и начали играть с системой событий, пока не почувствуете уверенность, что знаете, как она работает. Создавайте элементы, запускайте события, перемещайте их, присоединяйте к DOM, отсоединяйте и т.д.… все, что нужно! Если вы потратите на это один или несколько часов, это послужит вам до конца вашей карьеры веб-разработчика. Достаточно милая сделка в моих глазах 🙂

Добавить комментарий

Ваш адрес email не будет опубликован.