Тест-дизайн
Определить весь процесс тестирования приложения – задача сложная даже для опытного тестировщика. В этом вопросе не существует четких правил, которые подскажут, как тестировать, какие тесты выполнять и в каких ситуациях. Чтобы справиться с этой неопределенностью, тестировщики придумали множество приемов, которые помогают решать эти вопросы и дают подсказки, помогающие направить тестирование в правильное русло и сократить сроки тестирования.
Тест-дизайн – это этап тестирования ПО. На нем проектируются и создаются тест-кейсы, которые будут соответствовать определенным заранее критериям качества и целям тестирования. Цель тест-дизайна — создать наборы тестовых случаев, обеспечивающих оптимальное тестовое покрытие.
Разработка тестов начинается после проведения исследования ПО, когда цели определены, а критерии тестирования заданы и выполняются.
Техники тест-дизайна
Техники тест-дизайна помогают:
- Исключить непродуктивные тест-кейсы и сократить общее количество кейсов
- Покрыть тестами как можно больше функциональности
- Провести все тесты и не пропустить ничего важного
Для работы с кодом (white-box) важны такие аспекты:
- Покрытие операторов
- Покрытие условий
- Покрытие путей
- Покрытие функций
- Покрытие вход/выход
- Покрытие значений параметров
В работе с требованиями (black-box) тестирование проходит иначе:
- Классы эквивалентности
- Граничные значения
- Попарное тестирование
- Таблица принятия решений
- Диаграмма состояний и переходов
- Тестирование вариантов использования
- Доменное тестирование.
Техники тест-дизайна на основании требований
Классы эквивалентности
Техника классов эквивалентности – это разделение диапазона возможных вводимых значений на группы эквивалентных по своему влиянию на систему. Эта техника помогает не только сокращать количество тестов, но и сохранять приемлемое тестовое покрытие.
Рассмотрим для примера перевод денег в банке. Размер комиссии зависит от суммы перевода:
- от 1 до 999 долларов включительно – 0%
- от 1000 до 4999 долларов включительно – 5%
- от 5000 долларов – 7%
Максимальная сумма перевода – 100000 долларов, при этом дробные числа не учитываются.
Попробуем выяснить, сколько требований будет в этом случае. В этом помогут классы эквивалентности:
- 1-999 => ожидаем комиссию 0%
- 1000-4999 => ожидаем комиссию 5%
- 5000-100000 => ожидаем комиссию 7%
Выбранные значения для проверки: 500, 2500, 7500.
- На значение 500 система отреагирует так же, как и на любое другое значение из первого диапазона
- На значение 2500 система отреагирует так же, как и на любое другое значение из второго диапазона
- На значение 7500 система отреагирует так же, как и на любое другое значение из третьего диапазона
Граничные значения
Граничные значения – это значения, в которых один класс эквивалентности переходит в другой. По своей сути это техника, которая дополняет технику классов эквивалентности.
Важно проверять граничные значения, потому что именно на границах чаще всего допускаются ошибки при написании кода и формулировании требований.
Например, неопытный программист при постановке «не больше 1000» может поставить значение <1000.
Вернемся к примеру с комиссией:
- от 1 до 999 долларов включительно – 0%
- от 1000 до 4999 долларов включительно – 5%
- от 5000 долларов – 7%
Максимальная сумма перевода – 100000 долларов, при этом дробные числа не учитываются. Вспомним классы эквивалентности:
- 1-999 => ожидаем комиссию 0%
- 1000-4999 => ожидаем комиссию 5%
- 5000-100000 => ожидаем комиссию 7%
Граничные значения: 1, 999, 1000, 4999, 5000, 100000.
С учетом классов эквивалентности и граничных значений выбираем значения для проверки:
1, 500, 999, 1000, 2500, 4999, 5000, 7500, 100000.
Попарное тестирование (pairwise)
Попарное тестирование – техника тест-дизайна, при которой тест-кейсы создаются так, чтобы выполнить все возможные отдельные комбинации каждой пары входных параметров.
Достаточно проверить комбинации пар входных параметров, потому что ошибки чаще всего находятся именно на перекрестке двух параметров. Исключения бывают, но они достаточно редкие.
Рассмотрим пример. Возьмем наушники с такими характеристиками:
- Тип подключения (беспроводной или проводной)
- Микрофон (включен или выключен)
- Подсветка (включена или выключена)
Полный перебор даст нам восемь тест-кейсов. Это 2 в третьей степени, потому что у каждого из трех параметров может быть всего два значения:
Как правило, подобных характеристик в реальном мире куда больше, что неизбежно увеличивает количество тест-кейсов.
При попарном тестировании достаточно проверить лишь пары значений. При успешном выполнении тестов на 97% мы можем быть уверены, что проверяемая функциональность работает корректно.
- 1 нет, нет, нет -> нет, нет, нет -> нет, нет, нет (00, 00, 00)
- 2 да, да, нет ->да, да, нет -> да, да, нет (11, 10, 10)
- 3 нет, да, да -> нет, да, да -> нет, да, да (01, 11, 01)
- 4 да, нет, да -> да, нет, да -> да, нет, да (10, 01, 11)
При трех параметрах, каждый из которых имеет 3 значения, количество вариантов полного перебора – 27 (три в третьей)
Применив pairwise, количество тест-кейсов сведётся к 9.
Таблица принятия решений
Таблица решений или матрица решений — способ компактного представления модели со сложной логикой; инструмент для упорядочения сложных бизнес требований, которые должны быть реализованы в продукте.
Это взаимосвязь между множеством условий и действий.
Таблица принятия решений содержит следующие элементы:
- Условия — список возможных условий
- Варианты — комбинация из выполнения и/или невыполнения условий этого списка
- Действия — список возможных действий (вариантов исхода)
Например, кредит выдаётся людям, удовлетворяющим трём условиям:
- Возраст: 18-60 лет
- Гражданство: Россия
- Стаж работы: более 5 лет ИЛИ средняя месячная зарплата за год больше 100 тысяч рублей
Диаграмма состояний и переходов
Таблица переходов представляет собой все возможные комбинации начальных и конечных состояний. Она включает в себя действительные и недействительные переходы, инициирующие события, защитные условия и результирующие действия.
Диаграммы состояний и переходов показывают только действительные переходы и исключают недействительные переходы.
Состояние А ——переход—–> Состояние Б
Тестирование вариантов использования
Тестирование вариантов использования определяется как метод тестирования программного обеспечения, который помогает идентифицировать тестовые случаи, охватывающие всю систему, от транзакции к транзакции от начала до конечной точки.
Вариант использования или юз-кейс – это описание конкретного использования системы субъектом или пользователем
Юз-кейсы содержат следующие сведения:
- Кто использует сайт или приложение
- Что пользователь хочет сделать
- Какие шаги делает пользователь, чтобы совершить определенное действие
- Как сайт или приложение реагируют на действия пользователя
Доменное тестирование
Доменное тестирование (domain analysis) — методика разработки тестов, использующаяся для определения действенных и эффективных тестовых сценариев в случаях, когда множественные параметры могут или должны быть протестированы одновременно.
Доменное тестирование применяется для сокращения количества проводимых тестов без потери качества тестирования.
Очень часто классы эквивалентности, относящиеся к позитивным проверкам, можно проверять совместно.
Возьмем для примера такие требования:
- Размер файла: до 200 МБ
- Имя файла: от 5 до 24 символов, только латиница
- Форматы файлов: только изображения
В этом случае нужны такие проверки:
- Загрузить файл менее 200 МБ
- Загрузить файл с именем hexlet
- Загрузить файл с расширением .jpg
Можно заменить одной проверкой:
- Загрузить файл менее 200 МБ, с именем hexlet.jpg
Обратите внимание, что с негативными проверками так делать нельзя. При этом некоторые негативные проверки также можно исключать за счет несовместимости значений двух параметров.
Например, не нужно проверять конфигурацию компьютера с комплектующими, которые никогда не будут поставляться вместе (слабый процессор и сильная видеокарта). Также не нужно проверять конфигурацию автомобиля с агрегатами, которые никогда не будут поставляться вместе (дизельный мотор + турбонаддув).
Часто такая же ситуация может возникнуть при тестировании фильтров в интернет-магазинах. Часто параметр2 имеет строго определенное значение при выборе параметра1, поэтому проверять все возможные значения параметра2 в паре с параметром1 – смысла не имеет.
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 17:12:01 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="190L0cGtuMcIEMEODnwp0X2ltZXnvu0u2opXOLti49k4DMDmM9MVp75T5ZYCc9mmvayYP--JE4xnas1s6WUEtw";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Техники тест-дизайна | Рабочий процесс тестировщика</title>
<meta name="description" content="Техники тест-дизайна / Рабочий процесс тестировщика: Изучаем классы эквивалентности и попарное тестирование">
<link rel="canonical" href="https://ru.hexlet.io/courses/qa-engineer-workflow/lessons/test-design-techniques/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Техники тест-дизайна">
<meta property="og:title" content="Рабочий процесс тестировщика">
<meta property="og:description" content="Техники тест-дизайна / Рабочий процесс тестировщика: Изучаем классы эквивалентности и попарное тестирование">
<meta property="og:url" content="https://ru.hexlet.io/courses/qa-engineer-workflow/lessons/test-design-techniques/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="H-5HhPG0AIygOzYfgTTwO-FXT0JmmWJysLjw6dWEeKrwP4yzA8qt7BZ4EoeNOwBMIV5i6G6unNANWGq9h4OfxA" />
<script src="/vite/assets/inertia-INZxX8jp.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-nkZBEvfU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-6pOtQ3OW.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk1MCwicHVyIjoiYmxvYl9pZCJ9fQ==--4a16fe638654fb8d5ae09d7e8ab8e16ff228214f/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-amico.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T17:12:01.195Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"biAJt0vGk3coAsCCCcOOw7Iw7EQjI31B9zR7xuGcXReB8cKAubg-F55B5BoFzH60cjnB7isUg-NK1OGSs5u6eQ","topics":[{"id":80657,"title":"Добрый день,\nвопрос относительно упражнения к уроку \"Техники Тест-дизайна\" https://ru.hexlet.io/courses/qa-engineer-workflow/lessons/test-design-techniques/exercise_unit\n\nВ указанном правильном ответе содержится всего 3 проверки Имени и Фамилии:\n *Ivan\n *iVan ivanov\n *Ivan1 Ivanov\n - хотя требований относительно Имени и Фамилии пять:\n1)Поле \"Имя Фамилия\" должно состоять из двух слов, разделенных пробелом\n2) Имя должно начинаться с заглавной буквы\n3) Имя не должно содержать цифр\n4) Фамилия должна начинаться с заглавной буквы\n5) Фамилия не должна содержать цифры\n\nВведя в поле значение \" iVan ivanov\", мы проверяем требования №2 и №4. Но какое именно из них не выполняется, мы не знаем. Корректно ли это?\n\nВторой вопрос - относительно корректных e-mail-ов. Требование относительно них только одно: \n1)Проверяется e-mail на корректность.\n\n Значения, указанные в правильном ответе:\n \"\"\n @gmail.com\n test@test\n test@\nНепонятно, зачем перебирать так много вариантов? Есть какие-то специальные правила относительно корректности e-mail-адресов?\n\nЗаранее спасибо за ответ!\n\n\n","plain_title":"Добрый день, вопрос относительно упражнения к уроку \"Техники Тест-дизайна\" https://ru.hexlet.io/courses/qa-engineer-workflow/lessons/test-design-techniques/exercise_unit В указанном правильном ответе содержится всего 3 проверки Имени и Фамилии: *Ivan *iVan ivanov *Ivan1 Ivanov - хотя требований относительно Имени и Фамилии пять: 1)Поле \"Имя Фамилия\" должно состоять из двух слов, разделенных пробелом 2) Имя должно начинаться с заглавной буквы 3) Имя не должно содержать цифр 4) Фамилия должна начинаться с заглавной буквы 5) Фамилия не должна содержать цифры Введя в поле значение \" iVan ivanov\", мы проверяем требования №2 и №4. Но какое именно из них не выполняется, мы не знаем. Корректно ли это? Второй вопрос - относительно корректных e-mail-ов. Требование относительно них только одно: 1)Проверяется e-mail на корректность. Значения, указанные в правильном ответе: \"\" @gmail.com test@test test@ Непонятно, зачем перебирать так много вариантов? Есть какие-то специальные правила относительно корректности e-mail-адресов? Заранее спасибо за ответ! ","creator":{"public_name":"Алена Супикова","id":533742,"is_tutor":false},"comments":[{"creator":{"public_name":"Алена Супикова","id":533742,"is_tutor":false},"id":165321,"body":"Спасибо! ","topic_id":80657},{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":165312,"body":"**user-57cba041ddd4ad1f**, здравствуйте. Спасибо за фидбек. Я поправил задание. Вы правы, не по всем тест-кейсам понятно какое требование не соблюдается - это как раз поправил. По поводу емейла. Любой емейл состоит из двух частей: имя пользователя и доменное имя, они разделяются @. при этом доменное имя делится еще на две части. В итоге можно сказать, что емейл состоит из трех частей, которые и проверяются в этих тест-кейсах. ","topic_id":80657}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":103026,"title":"Добрый день, в задании есть \"Данные хранятся в yaml-формате\", прошу разъяснить как увидеть эти данные и где, в растерянности ","plain_title":"Добрый день, в задании есть \"Данные хранятся в yaml-формате\", прошу разъяснить как увидеть эти данные и где, в растерянности ","creator":{"public_name":"Роман Иваненков","id":867966,"is_tutor":false},"comments":[{"creator":{"public_name":"Роман Иваненков","id":867966,"is_tutor":false},"id":195393,"body":"Добрый день, разобрался","topic_id":103026}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":87587,"title":"Подскажите пожалуйста, сколько будет классов эквивалентности для пароля и каких? И вообще, в чем суть тестирования с помощью классов эквивалентности? Для себя я понял так: \"выявив классы, я составляю по ним тест-кейсы и убираю избыточные, т.е., взаимоисключающие\". \nПравильно или не совсем?\n","plain_title":"Подскажите пожалуйста, сколько будет классов эквивалентности для пароля и каких? И вообще, в чем суть тестирования с помощью классов эквивалентности? Для себя я понял так: \"выявив классы, я составляю по ним тест-кейсы и убираю избыточные, т.е., взаимоисключающие\". Правильно или не совсем? ","creator":{"public_name":"Пётр Гладких","id":340198,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":175426,"body":"**Пётр Гладких**, все верно. Вы можете составить критерии, которые должны проверяться, и затем убрать дублирующие кейсы.","topic_id":87587}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":82077,"title":"Доброго времени суток! Не могу понять, почему не проходит тест.. Мб до меня не доходит в какой форме его необходимо записать? Я правильно сделал, если просто скопировал файл \"test-cases.yml\" и удалил лишние данные для проверки? Или же в каком-то другом виде надо указывать?\nhttps://ru.hexlet.io/code_reviews/862695","plain_title":"Доброго времени суток! Не могу понять, почему не проходит тест.. Мб до меня не доходит в какой форме его необходимо записать? Я правильно сделал, если просто скопировал файл \"test-cases.yml\" и удалил лишние данные для проверки? Или же в каком-то другом виде надо указывать? https://ru.hexlet.io/code_reviews/862695 ","creator":{"public_name":"Алексей Филин","id":544993,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":167417,"body":"**Alexey Filin**, здравствуйте. В вашем решении нарушен синтаксис yaml в этом месте:\n\n```\nemail:\n \"\"\n```\n\nПустая строка \"\" относится к списку, поэтому перед ней должен стоять дефис.\n\nПомните, что вы учитесь в группе, и там за вами закреплен наставник. Вы можете обращаться к нему по любым вопросам связанным с решением задач.","topic_id":82077}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":95632,"title":"Добрый день.\nУ меня не проходит тест, по причине неправильного синтаксиса (если я правильно поняла) https://ru.hexlet.io/code_reviews/1262040\nВопрос: как правильно переностить скопированные тест кейсы в решение?","plain_title":"Добрый день. У меня не проходит тест, по причине неправильного синтаксиса (если я правильно поняла) https://ru.hexlet.io/code_reviews/1262040 Вопрос: как правильно переностить скопированные тест кейсы в решение? ","creator":{"public_name":"Svetlana Moiseenko","id":471668,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikolai Gagarinov","id":104929,"is_tutor":true},"id":186401,"body":"Добрый день.\n\nУ вас только некорректные тест-кейсы не хватает корректных тесткейсов `correct`","topic_id":95632}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":79791,"title":"Здравствуйте! Я тесты прошел, но почему то не пускает. Это баг? \nhttps://ru.hexlet.io/code_reviews/810040","plain_title":"Здравствуйте! Я тесты прошел, но почему то не пускает. Это баг? https://ru.hexlet.io/code_reviews/810040 ","creator":{"public_name":"Антон Морозов","id":536232,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":164078,"body":"Приветствую! Обратите внимание, что в выводе тестов в самом конце есть строка: Expected tests to fail, but they passed. See output above. То есть ожидалось, что тесты упадут, но они прошли. Поэтому упражнение и не засчитывается.","topic_id":79791}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":92833,"title":"Здравствуйте! Не понимаю, в какой директории нужно создать файл solution.yml ?\nПо всей системе я его не нашёл, в директории, в которой находишься при старте упражнения, нет возможности создать файл.","plain_title":"Здравствуйте! Не понимаю, в какой директории нужно создать файл solution.yml ? По всей системе я его не нашёл, в директории, в которой находишься при старте упражнения, нет возможности создать файл. ","creator":{"public_name":"Forest gnome Черненко","id":405868,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":182656,"body":"**Forest gnome**, здравствуйте! Создавать файл не нужно. В редакторе, в верхней части, должны быть вкладки, где вы можете переключиться на этот файл. В левой части, в файловом дереве, он тоже должен быть. Можете сделать скрин, загрузить его куда-нибудь и выслать на него ссылку? ","topic_id":92833},{"creator":{"public_name":"Forest gnome Черненко","id":405868,"is_tutor":false},"id":182687,"body":"**Ivan Gagarinov**, прикладываю ссылку на скриншот.\n\nhttps://disk.yandex.ru/i/Fs1YOk4_sEriCw\n\nДанный файл отсутствует.","topic_id":92833},{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":182690,"body":"**Forest gnome**, а сброс не пробовали сделать? Еще вопрос: это так только в этом упражнении? В остальных нормально? ","topic_id":92833},{"creator":{"public_name":"Forest gnome Черненко","id":405868,"is_tutor":false},"id":182691,"body":"Сброс упражнения помог. В других упражнениях проблемы не наблюдал.\nБлагодарю!\n","topic_id":92833}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":93874,"title":"не принимается решение, уже даже посмотрел подсказку все идентично, в чем моя ошибка?","plain_title":"не принимается решение, уже даже посмотрел подсказку все идентично, в чем моя ошибка? ","creator":{"public_name":"Давид Лабадзе","id":711160,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikolai Gagarinov","id":104929,"is_tutor":true},"id":184088,"body":"Давид, добрый день.\n\nЧтобы помочь разобраться, необходимо смотреть решение. Вы можете опубликовать его, нажав кнопку \"сохранить решение\", чтобы не спойлерить тем, кто его еще не прошел.\nhttps://help.hexlet.io/ru/articles/111490-ne-mogu-vypolnit-praktiku","topic_id":93874}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":94206,"title":"Добрый день, \nПохоже, что проверки по некоторым полям выполняются некорректно.\n\nВ моем решении есть:\n\n`correct:`\n` ...`\n \n`email:`\n \n`- |`\n`test@test` \n(тест прошел т.е. этот вариант воспринимается как корректный и если в incorrect записать, то тесты падают)\n\nи требование \"Фамилия не должна содержать цифры\" не является обязательным при проверке, пробовала проверить решение с Ivan Ivan2v и без.\n\nhttps://ru.hexlet.io/code_reviews/1212930","plain_title":"Добрый день, Похоже, что проверки по некоторым полям выполняются некорректно. В моем решении есть: correct: ... email: - | test@test (тест прошел т.е. этот вариант воспринимается как корректный и если в incorrect записать, то тесты падают) и требование \"Фамилия не должна содержать цифры\" не является обязательным при проверке, пробовала проверить решение с Ivan Ivan2v и без. https://ru.hexlet.io/code_reviews/1212930 ","creator":{"public_name":"Юлия","id":696788,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":184626,"body":"**Юлия**, здравствуйте.\n\n> test@test (тест прошел т.е. этот вариант воспринимается как корректный и если в incorrect записать, то тесты падают)\n\nЭто корректный адрес, такие адреса допустимы и могут существовать, хоть и редко встречаются.\n\nПо поводу второго замечания, тут уже требуется доработка. Спасибо, что обратили на это внимание. Я заведу тикет на это.","topic_id":94206}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}},{"id":97500,"title":"Добрый день. Не проходит тест, решение правильное, даже сверился с подсказками. В чем может быть проблема?\nhttps://ru.hexlet.io/code_reviews/1333518","plain_title":"Добрый день. Не проходит тест, решение правильное, даже сверился с подсказками. В чем может быть проблема? https://ru.hexlet.io/code_reviews/1333518 ","creator":{"public_name":"Василий","id":706823,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":188540,"body":"**Василий**, здравствуйте! Я проверил на вашем решении, тест проходит. Попробуйте сделать сброс еще раз проверить.","topic_id":97500}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Техники тест-дизайна","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":2168,"slug":"testing_workflow_test_design_techniques_exercise","name":null,"state":"active","kind":"exercise","language":"javascript","locale":"ru","has_web_view":true,"has_test_view":false,"reviewable":true,"readme":"В этом упражнении вам нужно протестировать форму регистрации, но сократить количество тест кейсов.\n\nФорма должна соответствовать нескольким требованиям:\n\n* Проверяется e-mail на корректность\n* Пароль должен быть не меньше шести символов\n* Пароль должен содержать хотя бы одну заглавную букву\n* Пароль должен содержать хотя бы один символ из `+-*$`\n* Поле \"Имя Фамилия\" должно состоять из двух слов, разделенных пробелом\n* Имя должно начинаться с заглавной буквы\n* Имя не должно содержать цифр\n* Фамилия должна начинаться с заглавной буквы\n* Фамилия не должна содержать цифры\n\n### test-cases.yml\n\nПросмотрите тест-кейсы. Данные хранятся в yaml-формате. Сократите количество тестируемых данных и скопируйте получившееся решение в файл __solution.yml__\n","prepared_readme":"В этом упражнении вам нужно протестировать форму регистрации, но сократить количество тест кейсов.\n\nФорма должна соответствовать нескольким требованиям:\n\n* Проверяется e-mail на корректность\n* Пароль должен быть не меньше шести символов\n* Пароль должен содержать хотя бы одну заглавную букву\n* Пароль должен содержать хотя бы один символ из `+-*$`\n* Поле \"Имя Фамилия\" должно состоять из двух слов, разделенных пробелом\n* Имя должно начинаться с заглавной буквы\n* Имя не должно содержать цифр\n* Фамилия должна начинаться с заглавной буквы\n* Фамилия не должна содержать цифры\n\n### test-cases.yml\n\nПросмотрите тест-кейсы. Данные хранятся в yaml-формате. Сократите количество тестируемых данных и скопируйте получившееся решение в файл __solution.yml__\n","has_solution":true,"entity_name":"Техники тест-дизайна"},"units":[{"id":7321,"name":"theory","url":"/courses/qa-engineer-workflow/lessons/test-design-techniques/theory_unit"},{"id":9664,"name":"quiz","url":"/courses/qa-engineer-workflow/lessons/test-design-techniques/quiz_unit"},{"id":7429,"name":"exercise","url":"/courses/qa-engineer-workflow/lessons/test-design-techniques/exercise_unit"}],"links":[{"id":424828,"name":"Pairwise Pict Online","url":"https://pairwise.yuuniworks.com/\n"},{"id":424829,"name":"Pairwise Tool","url":"https://pairwise.teremokgames.com/\n"},{"id":424830,"name":"Pict","url":"https://github.com/microsoft/pict/blob/main/doc/pict.md\n"}],"ordered_units":[{"id":7321,"name":"theory","url":"/courses/qa-engineer-workflow/lessons/test-design-techniques/theory_unit"},{"id":9664,"name":"quiz","url":"/courses/qa-engineer-workflow/lessons/test-design-techniques/quiz_unit"},{"id":7429,"name":"exercise","url":"/courses/qa-engineer-workflow/lessons/test-design-techniques/exercise_unit"}],"id":3297,"slug":"test-design-techniques","state":"approved","name":"Техники тест-дизайна","course_order":600,"goal":"Изучаем классы эквивалентности и попарное тестирование","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"776980600","theory":"## Тест-дизайн\n\nОпределить весь процесс тестирования приложения – задача сложная даже для опытного тестировщика. В этом вопросе не существует четких правил, которые подскажут, как тестировать, какие тесты выполнять и в каких ситуациях. Чтобы справиться с этой неопределенностью, тестировщики придумали множество приемов, которые помогают решать эти вопросы и дают подсказки, помогающие направить тестирование в правильное русло и сократить сроки тестирования.\n\nТест-дизайн – это этап тестирования ПО. На нем проектируются и создаются тест-кейсы, которые будут соответствовать определенным заранее критериям качества и целям тестирования. Цель тест-дизайна — создать наборы тестовых случаев, обеспечивающих оптимальное тестовое покрытие.\n\nРазработка тестов начинается после проведения исследования ПО, когда цели определены, а критерии тестирования заданы и выполняются.\n\n## Техники тест-дизайна\n\nТехники тест-дизайна помогают:\n\n1. Исключить непродуктивные тест-кейсы и сократить общее количество кейсов\n1. Покрыть тестами как можно больше функциональности\n1. Провести все тесты и не пропустить ничего важного\n\nДля работы с кодом (white-box) важны такие аспекты:\n\n* Покрытие операторов\n* Покрытие условий\n* Покрытие путей\n* Покрытие функций\n* Покрытие вход/выход\n* Покрытие значений параметров\n\nВ работе с требованиями (black-box) тестирование проходит иначе:\n\n* Классы эквивалентности\n* Граничные значения\n* Попарное тестирование\n* Таблица принятия решений\n* Диаграмма состояний и переходов\n* Тестирование вариантов использования\n* Доменное тестирование.\n\n## Техники тест-дизайна на основании требований\n\n### Классы эквивалентности\n\nТехника классов эквивалентности – это разделение диапазона возможных вводимых значений на группы эквивалентных по своему влиянию на систему. Эта техника помогает не только сокращать количество тестов, но и сохранять приемлемое тестовое покрытие.\n\nРассмотрим для примера перевод денег в банке. Размер комиссии зависит от суммы перевода:\n\n* от 1 до 999 долларов включительно – 0%\n* от 1000 до 4999 долларов включительно – 5%\n* от 5000 долларов – 7%\n\nМаксимальная сумма перевода – 100000 долларов, при этом дробные числа не учитываются.\n\nПопробуем выяснить, сколько требований будет в этом случае. В этом помогут классы эквивалентности:\n\n* 1-999 => ожидаем комиссию 0%\n* 1000-4999 => ожидаем комиссию 5%\n* 5000-100000 => ожидаем комиссию 7%\n\nВыбранные значения для проверки: 500, 2500, 7500.\n\n* На значение 500 система отреагирует так же, как и на любое другое значение из первого диапазона\n* На значение 2500 система отреагирует так же, как и на любое другое значение из второго диапазона\n* На значение 7500 система отреагирует так же, как и на любое другое значение из третьего диапазона\n\n### Граничные значения\n\nГраничные значения – это значения, в которых один класс эквивалентности переходит в другой. По своей сути это техника, которая дополняет технику классов эквивалентности.\n\nВажно проверять граничные значения, потому что именно на границах чаще всего допускаются ошибки при написании кода и формулировании требований.\n\nНапример, неопытный программист при постановке «не больше 1000» может поставить значение <1000.\n\nВернемся к примеру с комиссией:\n\n* от 1 до 999 долларов включительно – 0%\n* от 1000 до 4999 долларов включительно – 5%\n* от 5000 долларов – 7%\n\nМаксимальная сумма перевода – 100000 долларов, при этом дробные числа не учитываются. Вспомним классы эквивалентности:\n\n* 1-999 => ожидаем комиссию 0%\n* 1000-4999 => ожидаем комиссию 5%\n* 5000-100000 => ожидаем комиссию 7%\n\nГраничные значения: 1, 999, 1000, 4999, 5000, 100000.\n\nС учетом классов эквивалентности и граничных значений выбираем значения для проверки:\n**1**, 500, **999**, **1000**, 2500, **4999**, **5000**, 7500, **100000**.\n\n### Попарное тестирование (pairwise)\n\nПопарное тестирование – техника тест-дизайна, при которой тест-кейсы создаются так, чтобы выполнить все возможные отдельные комбинации каждой пары входных параметров.\n\nДостаточно проверить комбинации пар входных параметров, потому что ошибки чаще всего находятся именно на перекрестке двух параметров. Исключения бывают, но они достаточно редкие.\n\nРассмотрим пример. Возьмем наушники с такими характеристиками:\n\n* Тип подключения (беспроводной или проводной)\n* Микрофон (включен или выключен)\n* Подсветка (включена или выключена)\n\nПолный перебор даст нам восемь тест-кейсов. Это 2 в третьей степени, потому что у каждого из трех параметров может быть всего два значения:\n\n| # | Тип подключения | Микрофон | Подсветка |\n| - | --------------- | -------- | --------- |\n| 1 | Проводной | Включен | Включена |\n| 2 | Проводной | Включен | Выключена |\n| 3 | Проводной | Выключен | Включена |\n| 4 | Проводной | Выключен | Выключена |\n| 5 | Беспроводной | Включен | Включена |\n| 6 | Беспроводной | Включен | Выключена |\n| 7 | Беспроводной | Выключен | Включена |\n| 8 | Беспроводной | Выключен | Выключена |\n\nКак правило, подобных характеристик в реальном мире куда больше, что неизбежно увеличивает количество тест-кейсов.\n\nПри попарном тестировании достаточно проверить лишь пары значений. При успешном выполнении тестов на 97% мы можем быть уверены, что проверяемая функциональность работает корректно.\n\n<!-- TODO FIXME — отсюда и до конца блока не совсем понятно, что автор имел в виду, надо будет прояснить -->\n\n* 1 нет, нет, нет -> нет, нет, нет -> нет, нет, нет (00, 00, 00)\n* 2 да, да, нет ->да, да, нет -> да, да, нет (11, 10, 10)\n* 3 нет, да, да -> нет, да, да -> нет, да, да (01, 11, 01)\n* 4 да, нет, да -> да, нет, да -> да, нет, да (10, 01, 11)\n\nПри трех параметрах, каждый из которых имеет 3 значения, количество вариантов полного перебора – 27 (три в третьей)\nПрименив pairwise, количество тест-кейсов сведётся к 9.\n\n| # | Тип подключения | Микрофон | Подсветка |\n| - | --------------- | -------- | --------- |\n| 1 | Беспроводной | Выключен | Выключена |\n| 2 | Проводной | Включен | Выключена |\n| 3 | Беспроводной | Включен | Включена |\n| 4 | Проводной | Выключен | Включена |\n\n### Таблица принятия решений\n\nТаблица решений или матрица решений — способ компактного представления модели со сложной логикой; инструмент для упорядочения сложных бизнес требований, которые должны быть реализованы в продукте.\n\nЭто взаимосвязь между множеством условий и действий.\n\nТаблица принятия решений содержит следующие элементы:\n\n* Условия — список возможных условий\n* Варианты — комбинация из выполнения и/или невыполнения условий этого списка\n* Действия — список возможных действий (вариантов исхода)\n\nНапример, кредит выдаётся людям, удовлетворяющим трём условиям:\n\n* Возраст: 18-60 лет\n* Гражданство: Россия\n* Стаж работы: более 5 лет ИЛИ средняя месячная зарплата за год больше 100 тысяч рублей\n\n| Условие | Значение 1 | Значение 2 | Значение 3 | Значение 4 |\n| ------------------ | ---------- | ---------- | ---------- | ---------- |\n| Возраст 18-60 | да | да | да | нет |\n| Гражданство РФ | да | да | да | да |\n| Стаж больше 5 лет | да | нет | да | да |\n| ЗП выше 100 тысяч рублей | нет | да | да | да |\n| Действие | | | | |\n| Кредит одобрен | да | да | да | нет |\n\n### Диаграмма состояний и переходов\n\nТаблица переходов представляет собой все возможные комбинации начальных и конечных состояний. Она включает в себя действительные и недействительные переходы, инициирующие события, защитные условия и результирующие действия.\n\nДиаграммы состояний и переходов показывают только действительные переходы и исключают недействительные переходы.\n\n**Состояние А** ——переход—–> **Состояние Б**\n\n\n\n### Тестирование вариантов использования\n\nТестирование вариантов использования определяется как метод тестирования программного обеспечения, который помогает идентифицировать тестовые случаи, охватывающие всю систему, от транзакции к транзакции от начала до конечной точки.\n\nВариант использования или юз-кейс – это описание конкретного использования системы субъектом или пользователем\n\nЮз-кейсы содержат следующие сведения:\n\n* Кто использует сайт или приложение\n* Что пользователь хочет сделать\n* Какие шаги делает пользователь, чтобы совершить определенное действие\n* Как сайт или приложение реагируют на действия пользователя\n\n### Доменное тестирование\n\nДоменное тестирование (domain analysis) — методика разработки тестов, использующаяся для определения действенных и эффективных тестовых сценариев в случаях, когда множественные параметры могут или должны быть протестированы одновременно.\n\nДоменное тестирование применяется для сокращения количества проводимых тестов без потери качества тестирования.\n\nОчень часто классы эквивалентности, относящиеся к позитивным проверкам, можно проверять совместно.\n\nВозьмем для примера такие требования:\n\n* Размер файла: до 200 МБ\n* Имя файла: от 5 до 24 символов, только латиница\n* Форматы файлов: только изображения\n\nВ этом случае нужны такие проверки:\n\n1. Загрузить файл менее 200 МБ\n1. Загрузить файл с именем hexlet\n1. Загрузить файл с расширением .jpg\n\nМожно заменить одной проверкой:\n\n1. Загрузить файл менее 200 МБ, с именем hexlet.jpg\n\nОбратите внимание, что с негативными проверками так делать нельзя. При этом некоторые негативные проверки также можно исключать за счет несовместимости значений двух параметров.\n\nНапример, не нужно проверять конфигурацию компьютера с комплектующими, которые никогда не будут поставляться вместе (слабый процессор и сильная видеокарта). Также не нужно проверять конфигурацию автомобиля с агрегатами, которые никогда не будут поставляться вместе (дизельный мотор + турбонаддув).\n\nЧасто такая же ситуация может возникнуть при тестировании фильтров в интернет-магазинах. Часто параметр2 имеет строго определенное значение при выборе параметра1, поэтому проверять все возможные значения параметра2 в паре с параметром1 – смысла не имеет.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":7309,"name":"theory","url":"/courses/qa-engineer-workflow/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":7309,"name":"theory","url":"/courses/qa-engineer-workflow/lessons/intro/theory_unit"}],"id":3286,"slug":"intro","state":"approved","name":"Введение","course_order":100,"goal":"Знакомимся с темой курса","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Как и всем остальным новичкам в IT, начинающим тестировщикам не просто освоиться на первом месте работы. Им приходится разбираться с множеством новых терминов, осваивать инструменты, учиться вести документацию.\n\nВ этой ситуации намного комфортнее чувствуют себя те, кто заранее изучил эти темы в теории и подготовился использовать эти знания на практике. Именно такой подготовкой мы будем заниматься на этом курсе.\n\n## Цели курса\n\nЗдесь вы изучите рабочий процесс тестировщика. Вы познакомитесь со всеми важными терминами и узнаете все, из чего состоит каждый рабочий день специалиста по тестированию.\n\nВы ближе познакомитесь с багами и дефектами, научитесь писать баг-репорты, тест-кейсы и чеклисты. Мы рассмотрим разные типы тестирования и пройдем по шагам по всему циклу работы. Знания из этого курса помогут вам полностью подготовиться к рабочему процессу тестировщика, взаимодействовать с другими членами команды и быстрее влиться в процессы на рабочем месте.\n\nМы рассмотрим такие темы:\n\n* Какие бывают требования к программе и зачем они нужны тестировщику\n* Как работать с позитивными и негативными сценариями\n* Что такое баг-репорты, тест-кейсы и чеклисты, по каким принципам они составляются\n* Как устроены техники тест-дизайна\n* Какими инструментами пользуются тестировщики: Test Management Systems, Jira, Trello\n* Из чего состоит цикл работы с багами и дефектами\n* Что такое регрессионное и нефункциональное тестирование\n* Как работать с дефектами с продакшена\n* Как использовать явные и неявные требования\n"},"id":321,"slug":"qa-engineer-workflow","challenges_count":3,"name":"Рабочий процесс тестировщика","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы изучите рабочий процесс тестировщика. Вы узнаете, как составляются баг-репорты и чем отличаются чеклисты от тест-кейсов. В итоге вы научитесь обнаруживать дефекты, проводить тест-анализ, создавать понятные баг-репорты, пользоваться сервисами TMS.","kind":"additional","updated_at":"2026-01-20T11:50:21.713Z","language":"other","duration_cache":54900,"skills":["Проводить тест-анализ и использовать техники тест-дизайна","Отличать чеклисты от тест-кейсов и использовать их на практике","Проводить регрессионное тестирование"],"keywords":[],"lessons_count":14,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6ODExNiwicHVyIjoiYmxvYl9pZCJ9fQ==--cb74af7628f51dafe9cfa64ba1356ffc5c454073/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[{"stack":{"id":56,"slug":"qa-engineer","title":"Инженер по тестированию","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":40,"duration_in_months":4},"id":100,"slug":"qa-engineer","title":"Инженер по ручному тестированию","subtitle":"Изучите виды тестирования, тест-кейсы, DevTools, Postman, SQL, Git и HTTP/HTTPS.","subtitle_for_lists":"Изучите виды тестирования, тест-кейсы, DevTools, Postman, SQL, Git и HTTP/HTTPS.","locale":"ru","current":true,"duration_in_months_text":"4 месяца","stack_slug":"qa-engineer","price_text":"от 3 368 ₽","duration_text":"4 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk1MCwicHVyIjoiYmxvYl9pZCJ9fQ==--4a16fe638654fb8d5ae09d7e8ab8e16ff228214f/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-amico.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/qa-engineer-workflow/lessons/test-design-techniques/theory_unit","version":"0b0c6d4ebbd40fd58630a0dd89cc25544ccdf24e","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Рабочий процесс тестировщика</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Техники тест-дизайна</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Техники тест-дизайна","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"Рабочий процесс тестировщика"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><h2 id="heading-2-1">Тест-дизайн</h2>
<p>Определить весь процесс тестирования приложения – задача сложная даже для опытного тестировщика. В этом вопросе не существует четких правил, которые подскажут, как тестировать, какие тесты выполнять и в каких ситуациях. Чтобы справиться с этой неопределенностью, тестировщики придумали множество приемов, которые помогают решать эти вопросы и дают подсказки, помогающие направить тестирование в правильное русло и сократить сроки тестирования.</p>
<p>Тест-дизайн – это этап тестирования ПО. На нем проектируются и создаются тест-кейсы, которые будут соответствовать определенным заранее критериям качества и целям тестирования. Цель тест-дизайна — создать наборы тестовых случаев, обеспечивающих оптимальное тестовое покрытие.</p>
<p>Разработка тестов начинается после проведения исследования ПО, когда цели определены, а критерии тестирования заданы и выполняются.</p>
<h2 id="heading-2-2">Техники тест-дизайна</h2>
<p>Техники тест-дизайна помогают:</p>
<ol>
<li>Исключить непродуктивные тест-кейсы и сократить общее количество кейсов</li>
<li>Покрыть тестами как можно больше функциональности</li>
<li>Провести все тесты и не пропустить ничего важного</li>
</ol>
<p>Для работы с кодом (white-box) важны такие аспекты:</p>
<ul>
<li>Покрытие операторов</li>
<li>Покрытие условий</li>
<li>Покрытие путей</li>
<li>Покрытие функций</li>
<li>Покрытие вход/выход</li>
<li>Покрытие значений параметров</li>
</ul>
<p>В работе с требованиями (black-box) тестирование проходит иначе:</p>
<ul>
<li>Классы эквивалентности</li>
<li>Граничные значения</li>
<li>Попарное тестирование</li>
<li>Таблица принятия решений</li>
<li>Диаграмма состояний и переходов</li>
<li>Тестирование вариантов использования</li>
<li>Доменное тестирование.</li>
</ul>
<h2 id="heading-2-3">Техники тест-дизайна на основании требований</h2>
<h3 id="heading-3-4">Классы эквивалентности</h3>
<p>Техника классов эквивалентности – это разделение диапазона возможных вводимых значений на группы эквивалентных по своему влиянию на систему. Эта техника помогает не только сокращать количество тестов, но и сохранять приемлемое тестовое покрытие.</p>
<p>Рассмотрим для примера перевод денег в банке. Размер комиссии зависит от суммы перевода:</p>
<ul>
<li>от 1 до 999 долларов включительно – 0%</li>
<li>от 1000 до 4999 долларов включительно – 5%</li>
<li>от 5000 долларов – 7%</li>
</ul>
<p>Максимальная сумма перевода – 100000 долларов, при этом дробные числа не учитываются.</p>
<p>Попробуем выяснить, сколько требований будет в этом случае. В этом помогут классы эквивалентности:</p>
<ul>
<li>1-999 => ожидаем комиссию 0%</li>
<li>1000-4999 => ожидаем комиссию 5%</li>
<li>5000-100000 => ожидаем комиссию 7%</li>
</ul>
<p>Выбранные значения для проверки: 500, 2500, 7500.</p>
<ul>
<li>На значение 500 система отреагирует так же, как и на любое другое значение из первого диапазона</li>
<li>На значение 2500 система отреагирует так же, как и на любое другое значение из второго диапазона</li>
<li>На значение 7500 система отреагирует так же, как и на любое другое значение из третьего диапазона</li>
</ul>
<h3 id="heading-3-5">Граничные значения</h3>
<p>Граничные значения – это значения, в которых один класс эквивалентности переходит в другой. По своей сути это техника, которая дополняет технику классов эквивалентности.</p>
<p>Важно проверять граничные значения, потому что именно на границах чаще всего допускаются ошибки при написании кода и формулировании требований.</p>
<p>Например, неопытный программист при постановке «не больше 1000» может поставить значение <1000.</p>
<p>Вернемся к примеру с комиссией:</p>
<ul>
<li>от 1 до 999 долларов включительно – 0%</li>
<li>от 1000 до 4999 долларов включительно – 5%</li>
<li>от 5000 долларов – 7%</li>
</ul>
<p>Максимальная сумма перевода – 100000 долларов, при этом дробные числа не учитываются. Вспомним классы эквивалентности:</p>
<ul>
<li>1-999 => ожидаем комиссию 0%</li>
<li>1000-4999 => ожидаем комиссию 5%</li>
<li>5000-100000 => ожидаем комиссию 7%</li>
</ul>
<p>Граничные значения: 1, 999, 1000, 4999, 5000, 100000.</p>
<p>С учетом классов эквивалентности и граничных значений выбираем значения для проверки:
<strong>1</strong>, 500, <strong>999</strong>, <strong>1000</strong>, 2500, <strong>4999</strong>, <strong>5000</strong>, 7500, <strong>100000</strong>.</p>
<h3 id="heading-3-6">Попарное тестирование (pairwise)</h3>
<p>Попарное тестирование – техника тест-дизайна, при которой тест-кейсы создаются так, чтобы выполнить все возможные отдельные комбинации каждой пары входных параметров.</p>
<p>Достаточно проверить комбинации пар входных параметров, потому что ошибки чаще всего находятся именно на перекрестке двух параметров. Исключения бывают, но они достаточно редкие.</p>
<p>Рассмотрим пример. Возьмем наушники с такими характеристиками:</p>
<ul>
<li>Тип подключения (беспроводной или проводной)</li>
<li>Микрофон (включен или выключен)</li>
<li>Подсветка (включена или выключена)</li>
</ul>
<p>Полный перебор даст нам восемь тест-кейсов. Это 2 в третьей степени, потому что у каждого из трех параметров может быть всего два значения:</p>
<div style="--table-min-width:calc(50rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_a100c15 mantine-TableScrollContainer-scrollContainer m_d57069b5 mantine-ScrollArea-root"><div style="overflow-x:hidden;overflow-y:hidden" class="m_c0783ff9 mantine-ScrollArea-viewport" data-offset-scrollbars="x" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><div class="m_62259741 mantine-TableScrollContainer-scrollContainerInner"><table><thead><tr><th>#</th><th>Тип подключения</th><th>Микрофон</th><th>Подсветка</th></tr></thead><tbody><tr><td>1</td><td>Проводной</td><td>Включен</td><td>Включена</td></tr><tr><td>2</td><td>Проводной</td><td>Включен</td><td>Выключена</td></tr><tr><td>3</td><td>Проводной</td><td>Выключен</td><td>Включена</td></tr><tr><td>4</td><td>Проводной</td><td>Выключен</td><td>Выключена</td></tr><tr><td>5</td><td>Беспроводной</td><td>Включен</td><td>Включена</td></tr><tr><td>6</td><td>Беспроводной</td><td>Включен</td><td>Выключена</td></tr><tr><td>7</td><td>Беспроводной</td><td>Выключен</td><td>Включена</td></tr><tr><td>8</td><td>Беспроводной</td><td>Выключен</td><td>Выключена</td></tr></tbody></table></div></div></div></div>
<p>Как правило, подобных характеристик в реальном мире куда больше, что неизбежно увеличивает количество тест-кейсов.</p>
<p>При попарном тестировании достаточно проверить лишь пары значений. При успешном выполнении тестов на 97% мы можем быть уверены, что проверяемая функциональность работает корректно.</p>
<!-- -->
<ul>
<li>1 нет, нет, нет -> нет, нет, нет -> нет, нет, нет (00, 00, 00)</li>
<li>2 да, да, нет ->да, да, нет -> да, да, нет (11, 10, 10)</li>
<li>3 нет, да, да -> нет, да, да -> нет, да, да (01, 11, 01)</li>
<li>4 да, нет, да -> да, нет, да -> да, нет, да (10, 01, 11)</li>
</ul>
<p>При трех параметрах, каждый из которых имеет 3 значения, количество вариантов полного перебора – 27 (три в третьей)
Применив pairwise, количество тест-кейсов сведётся к 9.</p>
<div style="--table-min-width:calc(50rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_a100c15 mantine-TableScrollContainer-scrollContainer m_d57069b5 mantine-ScrollArea-root"><div style="overflow-x:hidden;overflow-y:hidden" class="m_c0783ff9 mantine-ScrollArea-viewport" data-offset-scrollbars="x" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><div class="m_62259741 mantine-TableScrollContainer-scrollContainerInner"><table><thead><tr><th>#</th><th>Тип подключения</th><th>Микрофон</th><th>Подсветка</th></tr></thead><tbody><tr><td>1</td><td>Беспроводной</td><td>Выключен</td><td>Выключена</td></tr><tr><td>2</td><td>Проводной</td><td>Включен</td><td>Выключена</td></tr><tr><td>3</td><td>Беспроводной</td><td>Включен</td><td>Включена</td></tr><tr><td>4</td><td>Проводной</td><td>Выключен</td><td>Включена</td></tr></tbody></table></div></div></div></div>
<h3 id="heading-3-7">Таблица принятия решений</h3>
<p>Таблица решений или матрица решений — способ компактного представления модели со сложной логикой; инструмент для упорядочения сложных бизнес требований, которые должны быть реализованы в продукте.</p>
<p>Это взаимосвязь между множеством условий и действий.</p>
<p>Таблица принятия решений содержит следующие элементы:</p>
<ul>
<li>Условия — список возможных условий</li>
<li>Варианты — комбинация из выполнения и/или невыполнения условий этого списка</li>
<li>Действия — список возможных действий (вариантов исхода)</li>
</ul>
<p>Например, кредит выдаётся людям, удовлетворяющим трём условиям:</p>
<ul>
<li>Возраст: 18-60 лет</li>
<li>Гражданство: Россия</li>
<li>Стаж работы: более 5 лет ИЛИ средняя месячная зарплата за год больше 100 тысяч рублей</li>
</ul>
<div style="--table-min-width:calc(50rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_a100c15 mantine-TableScrollContainer-scrollContainer m_d57069b5 mantine-ScrollArea-root"><div style="overflow-x:hidden;overflow-y:hidden" class="m_c0783ff9 mantine-ScrollArea-viewport" data-offset-scrollbars="x" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><div class="m_62259741 mantine-TableScrollContainer-scrollContainerInner"><table><thead><tr><th>Условие</th><th>Значение 1</th><th>Значение 2</th><th>Значение 3</th><th>Значение 4</th></tr></thead><tbody><tr><td>Возраст 18-60</td><td>да</td><td>да</td><td>да</td><td>нет</td></tr><tr><td>Гражданство РФ</td><td>да</td><td>да</td><td>да</td><td>да</td></tr><tr><td>Стаж больше 5 лет</td><td>да</td><td>нет</td><td>да</td><td>да</td></tr><tr><td>ЗП выше 100 тысяч рублей</td><td>нет</td><td>да</td><td>да</td><td>да</td></tr><tr><td>Действие</td><td></td><td></td><td></td><td></td></tr><tr><td>Кредит одобрен</td><td>да</td><td>да</td><td>да</td><td>нет</td></tr></tbody></table></div></div></div></div>
<h3 id="heading-3-8">Диаграмма состояний и переходов</h3>
<p>Таблица переходов представляет собой все возможные комбинации начальных и конечных состояний. Она включает в себя действительные и недействительные переходы, инициирующие события, защитные условия и результирующие действия.</p>
<p>Диаграммы состояний и переходов показывают только действительные переходы и исключают недействительные переходы.</p>
<p><strong>Состояние А</strong> ——переход—–> <strong>Состояние Б</strong></p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6ODE1OSwicHVyIjoiYmxvYl9pZCJ9fQ==--216cb2bd852628c8d5897b86ad4033f4ed5c105e/transitions.png" alt="переходы состояний для статьи на Хекслете" loading="lazy"/></p>
<h3 id="heading-3-9">Тестирование вариантов использования</h3>
<p>Тестирование вариантов использования определяется как метод тестирования программного обеспечения, который помогает идентифицировать тестовые случаи, охватывающие всю систему, от транзакции к транзакции от начала до конечной точки.</p>
<p>Вариант использования или юз-кейс – это описание конкретного использования системы субъектом или пользователем</p>
<p>Юз-кейсы содержат следующие сведения:</p>
<ul>
<li>Кто использует сайт или приложение</li>
<li>Что пользователь хочет сделать</li>
<li>Какие шаги делает пользователь, чтобы совершить определенное действие</li>
<li>Как сайт или приложение реагируют на действия пользователя</li>
</ul>
<h3 id="heading-3-10">Доменное тестирование</h3>
<p>Доменное тестирование (domain analysis) — методика разработки тестов, использующаяся для определения действенных и эффективных тестовых сценариев в случаях, когда множественные параметры могут или должны быть протестированы одновременно.</p>
<p>Доменное тестирование применяется для сокращения количества проводимых тестов без потери качества тестирования.</p>
<p>Очень часто классы эквивалентности, относящиеся к позитивным проверкам, можно проверять совместно.</p>
<p>Возьмем для примера такие требования:</p>
<ul>
<li>Размер файла: до 200 МБ</li>
<li>Имя файла: от 5 до 24 символов, только латиница</li>
<li>Форматы файлов: только изображения</li>
</ul>
<p>В этом случае нужны такие проверки:</p>
<ol>
<li>Загрузить файл менее 200 МБ</li>
<li>Загрузить файл с именем hexlet</li>
<li>Загрузить файл с расширением .jpg</li>
</ol>
<p>Можно заменить одной проверкой:</p>
<ol>
<li>Загрузить файл менее 200 МБ, с именем hexlet.jpg</li>
</ol>
<p>Обратите внимание, что с негативными проверками так делать нельзя. При этом некоторые негативные проверки также можно исключать за счет несовместимости значений двух параметров.</p>
<p>Например, не нужно проверять конфигурацию компьютера с комплектующими, которые никогда не будут поставляться вместе (слабый процессор и сильная видеокарта). Также не нужно проверять конфигурацию автомобиля с агрегатами, которые никогда не будут поставляться вместе (дизельный мотор + турбонаддув).</p>
<p>Часто такая же ситуация может возникнуть при тестировании фильтров в интернет-магазинах. Часто параметр2 имеет строго определенное значение при выборе параметра1, поэтому проверять все возможные значения параметра2 в паре с параметром1 – смысла не имеет.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/qa-engineer?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">4 месяца</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Инженер по ручному тестированию</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите виды тестирования, тест-кейсы, DevTools, Postman, SQL, Git и HTTP/HTTPS.</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk1MCwicHVyIjoiYmxvYl9pZCJ9fQ==--4a16fe638654fb8d5ae09d7e8ab8e16ff228214f/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-amico.png" alt="Инженер по ручному тестированию" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 368 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/qa-engineer-workflow/lessons/test-design-techniques/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 14</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/qa-engineer-workflow/lessons/test-design-techniques/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-CdBlNCiQ.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-nkZBEvfU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>