Выходные — хороший повод заняться тем, на что вечно не хватает времени. Например, освоить новый навык или углубиться в знакомую тему.
Вот несколько сочетаний:
Можно выбрать и другие курсы — под свои интересы и цели.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Тестируем не запрос, а цепочку действий
В учебной корзине добавление работает. При удалении товар исчезает, а общая цена остаётся прежней. Одиночный happy path этого не показывает.
Проверка модели
— Храним простой эталон корзины.
— Hypothesis выбирает цепочки add/remove.
— После каждого шага сравниваем итог с суммой позиций модели.
В проверенном примере Hypothesis 6.169 сократил сбой до
Найденную цепочку сохраняем как регрессионный пример. Успешный прогон не доказывает корректность всех сценариев.
Документация
Навигация: Вакансии
Библиотека тестировщика
#теория_на_пальцах
В учебной корзине добавление работает. При удалении товар исчезает, а общая цена остаётся прежней. Одиночный happy path этого не показывает.
Проверка модели
— Храним простой эталон корзины.
— Hypothesis выбирает цепочки add/remove.
— После каждого шага сравниваем итог с суммой позиций модели.
В проверенном примере Hypothesis 6.169 сократил сбой до
add(pen) → remove(pen): товара нет, сумма всё ещё 20. Исправленный вариант прошёл прогоны с max_examples=50 и stateful_step_count=20.Найденную цепочку сохраняем как регрессионный пример. Успешный прогон не доказывает корректность всех сценариев.
Документация
Навигация: Вакансии
Библиотека тестировщика
#теория_на_пальцах
❤2
Два NULL при UNIQUE. Это баг?
В PostgreSQL 18 у необязательного поля
Подсказка: уникальность и обязательность значения — разные требования.
Сам по себе второй NULL не нарушает обычный UNIQUE в PostgreSQL 18. По умолчанию два NULL не считаются равными при этой проверке. Если по требованиям допустим только один NULL, проверьте вариант UNIQUE NULLS NOT DISTINCT. Если значение обязательно, нужен NOT NULL.
Перед выводом сверяйте требования с реальным DDL таблицы. Проверьте три случая: два одинаковых непустых значения, два разных и два
Отдельно посмотрите, что API сохраняет из пустой строки и отсутствующего поля запроса. Это разные входные данные, и приложение может их преобразовывать. Поведение PostgreSQL не стоит переносить на все СУБД.
Ограничения в PostgreSQL 18
#теория_на_пальцах
В PostgreSQL 18 у необязательного поля
external_id стоит обычный UNIQUE. Вы создаёте две записи без внешнего идентификатора, в обеих сохраняется NULL. База принимает их. Заводим дефект?Подсказка: уникальность и обязательность значения — разные требования.
Перед выводом сверяйте требования с реальным DDL таблицы. Проверьте три случая: два одинаковых непустых значения, два разных и два
NULL.Отдельно посмотрите, что API сохраняет из пустой строки и отсутствующего поля запроса. Это разные входные данные, и приложение может их преобразовывать. Поведение PostgreSQL не стоит переносить на все СУБД.
Ограничения в PostgreSQL 18
#теория_на_пальцах
❤1👍1
Checks красные, а CI зелёный
В отчёте k6 есть неуспешные проверки ответов, но шаг CI завершился успешно. Прежде чем искать ошибку в пайплайне, посмотрите на thresholds.
Например, в скрипт с checks можно добавить такой учебный порог:
Он требует больше 90% успешных checks. Ровно 90% уже не проходит. Если хотя бы один threshold нарушен, k6 завершает прогон с ненулевым кодом. Шаг CI должен учитывать этот код.
Число 90% здесь показывает синтаксис, а не рекомендуемую планку качества. Порог выбирают из требований продукта. То же относится к
Порог не задаёт профиль нагрузки. Зелёный результат одного прогона не доказывает, что сервис выдержит любое число пользователей.
Checks и thresholds в документации k6
#тест_драйв
В отчёте k6 есть неуспешные проверки ответов, но шаг CI завершился успешно. Прежде чем искать ошибку в пайплайне, посмотрите на thresholds.
check фиксирует результат проверки. Сам по себе он не меняет код завершения k6. Критерий провала всего прогона задаёт threshold по метрике.Например, в скрипт с checks можно добавить такой учебный порог:
export const options = {
thresholds: {
checks: ['rate>0.9'],
},
}Он требует больше 90% успешных checks. Ровно 90% уже не проходит. Если хотя бы один threshold нарушен, k6 завершает прогон с ненулевым кодом. Шаг CI должен учитывать этот код.
Число 90% здесь показывает синтаксис, а не рекомендуемую планку качества. Порог выбирают из требований продукта. То же относится к
http_req_duration: ['p(95)<200']: это пример ограничения 95-го перцентиля времени запросов в миллисекундах.Порог не задаёт профиль нагрузки. Зелёный результат одного прогона не доказывает, что сервис выдержит любое число пользователей.
Checks и thresholds в документации k6
#тест_драйв
❤3
До 31 октября в Proglib Academy можно выбрать два курса и заплатить только за один.
Например, можно совместить:
💥 ИИ для разработчиков — применение нейросетей в повседневной разработке: работа с кодом, тестирование и ревью.💥 Разработка ИИ-агентов — архитектура агентов и создание собственных AI-решений.
Вместе курсы стоят 108 000 ₽, но по акции оба обойдутся в 59 000 ₽. Экономия — 49 000 ₽
Интересуют Python и ИИ или алгоритмы и архитектура? Можно выбрать любые два курса из каталога.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Первый тест ждал 503, второй сразу получил 200
Учебный мок WireMock устроен так: первый запрос возвращает 503 и переводит сценарий из
Один тест прошёл эту цепочку. Следующий ждёт исходный 503, но получает 200. В такой ситуации проверьте, не осталось ли состояние от предыдущего теста.
В JSON поле
Перед повтором сценария
— Посмотрите текущее состояние через
— На изолированном тестовом экземпляре пустой
— Для одного сценария, например
Общий reset может сбросить состояние соседнего параллельного теста. Поэтому для параллельных прогонов нужна изоляция экземпляров или сценариев, а не только сброс «перед каждым тестом».
Цепочка 503 → 200 проверяет поведение клиента на заданных ответах мока. Восстановление настоящего внешнего API она не подтверждает.
Состояния и сброс сценариев WireMock
#теория_на_пальцах
Учебный мок WireMock устроен так: первый запрос возвращает 503 и переводит сценарий из
Started в Recovered. Следующие запросы в этом состоянии получают 200.Один тест прошёл эту цепочку. Следующий ждёт исходный 503, но получает 200. В такой ситуации проверьте, не осталось ли состояние от предыдущего теста.
В JSON поле
requiredScenarioState задаёт состояние, в котором подходит заглушка. newScenarioState переводит сценарий дальше. Само начало нового теста не означает возврата к Started.Перед повтором сценария
— Посмотрите текущее состояние через
GET /__admin/scenarios.— На изолированном тестовом экземпляре пустой
POST /__admin/scenarios/reset сбрасывает все сценарии.— Для одного сценария, например
retry, используйте пустой PUT /__admin/scenarios/retry/state.Общий reset может сбросить состояние соседнего параллельного теста. Поэтому для параллельных прогонов нужна изоляция экземпляров или сценариев, а не только сброс «перед каждым тестом».
Цепочка 503 → 200 проверяет поведение клиента на заданных ответах мока. Восстановление настоящего внешнего API она не подтверждает.
Состояния и сброс сценариев WireMock
#теория_на_пальцах
👍1
Горизонтальный скролл при увеличении: когда это дефект
Страница при 100% выглядит аккуратно. Увеличиваете масштаб — и для чтения каждой строки приходится двигать её вправо и влево. Для обычного вертикального контента это повод проверить reflow.
Критерий WCAG 2.2 1.4.10, уровень AA требует при ширине, эквивалентной 320 CSS px, сохранять информацию и функции без прокрутки в двух направлениях. Для частей, которым по смыслу нужна двумерная компоновка, есть исключения.
Что проверить
— Доведите CSS-ширину viewport до 320 px. Один из вариантов — исходный viewport 1280 CSS px и browser zoom 400%. Сверяйте фактическую ширину, а не разрешение экрана.
— Прочитайте текст, заполните форму, доберитесь до кнопок. Ничего не должно теряться из-за нового расположения блоков.
— Если на странице таблица или карта, определите границы исключения. Горизонтальная прокрутка такой части сама по себе ещё не доказывает нарушение.
Ловушка с таблицей
Исключение не распространяется на её заголовок, поиск и пагинацию. Обычный текст внутри ячеек тоже должен перестраиваться, если ему не нужна двумерная компоновка.
Pinch zoom и увеличение только текста не равнозначны browser zoom. Этот чек-лист проверяет один критерий и не подтверждает соответствие всему WCAG.
Официальное объяснение Reflow от W3C
#теория_на_пальцах
Страница при 100% выглядит аккуратно. Увеличиваете масштаб — и для чтения каждой строки приходится двигать её вправо и влево. Для обычного вертикального контента это повод проверить reflow.
Критерий WCAG 2.2 1.4.10, уровень AA требует при ширине, эквивалентной 320 CSS px, сохранять информацию и функции без прокрутки в двух направлениях. Для частей, которым по смыслу нужна двумерная компоновка, есть исключения.
Что проверить
— Доведите CSS-ширину viewport до 320 px. Один из вариантов — исходный viewport 1280 CSS px и browser zoom 400%. Сверяйте фактическую ширину, а не разрешение экрана.
— Прочитайте текст, заполните форму, доберитесь до кнопок. Ничего не должно теряться из-за нового расположения блоков.
— Если на странице таблица или карта, определите границы исключения. Горизонтальная прокрутка такой части сама по себе ещё не доказывает нарушение.
Ловушка с таблицей
Исключение не распространяется на её заголовок, поиск и пагинацию. Обычный текст внутри ячеек тоже должен перестраиваться, если ему не нужна двумерная компоновка.
Pinch zoom и увеличение только текста не равнозначны browser zoom. Этот чек-лист проверяет один критерий и не подтверждает соответствие всему WCAG.
Официальное объяснение Reflow от W3C
#теория_на_пальцах
❤2
Warning тоже можно проверить тестом
Функция обработала ошибку, но забыла записать предупреждение. Если такой лог нужен для диагностики, одного правильного результата функции мало.
В pytest фикстура
Проверка наличия нужной тройки допускает другие записи. Сравнение всего вывода целиком сделало бы тест зависимым от посторонних логов.
Если захват пуст, проверьте настройку logging:
Это учебный logger. Показанный тест и проверка отсутствия записи без вызова функции прошли на pytest 9.0.3:
Работа с логами в pytest
#тест_драйв
Функция обработала ошибку, но забыла записать предупреждение. Если такой лог нужен для диагностики, одного правильного результата функции мало.
В pytest фикстура
caplog даёт доступ к записям логов. В record_tuples каждая запись — это имя logger, числовой уровень и уже сформированное сообщение.import logging
def reject_payment():
logging.getLogger('payments').warning('payment rejected: %s', 'timeout')
def test_warning_is_contract(caplog):
with caplog.at_level(logging.WARNING, logger='payments'):
reject_payment()
assert ('payments', logging.WARNING, 'payment rejected: timeout') in caplog.record_tuples
Проверка наличия нужной тройки допускает другие записи. Сравнение всего вывода целиком сделало бы тест зависимым от посторонних логов.
at_level меняет уровень временно, в пределах блока.Если захват пуст, проверьте настройку logging:
dictConfig может удалить обработчик caplog с root logger. Для записей из другой стадии теста используйте get_records('setup') или соответствующее имя стадии.Это учебный logger. Показанный тест и проверка отсутствия записи без вызова функции прошли на pytest 9.0.3:
2 passed. Реальная платёжная система здесь не тестировалась.Работа с логами в pytest
#тест_драйв
❤2👍1
В Proglib Academy действует акция: покупаешь один курс, а второй получаешь в подарок.
Например, можно совместить два направления по работе с ИИ:
💥 ИИ для разработчиков — применение нейросетей в повседневной разработке: работа с кодом, тестирование и ревью.💥 Разработка ИИ-агентов — архитектура агентов и создание собственных AI-решений.
А если тебе интереснее Python, алгоритмы, backend или DevOps — выбирай другую пару курсов из каталога.
Акция действует до 31 октября включительно
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Данные в UI есть, а page.route молчит
Мок Playwright не срабатывает, хотя страница показывает данные. Один из возможных участников цепочки — Service Worker. В документации Playwright отдельно разобран случай с MSW: его Service Worker забирает запросы, и они становятся невидимыми для
Для теста, которому нужен перехват средствами Playwright, документация предлагает блокировать Service Workers. Настройка Playwright Test:
Перехват задавайте до действия, которое отправляет запрос. Если при заблокированном SW обработчик начал получать запросы, это повод разобраться с маршрутом запроса в исходной среде. Само по себе это не диагноз «баг приложения».
У такой проверки есть цена: среда изменилась. Прогон с
Почему Service Worker влияет на перехват
#тест_драйв
Мок Playwright не срабатывает, хотя страница показывает данные. Один из возможных участников цепочки — Service Worker. В документации Playwright отдельно разобран случай с MSW: его Service Worker забирает запросы, и они становятся невидимыми для
page.route() и browserContext.route().Для теста, которому нужен перехват средствами Playwright, документация предлагает блокировать Service Workers. Настройка Playwright Test:
import { defineConfig } from '@playwright/test'
export default defineConfig({
use: { serviceWorkers: 'block' },
})Перехват задавайте до действия, которое отправляет запрос. Если при заблокированном SW обработчик начал получать запросы, это повод разобраться с маршрутом запроса в исходной среде. Само по себе это не диагноз «баг приложения».
У такой проверки есть цена: среда изменилась. Прогон с
serviceWorkers: 'block' не проверяет настоящий путь через SW, его кеш и offline-поведение. Для них нужен отдельный сценарий со включённым Service Worker.Почему Service Worker влияет на перехват
#тест_драйв
❤1
202 Accepted: выгрузка ещё не готова
Сервис экспорта ответил 202. Проверка только кода ответа пропустит задачу, которая позже упадёт.
202 означает, что запрос принят, но обработка ещё не завершена и могла даже не начаться. Успех обработки он не гарантирует.
Для такого API проверьте отдельно приём запроса, конечный статус задачи и итоговый файл: доступен ли он и те ли данные внутри. Проверьте и случай, когда задача завершилась ошибкой.
В примере MDN ответ содержит taskId и monitorUrl для проверки статуса. Эти поля задаёт контракт конкретного приложения, они не обязательны для любого ответа 202. Дедлайн ожидания тоже берите из контракта API.
202 в MDN
Навигация: Вакансии
Библиотека тестировщика
#теория_на_пальцах
Сервис экспорта ответил 202. Проверка только кода ответа пропустит задачу, которая позже упадёт.
202 означает, что запрос принят, но обработка ещё не завершена и могла даже не начаться. Успех обработки он не гарантирует.
Для такого API проверьте отдельно приём запроса, конечный статус задачи и итоговый файл: доступен ли он и те ли данные внутри. Проверьте и случай, когда задача завершилась ошибкой.
В примере MDN ответ содержит taskId и monitorUrl для проверки статуса. Эти поля задаёт контракт конкретного приложения, они не обязательны для любого ответа 202. Дедлайн ожидания тоже берите из контракта API.
202 в MDN
Навигация: Вакансии
Библиотека тестировщика
#теория_на_пальцах
Один список на два теста в pytest
Если первый тест добавит элемент в общий список, следующий получит его уже изменённым, даже если вы ожидали пустой список.
pytest передаёт параметры как есть, без копирования. Если два случая ссылаются на один list или dict, мутация в одном вызове будет видна следующему. Два отдельно созданных списка с одинаковыми значениями остаются разными объектами.
Сравните результаты запуска одного случая и всего набора и проверьте, откуда берётся объект. Для независимых данных создавайте новую структуру на каждый вызов через fixture или factory. Обычная поверхностная копия не изолирует вложенные списки.
На схеме один и тот же объект передан двум случаям. Это пример связи объектов, не результат прогона.
Правило pytest
Навигация: Вакансии
Библиотека тестировщика
#тест_драйв
Если первый тест добавит элемент в общий список, следующий получит его уже изменённым, даже если вы ожидали пустой список.
pytest передаёт параметры как есть, без копирования. Если два случая ссылаются на один list или dict, мутация в одном вызове будет видна следующему. Два отдельно созданных списка с одинаковыми значениями остаются разными объектами.
Сравните результаты запуска одного случая и всего набора и проверьте, откуда берётся объект. Для независимых данных создавайте новую структуру на каждый вызов через fixture или factory. Обычная поверхностная копия не изолирует вложенные списки.
На схеме один и тот же объект передан двум случаям. Это пример связи объектов, не результат прогона.
Правило pytest
Навигация: Вакансии
Библиотека тестировщика
#тест_драйв
Контейнер running, а сервис ещё отвечает 503
В интеграционном тесте контейнер уже запущен, TCP-порт открыт, а нужный HTTP-запрос всё ещё получает 503. Чего ждать перед тестом?
В Testcontainers for Java startup strategy проверяет состояние запуска. Wait strategy проверяет готовность к работе теста. У GenericContainer базовое ожидание — пока первый mapped port не начнёт слушать.
Если тесту нужен готовый HTTP-сервис, дождитесь ответа его endpoint с оговорённым статусом. Путь /health и код 200 в схеме — условия учебного сервиса. Ответ 200 говорит только о том, что проверяет этот endpoint.
Добавьте проверку: порт открыт, endpoint ещё не готов. Проверьте timeout и диагностику. Значения по умолчанию Java GenericContainer не стоит переносить на все языки и модули.
Startup и wait strategies
Навигация: Вакансии
Библиотека тестировщика
#теория_на_пальцах
В интеграционном тесте контейнер уже запущен, TCP-порт открыт, а нужный HTTP-запрос всё ещё получает 503. Чего ждать перед тестом?
В Testcontainers for Java startup strategy проверяет состояние запуска. Wait strategy проверяет готовность к работе теста. У GenericContainer базовое ожидание — пока первый mapped port не начнёт слушать.
Если тесту нужен готовый HTTP-сервис, дождитесь ответа его endpoint с оговорённым статусом. Путь /health и код 200 в схеме — условия учебного сервиса. Ответ 200 говорит только о том, что проверяет этот endpoint.
Добавьте проверку: порт открыт, endpoint ещё не готов. Проверьте timeout и диагностику. Значения по умолчанию Java GenericContainer не стоит переносить на все языки и модули.
Startup и wait strategies
Навигация: Вакансии
Библиотека тестировщика
#теория_на_пальцах