PRO автотесты – Telegram
Channel created
Привет! Это канал про автотесты.

Меня зовут Дима Андриянов, я работаю в веб-разработке больше 20 лет. 10 из них писал бэкенд на C#, потом еще 10 лет — фронтенд в Яндексе. Сейчас руковожу командой разработчиков в Яндекс 360.

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

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

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

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

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

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

Попробуйте этот подход на своем проекте!
👍4🕊3
Многие путают слова "простой" и "лёгкий".

Антоним слова "простой" — слово "сложный". Эти понятия обозначают количество элементов, из которых состоит что-либо. Например, когда кто-то говорит "сложная система", то сразу представляется система из большого количества частей с запутанными связями. Человеческий мозг плохо обрабатывает сложность (погуглите закон Миллера).

Антоним слова "лёгкий" — слово "тяжелый" или "трудный". Эти слова описывают, сколько усилий нужно приложить, чтобы достичь чего-то. Если "простота" — это объективное понятие (количество элементов в системе), то "лёгкость" — это относительное понятие, т.к. количество труда зависит в том числе от того, кто его прилагает.

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

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

Когда видите новый модный инструмент, который так легко использовать, задумайтесь, не усложнит ли он на самом деле ваш проект. Посмотрите старый, но актуальный доклад на эту тему: "Simple Made Easy" - Rich Hickey (2011)

Да пребудет с вами сила!
3👍1🔥1
Каким образом автотесты приносят пользу? Кажется, что ответ очевиден: автотесты находят баги.

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

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

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

В следующий раз поговорим о ключевых моментах, на которые нужно обратить внимание, чтобы автотесты достоверно показывали состояние вашего проекта.
🔥8
сегодня на работе получил ачивку за 10 лет работы в Яндексе 😊
🎉164🔥3👏3
В мире интеграционных тестов есть крутой паттерн Page Object. Суть его в том, что вы обращаетесь к тестируемому приложению не напрямую из кода тестов, а делаете слой абстракции — обертку, которая содержит логику работы с элементами вашего интерфейса. Вы сможете переиспользовать логику, помещенную в обертке, во всех местах, где она нужна, а код тестов станет проще и понятнее.

Например, представьте, что у вас есть тест на Playwright, в котором написано:

// кликаем по ссылке "Get started"
await page.getByRole('link', { name: 'Get started' }).click();


Вы можете сделать PageObject

class HomePage {
// в конструкторе получаем page,
// через который происходит
// взаимодействие с браузером
constructor(private page) {}

// свойство для доступа к ссылке "Get started"
get GetStarted() {
return this.page.getByRole('link', { name: 'Get started' });
}
}


тогда код теста будет выглядеть примерно так:

const homePage = new HomePage(page);

await homePage.GetStarted.click();


Теперь во всех местах, где вам нужно кликнуть по ссылке "Get started", вам не нужно заново писать код, который обращается к ней. Вы можете просто создать Page Object и обратиться к его свойству. Очень удобно!

А самое замечательное — этот подход применим и для модульных тестов. Отличие в том, что код интеграционных тестов выполняется вне браузера и отправляет команды через специальный API (Selenium или CDP), а код модульных тестов выполняется в одном контексте с кодом тестируемого интерфейса и может напрямую оперировать DOM элементами.

class HomePage {
constructor(private page: Element) {}

get GetStarted() {
// используем функцию getByRole из библиотеки @testing-library/dom
return getByRole(this.page, 'link', { name: 'Get started' });
}
}


Я написал небольшую библиотеку jsdom-fragments. Она содержит API, с помощью которого можно легко описывать Page Objects для модульных тестов в jsdom. Похожий API мы используем в Яндексе уже 4 года и это очень удобно. Теперь вы тоже можете попробовать Page Objects в своих модульных тестах!

https://www.npmjs.com/package/jsdom-fragments
👍12👏3
Вчера на работе была встреча с QA, на которой обсуждали терминологию и подходы к автоматизации тестирования. Руководитель QA поделился интересной статьей Shift testing left with unit tests от Microsoft.

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

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

В статье выделены уровни автотестов:
L1 — модульные тесты, которые зависят только от кода
L2 — функциональные тесты, которые взаимодействуют с зависимостями вне кода (например, БД, файловая система)
L3 — тесты, проверяющие задеплоенный сервис (при обращении к соседним сервисам использовать заглушки)
L4 — тесты, максимально приближенные к контексту, в котором находится пользователь

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

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

По ссылке вы можете прочитать полный текст статьи https://learn.microsoft.com/en-us/devops/develop/shift-left-make-testing-fast-reliable
3👍2💯2
Во время прогона наших модульных тестов в консоли отображалось много предупреждений (сообщений, которые выводятся через console.error или console.warn). В основном это были предупреждения React, например об устаревших API, которые используются в компонентах.

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

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

Но мы нашли еще одно решение. Пакет vitest-fail-on-console позволяет управлять выводом предупреждений в тестах. Для jest есть похожий пакет jest-fail-on-console.

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

Теперь при запуске наших тестов в консоль не выводится ничего лишнего и сообщения об ошибках сразу видны.
🔥157
Channel photo updated
В продолжение темы о предупреждениях в тестах расскажу еще про сообщения вида:

An update to <название_компонента> inside a test was not wrapped in act


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

Но дело в том, что при запуске тестов мы продолжали видеть предупреждения "... not wrapped in act", хотя на тот момент в проекте уже использовалась testing-library и обрабатывались краевые случаи, описанные в статьях. Предупреждений было очень много, несколько сотен 😱

Мы стали копать дальше и нашли обсуждение на GitHub. Оказалось, что логика обработки этой ситуации в @testing-library завязана на статический объект, создаваемый кодом библиотеки @testing-library/dom. Если в проекте содержится несколько разных версий этой библиотеки (например, установлены как транзитивные зависимости), то может оказаться, что разные вызовы API обращаются к разным экземплярам этого объекта и из-за этого они не понимают, что действие уже обернуто в act.

В конфиге Jest мы настроили маппинг, чтобы при сборке все импорты из @testing-library/dom разрешались в одну версию библиотеки (в конкретную папку в node_modules на верхнем уровне).

  moduleNameMapper: {
'^@testing-library/dom$': `<rootDir>/node_modules/@testing-library/dom`
}


Это помогло, предупреждения исчезли. Консольный вывод модульных тестов теперь короткий и понятный 😎
👍12🔥5😎5
Уже в этот четверг я проведу мастер‑класс по модульному тестированию на HolyJS 2025 Autumn. Поговорим о том, что именно стоит тестировать в реальных проектах и зачем. Я покажу практические приёмы, которые вы сможете перенести в свой код.

Формат мастер‑класса — лайв‑кодинг в духе парного программирования. Мы возьмём несколько сценариев, характерных для реальных проектов, напишем для них автотесты и проведём рефакторинг, чтобы сделать код более удобным для тестирования.

Для демонстрации я подготовил приложение‑тренажёр с реалистичной предметной областью (интернет‑магазин). Код написан в упрощённом учебном стиле, чтобы сфокусироваться на сути: тестах и рефакторинге. Стек: TypeScript + React, есть SSR, используются популярные библиотеки React Router, React Query и Redux Toolkit.

Проект будет полезен даже тем, кто не идёт на мастер‑класс. Попробуйте клонировать репозиторий, запустить приложение локально и покрыть автотестами функциональные требования, описанные в README.

https://github.com/dima117/example-store#readme

Если появятся вопросы — пишите в комментариях к этому посту или в личные сообщения. Буду рад обсудить ваши кейсы и идеи!
🔥217👍6
Вчера провёл мастер-класс по модульным тестам на HolyJS. На учебном проекте разобрали подходы к написанию тестов и рефакторингу, чтобы модульные тесты проверяли функциональные требования, а не только API внутренних компонентов.

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

Весь код с мастер‑класса выложен на GitHub; в ключевых местах добавил комментарии. Если появятся вопросы, пишите мне — можно прямо в комментариях к этому посту.

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

Дата: понедельник, 24 ноября
Время: 19:00 мск
Ссылка: https://telemost.yandex.ru/j/4102903660
🔥2813👏1
Сейчас был созвон, на котором обсудили вопросы, не поместившиеся в мастер-класс на HolyJS. Кто-то из учасников нажал запись и она сохранилась на его Яндекс Диск. Тот, кто это сделал, поделитесь, пожалуйста, записью.

UPD: запись нашлась, доступна на Яндекс Диске
8🔥6
С Новым годом вас! 🎉

Пусть в следующем году вся работа приносит удовольствие!

Пусть в работе будет больше автоматизации и меньше рутины!

Пусть в команде будет взаимопонимание: идеи пусть встречают поддержку, а обратная связь — только конструктивная!
🎉17🎄12🔥5