Привет! Это канал про автотесты.
Меня зовут Дима Андриянов, я работаю в веб-разработке больше 20 лет. 10 из них писал бэкенд на C#, потом еще 10 лет — фронтенд в Яндексе. Сейчас руковожу командой разработчиков в Яндекс 360.
Кажется, у меня большой опыт в автотестах:
- я организовывал автоматизированное тестирование в нескольких подразделениях Яндекса,
- с 2017 года читаю лекцию по автотестам в Школе разработки интерфейсов (и был человеком, который инициировал появление этой темы в учебной программе),
- рассказываю про автотесты на конференциях.
Во многих командах автотесты воспринимаются не как полезный инструмент, а как дополнительная нагрузка на разработчиков. Разработчики пишут множество тестов, но получают от них мало пользы. Так происходит потому, что они упускают из виду некоторые важные вещи, связанные с автотестами.
Я создал этот канал, чтобы акцентировать внимание на этих важных вещах. Информация здесь — это результат множества экспериментов, которые мы проводили в течение нескольких лет. В наших командах эти идеи уже принесли значительную пользу. Надеюсь, будут полезны и вам.
Меня зовут Дима Андриянов, я работаю в веб-разработке больше 20 лет. 10 из них писал бэкенд на C#, потом еще 10 лет — фронтенд в Яндексе. Сейчас руковожу командой разработчиков в Яндекс 360.
Кажется, у меня большой опыт в автотестах:
- я организовывал автоматизированное тестирование в нескольких подразделениях Яндекса,
- с 2017 года читаю лекцию по автотестам в Школе разработки интерфейсов (и был человеком, который инициировал появление этой темы в учебной программе),
- рассказываю про автотесты на конференциях.
Во многих командах автотесты воспринимаются не как полезный инструмент, а как дополнительная нагрузка на разработчиков. Разработчики пишут множество тестов, но получают от них мало пользы. Так происходит потому, что они упускают из виду некоторые важные вещи, связанные с автотестами.
Я создал этот канал, чтобы акцентировать внимание на этих важных вещах. Информация здесь — это результат множества экспериментов, которые мы проводили в течение нескольких лет. В наших командах эти идеи уже принесли значительную пользу. Надеюсь, будут полезны и вам.
❤12
Многие команды не пишут модульные (unit) тесты и считают их бесполезными. Такие тесты проверяют отдельные кусочки приложения, а продуктовые сценарии, работоспособность которых действительно важна, задействуют десятки или сотни таких кусочков. Проверка каждого из кусочков не гарантирует работоспособность какого-либо продуктового сценария, поэтому тестирование отдельного модуля — бессмысленно.
Мне попалась статья Мартина Фаулера об этом. Он говорит, что важен не размер, а изоляция модуля. Тестируемой сущностью unit-тестов может быть фрагмент приложения любого размера. Главное — чтобы он был изолирован от внешних зависимостей.
Откуда возникло мнение о том, что unit-тесты могут проверять только что-то маленькое? Кажется, причина в том, что многие приложения имеют внутри запутанные связи и их разработчики могут изолированно создать в тестах экземпляры только самых маленьких модулей.
В двух командах, где я работал, мы провели рефакторинг приложения, чтобы упорядочить зависимости модулей. В результате мы научились создавать экземпляры фрагментов приложения любого размера и писать на них unit-тесты. Оказалось, что это производительный и стабильный инструмент автоматизации тестирования. Большинство тестовых сценариев переехало в модульные тесты. Суммарное время прогона тестов сократилось с часов до минут, а количество ложных срабатываний — уменьшилось до нуля.
Попробуйте этот подход на своем проекте!
Мне попалась статья Мартина Фаулера об этом. Он говорит, что важен не размер, а изоляция модуля. Тестируемой сущностью unit-тестов может быть фрагмент приложения любого размера. Главное — чтобы он был изолирован от внешних зависимостей.
Откуда возникло мнение о том, что unit-тесты могут проверять только что-то маленькое? Кажется, причина в том, что многие приложения имеют внутри запутанные связи и их разработчики могут изолированно создать в тестах экземпляры только самых маленьких модулей.
В двух командах, где я работал, мы провели рефакторинг приложения, чтобы упорядочить зависимости модулей. В результате мы научились создавать экземпляры фрагментов приложения любого размера и писать на них unit-тесты. Оказалось, что это производительный и стабильный инструмент автоматизации тестирования. Большинство тестовых сценариев переехало в модульные тесты. Суммарное время прогона тестов сократилось с часов до минут, а количество ложных срабатываний — уменьшилось до нуля.
Попробуйте этот подход на своем проекте!
martinfowler.com
bliki: Unit Test
Unit Tests are focused on small parts of a code-base, defined in regular programming tools, and fast. There is disagreement on whether units should be solitary or sociable.
👍4🕊3
Сегодня опубликовали первый кусок программы осенней HolyJS и там есть мой воркшоп про модульные тесты!
https://holyjs.ru/talks/49b78b08a81445db963490d51be07131/
https://holyjs.ru/talks/49b78b08a81445db963490d51be07131/
HolyJS 2025 Autumn. JavaScript-конференция: от фронтенда до бэкенда
Как писать полезные unit-тесты для веб-интерфейса | Доклад на HolyJS 2025 Autumn
Вы узнаете, как сделать свой проект тестируемым, и познаете дзен unit-тестов. Научитесь писать тесты, которые проверяют продуктовые сценарии, выполняются за секунды и не падают, если приложение не сломано.
🔥9
Многие путают слова "простой" и "лёгкий".
Антоним слова "простой" — слово "сложный". Эти понятия обозначают количество элементов, из которых состоит что-либо. Например, когда кто-то говорит "сложная система", то сразу представляется система из большого количества частей с запутанными связями. Человеческий мозг плохо обрабатывает сложность (погуглите закон Миллера).
Антоним слова "лёгкий" — слово "тяжелый" или "трудный". Эти слова описывают, сколько усилий нужно приложить, чтобы достичь чего-то. Если "простота" — это объективное понятие (количество элементов в системе), то "лёгкость" — это относительное понятие, т.к. количество труда зависит в том числе от того, кто его прилагает.
К сожалению, многие популярные подходы и инструменты для программирования фокусируются на легкости использования, но при этом скрывают сложность вашей программы. Как минимум, скрытую сложность будет сложнее контролировать, а в худшем случае, сложность может увеличиться.
Не нужно далеко ходить за примерами. Все мы запускали короткую команду
Когда видите новый модный инструмент, который так легко использовать, задумайтесь, не усложнит ли он на самом деле ваш проект. Посмотрите старый, но актуальный доклад на эту тему: "Simple Made Easy" - Rich Hickey (2011)
Да пребудет с вами сила!
Антоним слова "простой" — слово "сложный". Эти понятия обозначают количество элементов, из которых состоит что-либо. Например, когда кто-то говорит "сложная система", то сразу представляется система из большого количества частей с запутанными связями. Человеческий мозг плохо обрабатывает сложность (погуглите закон Миллера).
Антоним слова "лёгкий" — слово "тяжелый" или "трудный". Эти слова описывают, сколько усилий нужно приложить, чтобы достичь чего-то. Если "простота" — это объективное понятие (количество элементов в системе), то "лёгкость" — это относительное понятие, т.к. количество труда зависит в том числе от того, кто его прилагает.
К сожалению, многие популярные подходы и инструменты для программирования фокусируются на легкости использования, но при этом скрывают сложность вашей программы. Как минимум, скрытую сложность будет сложнее контролировать, а в худшем случае, сложность может увеличиться.
Не нужно далеко ходить за примерами. Все мы запускали короткую команду
npm i, которая выкачивает гигабайты пакетов в папку node_modules.Когда видите новый модный инструмент, который так легко использовать, задумайтесь, не усложнит ли он на самом деле ваш проект. Посмотрите старый, но актуальный доклад на эту тему: "Simple Made Easy" - Rich Hickey (2011)
Да пребудет с вами сила!
❤3👍1🔥1
Каким образом автотесты приносят пользу? Кажется, что ответ очевиден: автотесты находят баги.
Но если так, то получается, что польза от автотестов возникает только тогда, когда они упали, и эта польза заключается в информации о найденном баге. Получается, что все успешные запуски автотестов бесполезны?
На самом деле, нет. Успешные и неуспешные автотесты одинаково полезны, если они показывают состояние вашего проекта, а не только наличие багов. Если ваши автотесты надежны и вы доверяете их результатам, то успешный прогон автотестов будет означать гарантию, что с проектом всё хорошо. Эта информация, полученная от автотестов, позволит вам принимать решения в процессе разработки, сэкономит команде время и нервы.
Посмотрите на автотесты в своем проекте и задайте себе вопрос: какую информацию вы получаете в результате их прогона. Полезна ли она?
В следующий раз поговорим о ключевых моментах, на которые нужно обратить внимание, чтобы автотесты достоверно показывали состояние вашего проекта.
Но если так, то получается, что польза от автотестов возникает только тогда, когда они упали, и эта польза заключается в информации о найденном баге. Получается, что все успешные запуски автотестов бесполезны?
На самом деле, нет. Успешные и неуспешные автотесты одинаково полезны, если они показывают состояние вашего проекта, а не только наличие багов. Если ваши автотесты надежны и вы доверяете их результатам, то успешный прогон автотестов будет означать гарантию, что с проектом всё хорошо. Эта информация, полученная от автотестов, позволит вам принимать решения в процессе разработки, сэкономит команде время и нервы.
Посмотрите на автотесты в своем проекте и задайте себе вопрос: какую информацию вы получаете в результате их прогона. Полезна ли она?
В следующий раз поговорим о ключевых моментах, на которые нужно обратить внимание, чтобы автотесты достоверно показывали состояние вашего проекта.
🔥8
В мире интеграционных тестов есть крутой паттерн Page Object. Суть его в том, что вы обращаетесь к тестируемому приложению не напрямую из кода тестов, а делаете слой абстракции — обертку, которая содержит логику работы с элементами вашего интерфейса. Вы сможете переиспользовать логику, помещенную в обертке, во всех местах, где она нужна, а код тестов станет проще и понятнее.
Например, представьте, что у вас есть тест на Playwright, в котором написано:
Вы можете сделать PageObject
тогда код теста будет выглядеть примерно так:
Теперь во всех местах, где вам нужно кликнуть по ссылке "Get started", вам не нужно заново писать код, который обращается к ней. Вы можете просто создать Page Object и обратиться к его свойству. Очень удобно!
А самое замечательное — этот подход применим и для модульных тестов. Отличие в том, что код интеграционных тестов выполняется вне браузера и отправляет команды через специальный API (Selenium или CDP), а код модульных тестов выполняется в одном контексте с кодом тестируемого интерфейса и может напрямую оперировать DOM элементами.
Я написал небольшую библиотеку jsdom-fragments. Она содержит API, с помощью которого можно легко описывать Page Objects для модульных тестов в jsdom. Похожий API мы используем в Яндексе уже 4 года и это очень удобно. Теперь вы тоже можете попробовать Page Objects в своих модульных тестах!
https://www.npmjs.com/package/jsdom-fragments
Например, представьте, что у вас есть тест на 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
Там описывается подход, с помощью которого команде огромного проекта удалось переработать свой набор тестов и сократить время от влития изменений в основную ветку до релиза с нескольких дней до 2 часов.
Авторы статьи предлагают разделить тесты на слои на основе количества зависимостей и времени выполнения. Лёгкие и быстрые тесты нужно выполнять как можно раньше и чаще, например, на машине разработчика или при изменениях в pull request. Тяжелые медленные тесты можно выполнять позже и реже, например, после мержа изменений в основную ветку и при релизах.
В статье выделены уровни автотестов:
L1 — модульные тесты, которые зависят только от кода
L2 — функциональные тесты, которые взаимодействуют с зависимостями вне кода (например, БД, файловая система)
L3 — тесты, проверяющие задеплоенный сервис (при обращении к соседним сервисам использовать заглушки)
L4 — тесты, максимально приближенные к контексту, в котором находится пользователь
Ключевые принципы:
- каждый тест должен быть написан на максимально простом уровне
- при проектировании продукта нужно сразу учитывать возможность тестирования
- код тестов настолько же важен, как и код продукта
- инфраструктура для тестирования должна быть общей
- за автотесты отвечают не только QA, но и разработка
Этот подход близок к тому, что мы делаем в своих проектах внутри Яндекс. Круто, что статья коротко, но при этом понятно объясняет ключевые идеи.
По ссылке вы можете прочитать полный текст статьи https://learn.microsoft.com/en-us/devops/develop/shift-left-make-testing-fast-reliable
Docs
Shift testing left with unit tests - Azure DevOps
Learn about shift left, unit tests, and other DevOps test principles and strategies that lead to better code quality and faster time to production.
❤3👍2💯2
Во время прогона наших модульных тестов в консоли отображалось много предупреждений (сообщений, которые выводятся через
Из-за этих предупреждений консольный вывод превращался в простыню текста (как на картинке). Это усложняло работу с тестами, т.к. среди этих предупреждений трудно было заметить полезные сообщения об ошибках.
Конечно же мы починили все предупреждения, которые возникали из-за кода, написанного в нашем проекте. Но часть ошибок возникало в коде сторонних библиотек, на которые мы не могли влиять. Получается, для решения проблемы нужно было либо внести изменения в код сторонних библиотек, либо отказаться от их использования.
Но мы нашли еще одно решение. Пакет vitest-fail-on-console позволяет управлять выводом предупреждений в тестах. Для jest есть похожий пакет jest-fail-on-console.
Мы настроили, чтобы заданные предупреждения сторонних библиотек не выводились на экран, а при возникновении любых других предупреждений тесты считались упавшими (чтобы разработчик не мог влить в основную ветку код, из-за которого возникают новые предупреждения в консоли).
Теперь при запуске наших тестов в консоль не выводится ничего лишнего и сообщения об ошибках сразу видны.
console.error или console.warn). В основном это были предупреждения React, например об устаревших API, которые используются в компонентах.Из-за этих предупреждений консольный вывод превращался в простыню текста (как на картинке). Это усложняло работу с тестами, т.к. среди этих предупреждений трудно было заметить полезные сообщения об ошибках.
Конечно же мы починили все предупреждения, которые возникали из-за кода, написанного в нашем проекте. Но часть ошибок возникало в коде сторонних библиотек, на которые мы не могли влиять. Получается, для решения проблемы нужно было либо внести изменения в код сторонних библиотек, либо отказаться от их использования.
Но мы нашли еще одно решение. Пакет vitest-fail-on-console позволяет управлять выводом предупреждений в тестах. Для jest есть похожий пакет jest-fail-on-console.
Мы настроили, чтобы заданные предупреждения сторонних библиотек не выводились на экран, а при возникновении любых других предупреждений тесты считались упавшими (чтобы разработчик не мог влить в основную ветку код, из-за которого возникают новые предупреждения в консоли).
Теперь при запуске наших тестов в консоль не выводится ничего лишнего и сообщения об ошибках сразу видны.
🔥15❤7
В продолжение темы о предупреждениях в тестах расскажу еще про сообщения вида:
Это предупреждение возникает, когда после завершения теста React продолжает выполнять обновления компонентов. В интернете много информации на эту тему (например, здесь и здесь). Статьи по этим ссылкам, а также документация React, рекомендуют взаимодействовать с компонентами в тестах через какую-нибудь готовую библиотеку, которая обрабатывает эту особенность.
Но дело в том, что при запуске тестов мы продолжали видеть предупреждения
Мы стали копать дальше и нашли обсуждение на GitHub. Оказалось, что логика обработки этой ситуации в
В конфиге Jest мы настроили маппинг, чтобы при сборке все импорты из
Это помогло, предупреждения исчезли. Консольный вывод модульных тестов теперь короткий и понятный 😎
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
Если появятся вопросы — пишите в комментариях к этому посту или в личные сообщения. Буду рад обсудить ваши кейсы и идеи!
Формат мастер‑класса — лайв‑кодинг в духе парного программирования. Мы возьмём несколько сценариев, характерных для реальных проектов, напишем для них автотесты и проведём рефакторинг, чтобы сделать код более удобным для тестирования.
Для демонстрации я подготовил приложение‑тренажёр с реалистичной предметной областью (интернет‑магазин). Код написан в упрощённом учебном стиле, чтобы сфокусироваться на сути: тестах и рефакторинге. Стек: TypeScript + React, есть SSR, используются популярные библиотеки React Router, React Query и Redux Toolkit.
Проект будет полезен даже тем, кто не идёт на мастер‑класс. Попробуйте клонировать репозиторий, запустить приложение локально и покрыть автотестами функциональные требования, описанные в README.
https://github.com/dima117/example-store#readme
Если появятся вопросы — пишите в комментариях к этому посту или в личные сообщения. Буду рад обсудить ваши кейсы и идеи!
HolyJS 2025 Autumn. JavaScript-конференция: от фронтенда до бэкенда
Как писать полезные unit-тесты для веб-интерфейса | Доклад на HolyJS 2025 Autumn
Вы узнаете, как сделать свой проект тестируемым, и познаете дзен unit-тестов. Научитесь писать тесты, которые проверяют продуктовые сценарии, выполняются за секунды и не падают, если приложение не сломано.
🔥21❤7👍6
Вчера провёл мастер-класс по модульным тестам на HolyJS. На учебном проекте разобрали подходы к написанию тестов и рефакторингу, чтобы модульные тесты проверяли функциональные требования, а не только API внутренних компонентов.
Два часа лайвкодинга пролетели незаметно. К сожалению, успел рассказать около 70% от запланированного, но зато обсудили много вопросов из зала. В конце голова кипела от большого количества информации, но кажется, многим участникам понравилось и было полезно.
Весь код с мастер‑класса выложен на GitHub; в ключевых местах добавил комментарии. Если появятся вопросы, пишите мне — можно прямо в комментариях к этому посту.
Хочу рассказать оставшуюся часть материала. Предлагаю созвониться в ближайший понедельник вечером: покажу несколько приёмов, как сделать код автотестов проще для восприятия. Приходите!
Два часа лайвкодинга пролетели незаметно. К сожалению, успел рассказать около 70% от запланированного, но зато обсудили много вопросов из зала. В конце голова кипела от большого количества информации, но кажется, многим участникам понравилось и было полезно.
Весь код с мастер‑класса выложен на GitHub; в ключевых местах добавил комментарии. Если появятся вопросы, пишите мне — можно прямо в комментариях к этому посту.
Хочу рассказать оставшуюся часть материала. Предлагаю созвониться в ближайший понедельник вечером: покажу несколько приёмов, как сделать код автотестов проще для восприятия. Приходите!
Дата: понедельник, 24 ноября
Время: 19:00 мск
Ссылка: https://telemost.yandex.ru/j/4102903660
🔥28❤13👏1
Сейчас был созвон, на котором обсудили вопросы, не поместившиеся в мастер-класс на HolyJS. Кто-то из учасников нажал запись и она сохранилась на его Яндекс Диск. Тот, кто это сделал, поделитесь, пожалуйста, записью.
UPD: запись нашлась, доступна на Яндекс Диске
UPD: запись нашлась, доступна на Яндекс Диске
Яндекс Диск
2025-11-24_190704_про автотесты.webm
Посмотреть и скачать с Яндекс Диска
❤8🔥6
