Я решил более регулярно вести канал, и мне интересно, что вам интересно и в каком виде — поучаствуйте в опросе, а если что-то хочется добавить, напишите в комментариях
Please open Telegram to view this post
VIEW IN TELEGRAM
👎55🔥3
👎52
Какой контент на моем канале для вас более полезный/интересный?
Anonymous Poll
77%
образовательный
71%
кейсы
27%
рассказы про конфы, доклады, митапы
👎62
Тесты можно разделить на две школы по способу изоляции кода:
⚖️ Разница в подходах
Тестируемый код
// orderService.js
class OrderService {
constructor(paymentService) {
this.paymentService = paymentService;
}
placeOrder(order) {
if (this.paymentService.processPayment(order)) {
return 'Order placed';
} else {
return 'Payment failed';
}
}
}
// paymentService.js
class PaymentService {
processPayment(order) {
// Логика обработки платежа
return true; // Или false в случае ошибки
}
}
Классический подход 📘
test('placeOrder returns "Order placed" when payment is successful', () => {
const paymentService = new PaymentService();
const orderService = new OrderService(paymentService);
const order = { amount: 100 };
const result = orderService.placeOrder(order);
expect(result).toBe('Order placed');
});Лондонская школа
test('placeOrder returns "Order placed" when payment is successful', () => {
const paymentService = { processPayment: jest.fn().mockReturnValue(true) };
const orderService = new OrderService(paymentService);
const order = { amount: 100 };
const result = orderService.placeOrder(order);
expect(result).toBe('Order placed');
expect(paymentService.processPayment).toHaveBeenCalledWith(order);
});Выбор подхода зависит от конкретного проекта и предпочтений команды.
Теги: #unitTests #testing #cleanСode
Please open Telegram to view this post
VIEW IN TELEGRAM
👎63👍8❤3
👎65
Метрика покрытия кода показывает, какая доля исходного кода была выполнена в тестах.
Однако стопроцентное покрытие кода не гарантирует идеальное качество или отсутствие ошибок. Давайте рассмотрим два основных типа метрик покрытия кода и их ограничения.
Типы метрик покрытия кода
Code Coverage
Code Coverage измеряет отношение количества выполненных строк кода к общему количеству строк кода. На первый взгляд, кажется логичным стремиться к максимальному покрытию, но этот подход имеет свои недостатки.
Пример:
const evaluate = (x) => {
if (x > 10) {
return "Greater";
} else {
return "Lesser";
}
};
// Тесты
assert(evaluate(5) == "Lesser");В этом примере тесты покрывают 50% строк кода.
Теперь рассмотрим пример рефакторинга кода:
const evaluateRefactor = (x) => (x > 10) ? "Greater" : "Lesser";
// Тесты
assert(evaluateRefactor(5) == "Lesser");
В этом примере тесты покрывают только одну строку, что соответствует 100% покрытия
Проблемы с таким подходом:
Branch Coverage
Branch Coverage измеряет отношение количества покрытых ветвей к общему количеству ветвей в коде. Ветви – это управляющие структуры, такие как
if, switch, и циклы.Пример:
const evaluate = (x) => {
if (x > 10) {
return "Greater";
} else if (x < 5) {
return "Lesser";
} else {
return "Equal";
}
};
// Тесты
assert(evaluate(15) == "Greater");
assert(evaluate(3) == "Lesser");
assert(evaluate(7) == "Equal");В этом примере тесты покрывают все возможные ветви:
if (x > 10), else if (x < 5) и else. Таким образом, достигается стопроцентное покрытие ветвей. Однако это не гарантирует, что все граничные условия, такие как точные значения 10 и 5, были протестированы.Проблемы с таким подходом:
Заключение:
code coverage или branch coverage, является полезным инструментом, и они могут говорить о проблемахСтавьте 👍 👎
Теги: #code_coverage #branch_coverage #automation
Please open Telegram to view this post
VIEW IN TELEGRAM
👎56👍14❤3
Представьте, что вы тестируете веб-приложение. Ваше приложение может обращаться к данным с другого сервера (например, API или статики). Однако, по умолчанию браузеры блокируют такие запросы, если они идут с одного домена на другой.
Access to fetch at 'https://api.example.com/data' from origin 'https://myapp.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
Если CORS настроен неправильно, вы можете столкнуться с ситуацией, когда ваш тестируемый веб-сайт пытается получить данные с другого домена, а браузер блокирует запрос. Вы увидите ошибку в консоли разработчика, что "доступ запрещен из-за политики CORS".
Как обойти CORS при тестировании?
Иногда, для тестирования или разработки, нужно временно обойти CORS. Вот несколько способов:
-disable-web-security, чтобы отключить проверку CORS. Это быстрое решение, когда вам нужно протестировать что-то без лишних препятствий.start chrome --disable-web-security --user-data-dir="C:/chrome_dev"open -na "Google Chrome" --args --disable-web-security --user-data-dir="/tmp/chrome_dev"Access-Control-Allow-Origin: * разрешает запросы со всех доменов.Обходить CORS можно только в процессе тестирования или разработки. В реальных условиях его следует настраивать правильно, чтобы сохранить безопасность приложения и данных.
Ставьте 👍 👎
Теги: #cors #security #tools
Please open Telegram to view this post
VIEW IN TELEGRAM
👎61👍14❤4
Если вы занимаетесь тестированием веб-сайтов, то, вероятно, вам приходилось тестировать триггеры в Google Tag Manager и отправлять события аналитики.
Почему это важно? Дело в том, что большинство таких событий имеют прямое отношение к маркетингу. Маркетологи используют различные метрики продаж, чтобы понять, как лучше продавать. В общем, это напрямую связано с деньгами.
Как тестировать события
Там просто вставляете url, и проходите тест кейсы, а в другом окне наблюдаете события (рекомендуем)
все события пушится в переменную
dataLayerДля автоматизированного тестирования на Playwright, вы можете использовать несколько подходов
test('Track network request and verify payload', async ({ page }) => {
await page.goto('https://your-website.com');
// Перехват и проверка сетевых запросов
const [response] = await Promise.all([
page.waitForResponse(response => response.url().includes('www.google-analytics.com/collect') && response.status() === 200),
// Выполняем действие на странице, которое должно вызвать запрос
page.click('button')
]);
const requestPayload = JSON.parse(response.request().postData());
expect(requestPayload).toHaveProperty('event', 'yourExpectedEvent');
expect(requestPayload).toHaveProperty('gtm', 'expectedGtmId');
});dataLayertest('Check dataLayer for specific event', async ({ page }) => {
await page.goto('https://your-website.com');
// Выполняем действие на странице
await page.click('button#yourButton');
// Получаем значение dataLayer из контекста страницы
const dataLayer = await page.evaluate(() => window.dataLayer);
// Проверка, что dataLayer содержит ожидаемое событие
const eventData = dataLayer.find(event => event.event === 'yourExpectedEvent');
expect(eventData).not.toBeNull();
expect(eventData).toHaveProperty('gtm', 'yourExpectedGtm');
});Отслеживание сетевых запросов через Network-панель полно и надежно, но тесты получаются менее стабильны. Проверка данных в
dataLayer проста и быстро настраивается, но может пропускать события при перезагрузке страницы или изменениях в dataLayer.Ставьте 👍 👎
Теги: #testing #playwright #automation #dataLayer #GTM #тестирование #разработка
Please open Telegram to view this post
VIEW IN TELEGRAM
Google
Google Tag Assistant
Deploy tags in Google Tag Manager easily and safely with the Community Template Gallery.
👎66👍11❤2
#дайджест 📢
⚡️ В nodeJS 22.6 добавили поддержку typenoscript
⚡️ Playwright в 👩💻 версии **1.46
🌟 позволяет указывать клиентские сертификаты, теперь не надо игнорировать https
🌟 новая опция CLI --only-changed позволяет запускать только тестовые файлы, которые изменились с момента последней фиксации git
🌟 Обновление UI Mode / Trace Viewer
🌟 В APIRequestContext.fetch() появилась новая опция maxRetries. Она позволяет повторять попытку при возникновении сетевой ошибки ECONNRESET.
🌟 Опция Box в фикстурах уменьшает мусор в отчетах
⚡️ 👩💻 k6 v0.53.0
Добавили поддержку нативных ECMAScript модулей
и другие улучшения
⚡️ WebdriverIO 👩💻 v9
Все сеансы будут автоматически использовать протокол Bidi
Подробнее
Теги: #updates
Добавили поддержку нативных ECMAScript модулей
и другие улучшения
Все сеансы будут автоматически использовать протокол Bidi
Подробнее
Теги: #updates
Please open Telegram to view this post
VIEW IN TELEGRAM
nodejs.org
Node.js — Node.js 22.6.0 (Current)
Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and noscripts.
👎56👍6❤4
В основном консоль используют для отслеживания ошибок на сайте.
Я предлагаю открыть консоль по новому для обычного QA.
Когда я только начинал свой путь в IT, я играл в одну браузерную игру. Тогда я и представить не мог, что существует автоматизация. Мой первый код на JavaScript был связан со сбором подарков в игре.
Чтобы собрать раньше всех в игре я использовал самый примитивный код, который вставлял в консоль
setInterval(()=> {
$('#gift').click()
}, 150)Суть кода понятна каждому, где мы просто кликаем по селекту каждые 150 мс.
Давайте я подскажу кейсы где еще можно использовать простые скриптики для ускорения ручного регресса.
В основном консоль используют для отслеживания ошибок, но мало кто знает, что ее можно использовать для простой автоматизации или в качестве помощника.
Кейс 1: Подсчёт элементов на странице
Этот кейс вы уже упомянули, и он действительно полезен для быстрой оценки, например, количества отображаемых элементов.
const countElement = $$('[class^="_ObjectsListCard"]').length;
console.log(`Количество проектов: ${countElement}`);Кейс 2: Парсинг данных со страницы (ссылки)
Иногда нужно собрать все ссылки со страницы, например, для проверки их на работоспособность или для подготовки тест-кейсов.
const links = [...document.querySelectorAll('a')].map(link => link.href);
console.log('Все ссылки на странице:', links);Кейс 3: Парсинг данных с таблицы (таблицу)
Недавно я работал над задачей, связанной с таблицей, чтобы получить данные о списках поступающих и оценить вероятность поступления своего брата. Данные из таблицы для дальнейшего анализа, можно использовать следующий скрипт:
const tableRows = [...document.querySelectorAll('table tbody tr')];
const data = tableRows.map(row => {
const columns = row.querySelectorAll('td');
return {
column1: columns[0].innerText,
column2: columns[1].innerText,
column3: columns[2].innerText,
};
});
console.log('Данные из таблицы:', data);Кейс 4: Парсинг данных и выполнение вычислений
Можно не только парсить данные, но и сразу выполнять вычисления. Например, если нужно суммировать значения в колонке:
const tableRows = [...document.querySelectorAll('table tbody tr')];
const total = tableRows.reduce((sum, row) => {
const value = parseFloat(row.querySelector('td:nth-child(3)').innerText);
return sum + (isNaN(value) ? 0 : value);
}, 0);
console.log(`Сумма значений в третьей колонке: ${total}`);Кейс 5: Отправка запросов для подготовки данных
Иногда требуется подготовить данные или проверить API без использования Postman или других инструментов. Это можно сделать прямо из консоли:
fetch('<https://example.com/api/data>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ key: 'value' }),
})
.then(response => response.json())
.then(data => console.log('Ответ от сервера:', data))
.catch(error => console.error('Ошибка:', error));Кейс 6: Подключение к сокетам
Если ваш проект использует WebSocket, можно подключиться к сокету и прослушивать события прямо в консоли:
const socket = new WebSocket('wss://example.com/socket');
socket.onopen = () => {
console.log('Соединение установлено');
socket.send(JSON.stringify({ action: 'subscribe', channel: 'updates' }));
};
//далее отправляем различные запросыКейс 7: Заполнение форм
Для ускорения тестирования форм можно использовать автоматическое заполнение и отправку данных:
document.querySelector('#name').value = 'Тестовое имя';
document.querySelector('#email').value = 'test@example.com';
document.querySelector('form').submit();
console.log('Форма заполнена и отправлена'); Теги: #tools
Please open Telegram to view this post
VIEW IN TELEGRAM
👎58👍16❤6
Важное замечание👆
Однако стоит отметить, что не всегда удается просто кликнуть или заполнить поля формы через консоль. Это может быть связано с использованием сложных UI-фреймворков, которые добавляют валидацию или управляют состоянием элементов на странице. Например, в некоторых случаях элемент может не воспринимать ввод значения через
В таких ситуациях можно попытаться вызвать соответствующие события вручную:
Если и это не помогает, стоит рассмотреть использование других инструментов для автоматизации, таких как Selenium или Cypress, которые могут эмулировать взаимодействие пользователя с элементами страницы на более глубоком уровне.
О хранении и управлении скриптами будет завтра. Было полезно? Ставьте 👍👎
Теги: #automation #tools
Однако стоит отметить, что не всегда удается просто кликнуть или заполнить поля формы через консоль. Это может быть связано с использованием сложных UI-фреймворков, которые добавляют валидацию или управляют состоянием элементов на странице. Например, в некоторых случаях элемент может не воспринимать ввод значения через
value, так как внутренние скрипты фреймворка могут ожидать определенных событий или использовать собственные механизмы управления состоянием.В таких ситуациях можно попытаться вызвать соответствующие события вручную:
const nameInput = document.querySelector('#name');
nameInput.value = 'Тестовое имя';
nameInput.dispatchEvent(new Event('input', { bubbles: true }));
nameInput.dispatchEvent(new Event('change', { bubbles: true }));Если и это не помогает, стоит рассмотреть использование других инструментов для автоматизации, таких как Selenium или Cypress, которые могут эмулировать взаимодействие пользователя с элементами страницы на более глубоком уровне.
О хранении и управлении скриптами будет завтра. Было полезно? Ставьте 👍👎
Теги: #automation #tools
Please open Telegram to view this post
VIEW IN TELEGRAM
2👎60👍15❤2
Для хранения и удобного использования таких скриптов есть несколько подходов, каждый из которых имеет свои преимущества и недостатки. Вот несколько вариантов:
Вы можете хранить небольшие скрипты прямо в закладках браузера. Я использую такой метод для запросов и авторизации. Это удобно для простых и часто используемых скриптов.
javanoscript:. Например:
javanoscript:(function(){
// Ваш код здесь
const countElement = $$('[class^="_ObjectsListCard"]').length;
console.log(`Количество проектов: ${countElement}`);
})();
Если скрипты регулярно обновляются и их нужно делиться с коллегами, использование репозитория на GitHub или GitLab.
Chrome DevTools имеет встроенную возможность хранения скриптов в виде сниппетов. Это отличный способ хранить и запускать скрипты непосредственно в инструментах разработчика.
Если у вас есть набор скриптов, которые используются регулярно и в разных проектах, создание собственного расширения для Chrome может быть удобным решением.
Выбор способа хранения зависит от ваших потребностей и частоты использования скриптов. Для простых задач подойдут закладки или сниппеты в DevTools. Если скрипты требуют совместной работы или версионирования, лучше использовать GitHub/GitLab. Если же у вас сложные и регулярные задачи, имеет смысл рассмотреть создание собственного расширения для Chrome.
Ставьте
Теги: #DevTools #tools
Please open Telegram to view this post
VIEW IN TELEGRAM
2👎56👍14🔥2❤1
Видео с гейзенбага 🪲
Выложили мой доклад с последнего heisenbug.
В прошлом году я рассказывал о проблемах перфоманса, а в весной сделал акцент на проблемы рендеринга. Всем любителям девтулзов и не равнодушным к веб-тестированию рекомендую к просмотру🖥
Теги: #heisenbug #доклад #tools
Выложили мой доклад с последнего heisenbug.
В прошлом году я рассказывал о проблемах перфоманса, а в весной сделал акцент на проблемы рендеринга. Всем любителям девтулзов и не равнодушным к веб-тестированию рекомендую к просмотру🖥
Теги: #heisenbug #доклад #tools
YouTube
Алексей Иванов — Браузерные инструменты: ищем проблемы рендеринга
Ближайшая конференция — Heisenbug 2025 Autumn, 19—20 октября, Санкт-Петербург + online. Подробности и билеты: https://jrg.su/D6uGC9
— —
Скачать презентацию с сайта Heisenbug — https://jrg.su/O4v5Fo
Глубокий технический анализ процессов веб-рендеринга, а…
— —
Скачать презентацию с сайта Heisenbug — https://jrg.su/O4v5Fo
Глубокий технический анализ процессов веб-рендеринга, а…
2👎63👍9❤3🔥3
Захват и анализ сетевого трафика через chrome://net-export. Это может быть полезно
chrome://net-export/ может предоставить более подробные данные, включая информацию о сетевых пакетах, DNS-запросах и внутренней работе браузера.В Chrome есть встроенная функция
net-export, которая проста и удобна в использовании:В итоге,
chrome://net-export/ чаще используется для более глубокого и продолжительного анализа сетевой активности, когда стандартных возможностей DevTools недостаточно для решения задач.Дальше открываешь полученный JSON netlog-viewer.appspot.com
Ставьте 👍👎
Теги: #chrome #netexport #devtools
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
1👎65👍13🤯4❤1
Forwarded from Грамота от Кузьмича
"Что лежит в его мерзких грязных карманцах?"
(с) Голлум, Властелин колец
Пороемся в карманах у браузера - где и что он хранит - и разберем cookies, sessionStorage & localStorage.
Про куки мы уже говорили в одном из постов выше. Вкратце - это небольшие файлы, которые сервер подкладывает в наш браузер что бы помнить кто ты, что ты, и какие настройки используешь. Куки отправляются сайту с каждым запросом к этому сайту.
📦localStorage - долгосрочное хранилище данных. Данные из localStorage доступны из любого окна/вкладки и хранятся до тех пор пока ты сам или веб приложение их не удалит. Т.е. localStorage похож на браузерный кеш - только хранит не скрипты, стили и картинки - а данные.
🖼Пример из жизни - Youtube хранит уровень громкости видео в localStorage (по ключу yt-player-volume), а раз localStorage доступен для всех вкладок и окон - то поэтому по умолчанию каждое новое открытое окно ютюба будет иметь одинаково выставленный уровень громкости.
🔒Доступ - через DevTools (вкладка Application) или через JS:
localStorage.setItem('имяКлюча', 'значение'); //запись
localStorage.getItem('имяКлюча'); //чтение📦sessionStorage - временное хранилище данных. Данные хранятся только пока ты находишься на конкретном сайте в конкретной вкладке. Как только ты покинешь сайт или закроешь вкладку - пиши пропало. Это типа буфера обмена, или временных заметок на полях для веб-приложений.
🖼Пример из жизни - Amazon при оформлении заказа может хранить данные из заполненных форм именно тут - что бы если пользаку после заполнения адреса доставки вдруг приспичило вернуться на предыдущий шаг - данные уже заполненного адреса не оказались потеряны. А Netflix - сохранит в sessionStorage текущую отметку времени просмотра видоса, так что при обновлении страницы ты не упадешь в самое начало серии а продолжишь просмотр оттуда где прервался. А еще с помощью sessionStorage запомнит что показывал те или иные рекомендации что бы не бесить по 10 раз подряд всплывашкой с рекомендацией одного и того же сериала.
🔒Доступ - через DevTools (вкладка Application) или через JS:
sessionStorage.setItem('имяКлюча', 'значение'); //запись
sessionStorage.getItem('имяКлюча'); //чтениеВ основе sessionStorage & localStorage так же как и у кук - лежит доменная модель. Т.е. каждому сайту - свой огород, свое пространство. Поэтому не удивляйся когда увидишь во вкладке дев тулов Local storage и Session storage по несколько хранилищ каждого из типов - каждый из сайтов будет иметь свое и в то же время не будет иметь доступа к "чужим" хранилищам, хранилищам других сайтов.
📦Для особо тяжелых (ресурсоемких) случаев - есть еще и IndexedDB - встроенная в браузер база данных (присутствует и в Chrome, и в Firefox, и в Safari). Там размер не фиксирован 5 или 10 мегабайтами и может достигать до 20% от свободного дискового пространства, а иногда и больше.
🖼Пример из жизни - та же гугл почта или гугл доки будут хранить копию данных в indexedDB, что позволит продолжить с ними работу даже при потере интернет-соединения. А при возврате коннекта - локально внесенные изменения в indexedDB тут же подтянутся на сервер, в облако.
___
P.S. огоньки🔥, сердечки❤️ и репосты📩 очень помогают каналу и заряжают мою батарейку на дальнейшее творчество и контент😉😇
2👎62👍19🔥15❤7
Локаторы vs селекторы🤓 💪
Что такое селекторы и локаторы в Playwright?
📌 Селекторы — это строки, которые используются для сопоставления элементов в DOM страницы. Playwright поддерживает различные типы селекторов, включая поиск по тексту, CSS, XPath и другие.
📌 Локаторы — это абстракция, построенная на основе селекторов, которая позволяет находить любые элементы на странице, а также обеспечивает "умные" ожидания, являющиеся ключевой функцией Playwright.
Я планирую написать подробное руководство о том, как и какой локатор лучше использовать. Вот выдержка из документации по рекомендуемым локаторам. Если ни одна из функций не подходит, можно добавить атрибут data-testid:
🔵
🔵
🔵
🔵
🔵
🔵
🔵
Было полезно? Ставьте👍 👎
Теги: #playwright
Что такое селекторы и локаторы в Playwright?
Я планирую написать подробное руководство о том, как и какой локатор лучше использовать. Вот выдержка из документации по рекомендуемым локаторам. Если ни одна из функций не подходит, можно добавить атрибут data-testid:
page.getByRole() для поиска по явным и неявным атрибутам доступности.page.getByText() для поиска по текстовому содержимому.page.getByLabel() для поиска по лейблуpage.getByPlaceholder() для поиска поля ввода по плейсхолдеру.page.getByAltText() для поиска элемента, по альтернативному тексту.page.getByTitle() для поиска элемента по его атрибуту noscript.page.getByTestId() для поиска элемента по атрибуту data-testid.Было полезно? Ставьте
Теги: #playwright
Please open Telegram to view this post
VIEW IN TELEGRAM
1👎64👍20❤2
Как сделать так, чтобы в ветку попадал только чистый и рабочий код?👩🍳
В разработке важно, чтобы в ветку попадал код, который соответствует стандартам и не ломает приложение или на код-ревью не писали💩 Один из эффективных способов это обеспечить — использование Git хуков, особенно
Для упрощения этого процесса есть масса различных библиотек📖 :
📍 husky (nodejs)
📍 pre-commit (python)
📍 использовать нативные (для любого проекта)
Пример настройки для nodejs husky📝
Прежде чем приступать к настройки у нас должны быть установлены linter, prettier
Пример package.json
Команда init упрощает настройку husky в проекте. Он создает
Дальше просто добавляем наши скрипты проверки качества кода
После можно попробовать сделать коммит и наши скрипты запустятся
Если вам надо закоммитить быстро можно отключить запуск хуков
Было полезно? Ставьте👍 👎
Теги: #husky #lint #prettier #git #commit
В разработке важно, чтобы в ветку попадал код, который соответствует стандартам и не ломает приложение или на код-ревью не писали
pre-commit. Этот хук запускает проверки до того, как изменения будут зафиксированы, и позволяет автоматизировать запуск линтера и тестов перед каждым коммитом.Для упрощения этого процесса есть масса различных библиотек
Пример настройки для nodejs husky
Прежде чем приступать к настройки у нас должны быть установлены linter, prettier
Пример package.json
{
"noscripts": {
"lint": "eslint .",
"format": "prettier . --write",
"test": "jest"
}
}Команда init упрощает настройку husky в проекте. Он создает
pre-commit в .husky/ и обновляет сценарий для подготовки в package.json.npx husky init
Дальше просто добавляем наши скрипты проверки качества кода
echo "npm prettier && npm lint && npm test" > .husky/pre-commit
После можно попробовать сделать коммит и наши скрипты запустятся
Если вам надо закоммитить быстро можно отключить запуск хуков
git commit -m "..." -n
Было полезно? Ставьте
Теги: #husky #lint #prettier #git #commit
Please open Telegram to view this post
VIEW IN TELEGRAM
1👎67👍15❤2🔥2
Меня читают много разработчиков и скорее всего это пост для них.
При написании юнит тестов возникает необходимость структурировать код таким образом, чтобы он был понятным, легко поддерживаемым и эффективно выявлял ошибки. Один из наиболее популярных подходов для достижения этих целей — паттерн AAA (Arrange-Act-Assert).
Патерн делит процесс написание тестов на 3 логические части:
Пример прям из тестов текущего проекта:
test('Закрывает Drawer при вызове onClose', () => {
// Arrange
render(<Comments />);
const closeButton = screen.getByRole('button', { name: 'close' });
// Act
fireEvent.click(closeButton);
TypeScript
// Assert
expect(mockedUseStore().services.comments.close).toHaveBeenCalled();
});Рекомендации
Почти все эти рекомендации можно соблюдать автоматически настроив правила
eslint-a с плагином eslint-plugin-jest.
Например:
test('Закрывает и открывает Drawer при вызове onClose', () => {
// Arrange
render(<Comments />);
const closeButton = screen.getByRole('button', { name: 'close' });
// Act
fireEvent.click(closeButton);
// Assert
expect(mockedUseStore().services.comments.close).toHaveBeenCalled();
// Arrange
const openCommentButton = screen.getByRole('button', { name: 'Комментарии' });
// Act
fireEvent.click(openCommentButton);
// Assert
expect(modal).toBeVisible();
});Тесты должны быть последовательны и без ветвлений.
Включите правило линтера: no-conditional-in-test.
Можно использовать правила линтера:
По мотивам книги Принципы юнит-тестирования
Теги: #unitTests #cleanСode #testing #TDD
Please open Telegram to view this post
VIEW IN TELEGRAM
1👎61👍8🔥4
Выход playwright 1.47
⚡ Обновления Playwright:
1⃣ Network Tab (небольшие улучшения в UI mode и trace viewer)
⚪ Фильтрация по типу запроса и URL
⚪ Улучшенное отображения query parameters
⚪ Превью для шрифтов
2⃣ Опция — tsconfig
Теперь можно указать конкретный tsconfig для всех импортируемых файлов:
3⃣ APIRequestContext
Поддержка URLSearchParams в query параметрах:
https://github.com/microsoft/playwright/releases/tag/v1.47.0
Теги: #playwright #updates
Теперь можно указать конкретный tsconfig для всех импортируемых файлов:
npx playwright test --tsconfig tsconfig.test.json
Поддержка URLSearchParams в query параметрах:
const response = await request.get('url', { params: 'userId=1' });https://github.com/microsoft/playwright/releases/tag/v1.47.0
Теги: #playwright #updates
Please open Telegram to view this post
VIEW IN TELEGRAM
GitHub
Release v1.47.0 · microsoft/playwright
Network Tab improvements
The Network tab in the UI mode and trace viewer has several nice improvements:
filtering by asset type and URL
better display of query string parameters
preview of font as...
The Network tab in the UI mode and trace viewer has several nice improvements:
filtering by asset type and URL
better display of query string parameters
preview of font as...
1👎63🔥13👍5❤3
Продолжаем тему с юнит тестами, и поговорим какие существуют антипаттерны при написании юнит тестов.
У вас есть класс
PaymentProcessor, который обрабатывает платежи и содержит приватный метод для валидации транзакций.class PaymentProcessor {
private validateTransaction(transaction: Transaction): boolean {
// сложная логика
return transaction.amount > 0 && transaction.currency === 'USD';
}
processPayment(transaction: Transaction): string {
if (this.validateTransaction(transaction)) {
return 'Payment processed';
} else {
return 'Invalid transaction';
}
}
}Антипаттерн:
Делаем метод публичным и тестирование метода
validateTransaction напрямую.Почему это плохо:
Тестировать публичный метод
processPayment, который использует приватный метод.Или провести рефакторинг и вынести
validateTransaction в отдельный класс.Класс
Inventory управляет списком товаров на складе.class Inventory {
private items: Item[] = [];
addItem(item: Item): void {
this.items.push(item);
}
getItems(): ReadonlyArray<Item> {
return this.items;
}
// Другие методы...
}Антипаттерн:
Чтобы упростить тестирование, приватное свойство
items делается публичным.Тестировать публичный метод
processPayment, который использует приватный метод.Система рассчитывает налог в зависимости от категории продукта.
class TaxCalculator {
calculateTax(product: Product): number {
if (product.category === 'Food') {
return product.price * 0.05;
} else if (product.category === 'Electronics') {
return product.price * 0.2;
}
return product.price * 0.1;
}
}Антипаттерн:
Тест дублирует бизнес-логику при расчете ожидаемого результата.
describe('TaxCalculator', () => {
it('should calculate tax for food category', () => {
const calculator = new TaxCalculator();
const product = { price: 100, category: 'Food' };
// тут дублируется логика
const expectedTax = product.price * 0.05;
const tax = calculator.calculateTax(product);
expect(tax).toBe(expectedTax);
});
});Почему это плохо:
Использовать конкретные значения для ожидаемых результатов.
describe('TaxCalculator', () => {
it('should calculate 5% tax for food category', () => {
const calculator = new TaxCalculator();
const product = { price: 100, category: 'Food' };
const tax = calculator.calculateTax(product);
expect(tax).toBe(5);
});
it('should calculate 20% tax for electronics category', () => {
const calculator = new TaxCalculator();
const product = { price: 200, category: 'Electronics' };
const tax = calculator.calculateTax(product);
expect(tax).toBe(40);
});
}); Теги: #unitTests #cleanСode #TDD #BDD #learning #recommendation #practices
Please open Telegram to view this post
VIEW IN TELEGRAM
👎58👍7❤3🔥2