<!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 18:48:06 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="IJ0r-EnztvY4V3AX1F8oEGhUUBh4tyMzTE4Ktp2g8LPPTODPu40blo4UVI_YUNhnqF19snCA3ZHxrpDiz6cX3Q";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>CRUD | Введение в тестирование веб-приложений</title>
<meta name="description" content="CRUD / Введение в тестирование веб-приложений: Знакомимся с CRUD-операциями — Create, Read, Update, Delete">
<link rel="canonical" href="https://ru.hexlet.io/courses/web-testing-basics/lessons/crud/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="CRUD">
<meta property="og:title" content="Введение в тестирование веб-приложений">
<meta property="og:description" content="CRUD / Введение в тестирование веб-приложений: Знакомимся с CRUD-операциями — Create, Read, Update, Delete">
<meta property="og:url" content="https://ru.hexlet.io/courses/web-testing-basics/lessons/crud/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="zDkpRA0gCH1xMvvmRT028VxGUKW5L5OjMKonvkMcc1Yj6OJz_16lHcdx335JMsaGnE99D7EYbQGNSr3qERuUOA" />
<script src="/vite/assets/inertia-DfXos102.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-BrRXra1y.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-cb8xch9l.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="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyOCwicHVyIjoiYmxvYl9pZCJ9fQ==--f60f9dfdd11bed62e5573394ea442764a862e2c8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Mobile%20testing-bro.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-26T18:48:06.627Z","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":"xJxa4IAjxzSdIAaJxxSTa8cETYQOqw5NUNOQ5fpxS4UrTZHXcl1qVCtjIhHLG2McBw1gLgac8O_tMwqxqHas6w","topics":[{"id":83826,"title":"Проверьте пожалуйста правильность ответов.\nЭто ответ из подсказки:\n\"В таблице выводится информация об уже существующих товарах, для каждого товара указано название и цена\nТекст интерфейса на русском языке — тексты кнопок, заголовки таблицы\n(При нажатии на кнопку добавления товара, новый товар добавляется в таблицу)\n(Добавление нового товара происходит без перезагрузки страницы)\n(Добавление нового товара происходит при клике по кнопке «Добавить»)\nПри нажатии на кнопку «Удалить», удаляется соответствующий товар из списка\nТовар удаляется без перезагрузки страницы\" . .\n\nВ скобки я заключил неправильные ответы, на мой взгляд, потому что при нажатии кнопки \"Добавить\" товар в таблицу не добавляется, а добавляется только пустая строка с ценой товара.\nПоэтому эти три строки я не включил в ответ.\n\n\n","plain_title":"Проверьте пожалуйста правильность ответов. Это ответ из подсказки: \"В таблице выводится информация об уже существующих товарах, для каждого товара указано название и цена Текст интерфейса на русском языке — тексты кнопок, заголовки таблицы (При нажатии на кнопку добавления товара, новый товар добавляется в таблицу) (Добавление нового товара происходит без перезагрузки страницы) (Добавление нового товара происходит при клике по кнопке «Добавить») При нажатии на кнопку «Удалить», удаляется соответствующий товар из списка Товар удаляется без перезагрузки страницы\" . . В скобки я заключил неправильные ответы, на мой взгляд, потому что при нажатии кнопки \"Добавить\" товар в таблицу не добавляется, а добавляется только пустая строка с ценой товара. Поэтому эти три строки я не включил в ответ. ","creator":{"public_name":"Дмитрий","id":536974,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":170237,"body":"**Дмитрий**, при добавлении товара появляется новая запись в таблице товаров. В ней могут не отображаться данные товара, но это не значит, что товар не добавился. Ошибка может быть в отображении товара, а не в добавлении его в список.","topic_id":83826}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":82981,"title":" CRUD-откидываю все вроде неподходящие варианты.. и все равно недостаточно\n","plain_title":" CRUD-откидываю все вроде неподходящие варианты.. и все равно недостаточно ","creator":{"public_name":"Роман Тимановский","id":558112,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":168753,"body":"**Роман Романов**, вы можете попросить помощи в группе. За вами закреплен наставник, пользуйтесь этим преимуществом. Здесь, в обсуджениях, мы не даем готовые ответы.","topic_id":82981}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":87723,"title":"При добавлении нового товара не вводится его наименование.","plain_title":"При добавлении нового товара не вводится его наименование. ","creator":{"public_name":"Петр","id":618081,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":175723,"body":"**Петр**, по заданию вам нужно найти требования, которые соблюдаются. Если вы нашли ошибку, это значит какое-то требование не соблюдается.","topic_id":87723}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":81748,"title":"Ребят, там ошибка в первом предложении. \nВариант, который предлагается в ответе \"В таблице выводится информация об уже существующих товарах, для каждого товара указано название и цена\"\nВариант подсказки \"В таблице выводится информация об уже существующих товарах, для каждого указано название и цена\"\n","plain_title":"Ребят, там ошибка в первом предложении. Вариант, который предлагается в ответе \"В таблице выводится информация об уже существующих товарах, для каждого товара указано название и цена\" Вариант подсказки \"В таблице выводится информация об уже существующих товарах, для каждого указано название и цена\" ","creator":{"public_name":"Maxim Skripov","id":484153,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":166901,"body":"Спасибо! Поправил.","topic_id":81748}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":86885,"title":"пункт \"При нажатии на кнопку «Удалить», удаляется соответствующий товар из списка\" не совсем корректный, так как есть товары, которые удаляются по нажатии кнопки \"удалить\", а у некоторых товаров по клике на кнопку \"удалить\", добавляются новые товары. Поэтому не считаю, что утверждение верно","plain_title":"пункт \"При нажатии на кнопку «Удалить», удаляется соответствующий товар из списка\" не совсем корректный, так как есть товары, которые удаляются по нажатии кнопки \"удалить\", а у некоторых товаров по клике на кнопку \"удалить\", добавляются новые товары. Поэтому не считаю, что утверждение верно ","creator":{"public_name":"Владислав Клепиков","id":623415,"is_tutor":false},"comments":[{"creator":{"public_name":"Владислав Клепиков","id":623415,"is_tutor":false},"id":174406,"body":"У меня проявилось так:\n1) добавить товаров с 10-15 (добавлял с названием йцуЙЦУ123!\"№ и ценой 123!\"№, либо пустой ценой).\n2) пробовать удалять с начала списка (с позиции 3-6).\nгде-то в этом диапазоне кнопка \"удалить\" перестает работать и может добавлять товары, либо ничего не происходит.\n3) если кнопка \"удалить\" все еще работает правильно, попробовать еще раз добавить до нужного количества товаров и далее пункт 2.\nПопробовал сейчас еще раз спровоцировать баг - баг присутствует.","topic_id":86885},{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":174507,"body":"**Владислав Клепиков**, спасибо! Нашел в чем проблема. Поправил","topic_id":86885},{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":174396,"body":"**Владислав Клепиков**, подскажите, какие товары добавляются при нажатии кнопки \"удалить\"? Такой логики не заложено в приложении, и я не могу воспроизвести, вероятно где-то ошибка.","topic_id":86885}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":79592,"title":"В задании есть ошибка. \nВ описании (README) есть пункт:\n\n> Добавление нового товара происходит и при клике по кнопке \"Отправить\", и при отправках формы с помощью \"Enter\"\n\nОднако автотест ожидает получить вариант:\n\n> Добавление нового товара происходит и при клике по кнопке \"Отправить\", и при отправки формы с помощью клавиши \"Enter\"\n\nПожалуйста, поправьте описание или ответ в автотесте.","plain_title":"В задании есть ошибка. В описании (README) есть пункт: Добавление нового товара происходит и при клике по кнопке \"Отправить\", и при отправках формы с помощью \"Enter\" Однако автотест ожидает получить вариант: Добавление нового товара происходит и при клике по кнопке \"Отправить\", и при отправки формы с помощью клавиши \"Enter\" Пожалуйста, поправьте описание или ответ в автотесте. ","creator":{"public_name":"Ростислав Алфеев","id":181695,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikolai Gagarinov","id":104929,"is_tutor":true},"id":163827,"body":"Ростислав, прошу прощения, ввел в заблуждение.\nПока вам отвечал, тесты поправили. Вам необходмо сбросить упражнение.","topic_id":79592},{"creator":{"public_name":"Nikolai Gagarinov","id":104929,"is_tutor":true},"id":163825,"body":"Добрый день.\n\nЗадание проверил тесты принимают первый вариант.\nПопробуйте нажать кнопку \"Сбросить\", чтобы получить актуальную версию практики. Плюс вы можете после этого прислать решение.\n\n[Как сделать сброс](https://help.hexlet.io/ru/articles/111165-reshenie-uchitelya-ne-rabotaet)\n\n[Как сохранить свое решение](https://help.hexlet.io/ru/articles/282174-kak-soxranit-svoe-resenie)","topic_id":79592}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":79652,"title":"1) Почему вариант: Добавление нового товара происходит при клике по кнопке \"Отправить\" считается правильным, если кнопки \"отправить\" там нет?\n2) \"Работает заполнение полей нового товара. \" Этот вариант не совсем ясен. Поля в принципе заполняются, а вот уже на следующем этапе, когда нажимается кнопка \"Добавить\" - символы, введенные в поле \"Название\" пропадают. Было бы, наверно, точнее, если выделить этот момент отдельно, что после нажатия кнопки \"Добавить\" заполненные поля о новом товаре отображаются в списке. ","plain_title":"1) Почему вариант: Добавление нового товара происходит при клике по кнопке \"Отправить\" считается правильным, если кнопки \"отправить\" там нет? 2) \"Работает заполнение полей нового товара. \" Этот вариант не совсем ясен. Поля в принципе заполняются, а вот уже на следующем этапе, когда нажимается кнопка \"Добавить\" - символы, введенные в поле \"Название\" пропадают. Было бы, наверно, точнее, если выделить этот момент отдельно, что после нажатия кнопки \"Добавить\" заполненные поля о новом товаре отображаются в списке. ","creator":{"public_name":"Мария Скворцова","id":537285,"is_tutor":false},"comments":[{"creator":{"public_name":"Мария Скворцова","id":537285,"is_tutor":false},"id":163950,"body":"Спасибо!!","topic_id":79652},{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":163946,"body":"**Мария Скворцова**, здравствуйте! Спасибо за замечания! Поправили формулировки, чтобы было более очевидно.","topic_id":79652}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":88791,"title":"добрый день, тест решен верно, но Linter выдает \n/usr/src/app/src/components/AddProductForm.jsx\n 23:20 error Unexpected usage of singlequote jsx-quotes\n 24:22 error Unexpected usage of singlequote jsx-quotes\n 25:23 error Unexpected usage of singlequote jsx-quotes\n\n✖ 3 problems (3 errors, 0 warnings)\n 3 errors and 0 warnings potentially fixable with the `--fix` option.\nЧего он ругается, и нужно вообще на него внимание обращать?","plain_title":"добрый день, тест решен верно, но Linter выдает /usr/src/app/src/components/AddProductForm.jsx 23:20 error Unexpected usage of singlequote jsx-quotes 24:22 error Unexpected usage of singlequote jsx-quotes 25:23 error Unexpected usage of singlequote jsx-quotes ✖ 3 problems (3 errors, 0 warnings) 3 errors and 0 warnings potentially fixable with the --fix option. Чего он ругается, и нужно вообще на него внимание обращать? ","creator":{"public_name":"Татьяна Щуркова","id":644584,"is_tutor":false},"comments":[{"creator":{"public_name":"Olga Pejenkova","id":364375,"is_tutor":true},"id":177200,"body":"**Татьяна Щуркова**, \n\nвы правы, данные ошибки линтера не являются частью задания, они относятся к коду приложения, которое необходимо протестировать. Я внесла правки, теперь все должно быть в порядке. Пожалуйста, сделайте сброс упражнения, чтобы изменения вступили в силу.","topic_id":88791}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":94824,"title":"\"После перезагрузки удаленный товар не появляется в списке\"\nДа, уже обсуждали, но не пояснили, что есть разница между удалением товара, который был в списке изначально, и удалением товара, который был добавлен тестировщиком при тестировании\n","plain_title":"\"После перезагрузки удаленный товар не появляется в списке\" Да, уже обсуждали, но не пояснили, что есть разница между удалением товара, который был в списке изначально, и удалением товара, который был добавлен тестировщиком при тестировании ","creator":{"public_name":"Xenia Plotnikova","id":286300,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":185334,"body":"**Xenia Plotnikova**, здравствуйте. Я удалил этот вариант, чтобы это не вызывало неоднозначности. Спасибо, что обратили на это внимание!","topic_id":94824}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":90685,"title":"### Ошибка линтера\n\n/usr/src/app/prepare.js\n 2:12 error Require statement not part of import statement @typescript-eslint/no-var-requires\n\n✖ 1 problem (1 error, 0 warnings)\n\nИз-за этого тест не пропускает [решение](https://ru.hexlet.io/code_reviews/1094015)?","plain_title":"Ошибка линтера /usr/src/app/prepare.js 2:12 error Require statement not part of import statement @typescript-eslint/no-var-requires ✖ 1 problem (1 error, 0 warnings) Из-за этого тест не пропускает решение (https://ru.hexlet.io/code_reviews/1094015)? ","creator":{"public_name":"Владимир Ш.","id":600167,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":179807,"body":"**Владимир Ш.**, здравствуйте. Нет, ошибки линтера не влияют на результат и работу тестов. Тесты показывают, что ваш ответ неверный.","topic_id":90685}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":2124,"slug":"web_testing_basics_crud_exercise","name":null,"state":"active","kind":"exercise","language":"javascript","locale":"ru","has_web_view":true,"has_test_view":false,"reviewable":true,"readme":"В этом задании нужно протестировать SPA-приложение с каталогом товаров и новым функционалом. При загрузке страницы появляется список товаров. В таблице товара нужно проверить форму добавления нового товара, заполняется ли название товара и цена.\n\nПриложение должно соответствовать нескольким требованиям:\n\n* В таблице выводится информация об уже существующих товарах, для каждого товара указано название и цена\n* Текст интерфейса на русском языке — тексты кнопок, заголовки таблицы\n* Отображается информация добавленного товара — название и цена\n* При нажатии на кнопку добавления товара, добавляется строка в таблицу\n* Добавление нового товара происходит без перезагрузки страницы\n* Добавление нового товара происходит при клике по кнопке \"Добавить\"\n* Добавление нового товара происходит при нажатии клавиши Enter при вводе любого из значений нового товара (фокус находится на одном из полей ввода)\n* При перезагрузке страницы новый товар должен быть в списке\n* Цена товара может содержать только число\n* После отправки формы, значение полей очищается\n* При нажатии на кнопку \"Удалить\", удаляется соответствующий товар из списка\n* Товар удаляется без перезагрузки страницы\n* Удаление товара происходит с подтверждением удаления\n\n## solution\n\nЗапишите в файл _solution_ все требования, которые соблюдаются. Помните, что одно различие — одна строчка. Сохраняйте заглавные буквы и кавычки. Самый простой способ — скопировать правильные варианты в файл.\n\nПример файла с решением:\n\n```\nВ таблице выводится информация для каждого товара — название и цена\nТекст интерфейса на русском языке — тексты кнопок, заголовки таблицы\n```\n\n## Подсказки\n\n* При добавлении товара появляется новая запись в таблице товаров. В ней могут не отображаться данные товара, но это не значит, что товар не добавился\n","prepared_readme":"В этом задании нужно протестировать SPA-приложение с каталогом товаров и новым функционалом. При загрузке страницы появляется список товаров. В таблице товара нужно проверить форму добавления нового товара, заполняется ли название товара и цена.\n\nПриложение должно соответствовать нескольким требованиям:\n\n* В таблице выводится информация об уже существующих товарах, для каждого товара указано название и цена\n* Текст интерфейса на русском языке — тексты кнопок, заголовки таблицы\n* Отображается информация добавленного товара — название и цена\n* При нажатии на кнопку добавления товара, добавляется строка в таблицу\n* Добавление нового товара происходит без перезагрузки страницы\n* Добавление нового товара происходит при клике по кнопке \"Добавить\"\n* Добавление нового товара происходит при нажатии клавиши Enter при вводе любого из значений нового товара (фокус находится на одном из полей ввода)\n* При перезагрузке страницы новый товар должен быть в списке\n* Цена товара может содержать только число\n* После отправки формы, значение полей очищается\n* При нажатии на кнопку \"Удалить\", удаляется соответствующий товар из списка\n* Товар удаляется без перезагрузки страницы\n* Удаление товара происходит с подтверждением удаления\n\n## solution\n\nЗапишите в файл _solution_ все требования, которые соблюдаются. Помните, что одно различие — одна строчка. Сохраняйте заглавные буквы и кавычки. Самый простой способ — скопировать правильные варианты в файл.\n\nПример файла с решением:\n\n```\nВ таблице выводится информация для каждого товара — название и цена\nТекст интерфейса на русском языке — тексты кнопок, заголовки таблицы\n```\n\n## Подсказки\n\n* При добавлении товара появляется новая запись в таблице товаров. В ней могут не отображаться данные товара, но это не значит, что товар не добавился\n","has_solution":true,"entity_name":"CRUD"},"units":[{"id":7263,"name":"theory","url":"/courses/web-testing-basics/lessons/crud/theory_unit"},{"id":7514,"name":"quiz","url":"/courses/web-testing-basics/lessons/crud/quiz_unit"},{"id":7381,"name":"exercise","url":"/courses/web-testing-basics/lessons/crud/exercise_unit"}],"links":[],"ordered_units":[{"id":7263,"name":"theory","url":"/courses/web-testing-basics/lessons/crud/theory_unit"},{"id":7514,"name":"quiz","url":"/courses/web-testing-basics/lessons/crud/quiz_unit"},{"id":7381,"name":"exercise","url":"/courses/web-testing-basics/lessons/crud/exercise_unit"}],"id":3263,"slug":"crud","state":"approved","name":"CRUD","course_order":660,"goal":"Знакомимся с CRUD-операциями — Create, Read, Update, Delete","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"774792993","theory":"Представьте мобильное приложение для туризма. В нем пользователь может изучать карту местности и:\n\n* Создавать записи о местах, которые хочет посетить\n* Просматривать информацию о городах и достопримечательностях\n* Исправлять информацию о достопримечательностях\n* Удалять записи о местах, которые больше не хочется посетить\n\nДругой пример — вы администратор художественной онлайн-галереи. Вам необходимо:\n\n* Создавать записи о новых художниках и их работах\n* Просматривать информацию об экспонатах\n* Редактировать информацию о художниках и экспонатах\n* Удалять записи о работах, которые убрали из галереи\n\nВ этих сценариях прослеживаются базовые действия: создание, чтение, обновление и удаление. Они известны как **CRUD** (**C***reate **R***ead **U***pdate **D**elete).\n\nВ этом уроке мы изучим каждую из операций CRUD. Вы разберетесь в особенностях создания, чтения, обновления и удаления. Также вы узнаете, как тестировать CRUD на примере сервиса Яндекс.Карты.\n\n## Особенности CRUD\n\nСначала разберем каждую операцию CRUD в отдельности. Все операции связаны друг с другом. Например, мы не можем получить доступ к удаленному товару или удалить товар, который еще не создан. Эти вещи кажутся очевидными, но всегда есть вероятность допустить ошибку, что может привести к неожиданным результатам.\n\n### Create\n\nСоздание новых данных — ключевая функция всего CRUD. Остальные операции не имеют смысла без создания новой записи в базе данных.\n\nПри создании новой записи важно валидировать данные, как и в любой форме. Подробнее про валидацию вы узнали из урока «Формы».\n\nВалидация помогает не допустить ошибочные данные в базу и таким образом предотвратить возможные ошибки при чтении. Правильная валидация данных так же обеспечивает защиту от вредоносных значений, которые могут быть добавлены в базу данных.\n\n### Read\n\nЧтение данных — операция, которая доступна всем пользователям. Зачастую, чтение — единственная операция, которая доступна пользователю без прав администратора. Например, вы и другие студенты Хекслета можете читать этот урок, но отредактировать урок или создать новый не получится.\n\nВ операции чтения важно контролировать доступ к информации. Пользователь не должен видеть информацию, к которой у него нет доступа. Например, как владелец банковского счета вы можете увидеть только свой баланс, доступа к чужим счетам у вас нет.\n\n### Update\n\nОперация обновления похожа на создание — с ее помощью мы вносим изменения в базу данных. В случае с обновлением важно корректно сохранять ранее внесенные данные. Например, если вы меняете свой логин на сайте, то при этом не должна затрагиваться информация о пароле и электронной почте.\n\n### Delete\n\nУдаление — последняя операция для CRUD. Она удаляет записи из базы данных, будь то информация о картине или метка на карте.\n\nОбычно удаление выполняется после проверки прав доступа к данным. Например, если пользователь не имеет право удалять записи, запрос не сработает. Если право есть, система начинает удалять данные.\n\nС точки зрения реализации удаление делится на два типа:\n\n* Физическое — запись стирается из базы данных без возможности восстановления\n* Логическое — к записи закрывается доступ на чтение, то есть она скрывается. Доступ всегда можно восстановить при необходимости. Логическое удаление больше похоже на обновление, потому что физически запись в базе данных остается\n\n## Как тестировать CRUD\n\nПопробуем протестировать CRUD-операции вручную. Для этого воспользуемся сервисом Яндекс.Карты, который позволяет создавать списки меток и добавлять интересные точки на карте. Этим сервисом часто пользуются путешественники, чтобы добавить все интересующие их места на карте:\n\n\n\nПредставим, что нам нужно протестировать эту функциональность и проверить корректность создания списка, его обновления, чтения и удаления.\n\nТестировать будем по такому сценарию:\n\n1. Создать список\n2. Добавить две точки на карте\n3. Проверить их отображение в списке\n4. Удалить список\n\n### Cоздание списка\n\nСоздадим новый список и назовем его «Отпуск»:\n\n\n\nЗдесь мы видим операцию создания — в нашей учетной записи появляется новый список. Этот список доступен только для нашей учетной записи.\n\nОн позволяет проводить остальные операции по CRUD:\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\n### Удаление категории\n\nПосле проверки списка на чтения осталось его удалить:\n\n\n\nУдаление списка стирает не только список, но и все точки на карте, связанные с этим списком. Важно, чтобы точки на карте не были привязаны к несуществующему списку.\n\nЕще одна особенность — подтверждение удаления. Это сделано, чтобы случайно не удалить категорию:\n\n\n\nТакой подход часто критикуется, хотя этот вопрос лежит уже в области дизайна и взаимодействия с пользователем. Если кнопка подтверждения есть, то стоит проверить, что список не удаляется до подтверждения.\n\n## Выводы\n\nВ этом уроке мы разобрали механизм, который называется CRUD. Он состоит из четырех базовых операций, которые происходят в базе данных:\n\n* Create — создание\n* Read — чтение\n* Update — обновление\n* Delete — удаление\n\nКаждая из этих операций обладает своими особенностями и зачастую связана с проверкой прав доступа к той или иной операции. Например, студенты Хекслета могут только читать урок, тогда как сотрудники — обновлять уроки и удалять их.\n\nВо время ручного тестирования CRUD мы воссоздаем сценарий использования сайта и проверки всех операций. Этот сценарий называют **«путь пользователя»**.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":7261,"name":"theory","url":"/courses/web-testing-basics/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":7261,"name":"theory","url":"/courses/web-testing-basics/lessons/intro/theory_unit"}],"id":3262,"slug":"intro","state":"approved","name":"Введение","course_order":60,"goal":"Знакомимся с темой курса","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"В этом курсе мы поговорим о ручном тестировании: из чего оно состоит, какие инструменты используются и на что обращать внимание при тестировании.\n\nНесмотря на то, что курс является вводным, он полезен как для разработчиков, так и для новичков:\n\n* **Новичкам в тестировании** — познакомиться с основами тестирования, выяснить, из чего оно состоит, и какие аспекты приложения или сайта требует\n* **Начинающим разработчикам** — выявить типичные проблемы при создании сайта, которых можно избежать уже на этапе постановки задачи. Например, как ускорить загрузку сайта, как обеспечить базовую защиту от атак и так далее\n* **Кто уже тестирует** — структурировать информацию, найти новые подходы к тестированию и почерпнуть новые темы, которые пригодятся в работе\n\nПроцесс тестирования в крупных приложениях неразрывно связан с разработкой, и на это есть три причины.\n\n### Качество\n\nБольшинство проектов в интернете рассчитано на массовое использование, будь то сайт с рецептами или приложение для просмотра сериалов. Такие проекты постоянно вводят новые функции и разделы, из-за чего сложно отслеживать их работоспособность.\n\nПредставьте свои ощущения, если вместо рецепта ваших любимых кексиков откроется страница с контактами или количество ингредиентов будет рассчитываться неправильно.\n\nТак как разработчики заняты разработкой продукта, то главными героями являются тестировщики. Их задача — проверить новый функционал и убедиться, что старый работает исправно. После этого продукт можно открывать для всех пользователей.\n\n### Понятность\n\nРазработчики сделали возможность изменения языка озвучки для сериала. Для компании важно, чтобы этот функционал не просто работал, но и был доступен для понимания. Он не должен быть скрыт за кучей настроек или расположен в том месте, где его не ожидаешь.\n\nТестировщики, как фокус-группа, не просто находят технические проблемы, но и следят, чтобы функционал был понятен и прост для использования. Такая область, как понятность и удобность использования, еще называется юзабилити, от английского usability — удобство использования.\n\n### Безопасность\n\nСайт или приложение = большое количество кода. Чем больше проект и больше в нем данных, тем больше встает вопрос о безопасности. Если продукт можно взломать простыми способами, которые написаны на первой странице поисковой системы, то будьте уверены — кто-то воспользуется этим.\n\nЕсли злоумышленник получит персональные данные, то может пострадать сайт, приложение, репутация или пользователи.\n\nОдна из задач тестировщика — проверить базовые виды атак, которые можно исправить сразу. Например, добавление кода на JavaScript в поля формы.\n\n## Что изучается в курсе\n\nВ процессе прохождения курса мы изучим, что тестируют специалисты, какие используют инструменты и на что стоит обращать внимание при заходе на сайт. Также изучим:\n\n* Как проверить сайт на разных устройствах: компьютер, планшет, смартфоны\n* Почему сайт может отличаться в разных программах и устройствах\n* Из чего состоит сайт, что такое HTML и CSS\n* Какой инструмент в браузерах необходим для тестировщиков\n* С какими проблемами безопасности можно встретиться и как их выявлять\n* Какие типы тестирования существуют\n"},"id":245,"slug":"web-testing-basics","challenges_count":4,"name":"Введение в тестирование веб-приложений","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"free","description":"На этом курсе вы узнаете о веб-приложениях и изучите основы их тестирования. \nВ процессе прохождения курса мы изучим, что тестируют специалисты, какие используют инструменты и на что стоит обращать внимание при заходе на сайт. Вы узнаете, как на работоспособность веб-приложения влияет многообразие устройств, скорость соединения и другие условия. Вы познакомитесь с основами юзабилити и доступности. Вы научитесь находить уязвимости, ошибки верстки и логики работы веб-приложения. Вы научитесь диагностировать работу сайта с помощью инструментов Devtools. \nОсвоить тестирование веб-приложений с нуля непросто, поэтому с первого же урока вы будете подкреплять теорию практикой. В упражнениях курса вы будете проводить тестирование различных страниц, каталогов, форм и приложений.","kind":"basic","updated_at":"2026-01-20T11:40:26.695Z","language":"other","duration_cache":62880,"skills":["Использовать DevTools для диагностики запросов","Обнаруживать ошибки в верстке и UX приложения","Тестировать SPA-приложения","Находить уязвимости веб-приложений, например, XSS"],"keywords":[],"lessons_count":15,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjIyMSwicHVyIjoiYmxvYl9pZCJ9fQ==--23e3df9feea39eaed8d150557abd4377bc99b425/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"},{"stack":{"id":121,"slug":"testing-basics-free","title":"Основы тестирования","audience":"for_beginners","start_type":"anytime","pricing_model":"free","priority":"low","kind":"track","state":"published","stack_state":"finished","order":1000,"duration_in_months":1},"id":200,"slug":"testing-basics-free","title":"Основы тестирования","subtitle":"Изучите основы тестирование, HTML/CSS, DevTools и веб-стандарты","subtitle_for_lists":"","locale":"ru","current":true,"duration_in_months_text":"1 месяц","stack_slug":"testing-basics-free","price_text":"Бесплатно","duration_text":"1 месяц","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyOCwicHVyIjoiYmxvYl9pZCJ9fQ==--f60f9dfdd11bed62e5573394ea442764a862e2c8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Mobile%20testing-bro.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":true,"accessToCourseExists":true},"url":"/courses/web-testing-basics/lessons/crud/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","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">Теория: CRUD</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"CRUD","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 style="margin-bottom:var(--mantine-spacing-xl)" class=""><div class="ratio ratio-16x9"><iframe width="100%" height="auto" src="//player.vimeo.com/video/774792993" loading="lazy" allowFullScreen="" title="video"></iframe></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>Представьте мобильное приложение для туризма. В нем пользователь может изучать карту местности и:</p>
<ul>
<li>Создавать записи о местах, которые хочет посетить</li>
<li>Просматривать информацию о городах и достопримечательностях</li>
<li>Исправлять информацию о достопримечательностях</li>
<li>Удалять записи о местах, которые больше не хочется посетить</li>
</ul>
<p>Другой пример — вы администратор художественной онлайн-галереи. Вам необходимо:</p>
<ul>
<li>Создавать записи о новых художниках и их работах</li>
<li>Просматривать информацию об экспонатах</li>
<li>Редактировать информацию о художниках и экспонатах</li>
<li>Удалять записи о работах, которые убрали из галереи</li>
</ul>
<p>В этих сценариях прослеживаются базовые действия: создание, чтение, обновление и удаление. Они известны как <strong>CRUD</strong> (<strong>C</strong><em>reate <strong>R</strong></em>ead <strong>U</strong>*pdate <strong>D</strong>elete).</p>
<p>В этом уроке мы изучим каждую из операций CRUD. Вы разберетесь в особенностях создания, чтения, обновления и удаления. Также вы узнаете, как тестировать CRUD на примере сервиса Яндекс.Карты.</p>
<h2 id="heading-2-1">Особенности CRUD</h2>
<p>Сначала разберем каждую операцию CRUD в отдельности. Все операции связаны друг с другом. Например, мы не можем получить доступ к удаленному товару или удалить товар, который еще не создан. Эти вещи кажутся очевидными, но всегда есть вероятность допустить ошибку, что может привести к неожиданным результатам.</p>
<h3 id="heading-3-2">Create</h3>
<p>Создание новых данных — ключевая функция всего CRUD. Остальные операции не имеют смысла без создания новой записи в базе данных.</p>
<p>При создании новой записи важно валидировать данные, как и в любой форме. Подробнее про валидацию вы узнали из урока «Формы».</p>
<p>Валидация помогает не допустить ошибочные данные в базу и таким образом предотвратить возможные ошибки при чтении. Правильная валидация данных так же обеспечивает защиту от вредоносных значений, которые могут быть добавлены в базу данных.</p>
<h3 id="heading-3-3">Read</h3>
<p>Чтение данных — операция, которая доступна всем пользователям. Зачастую, чтение — единственная операция, которая доступна пользователю без прав администратора. Например, вы и другие студенты Хекслета можете читать этот урок, но отредактировать урок или создать новый не получится.</p>
<p>В операции чтения важно контролировать доступ к информации. Пользователь не должен видеть информацию, к которой у него нет доступа. Например, как владелец банковского счета вы можете увидеть только свой баланс, доступа к чужим счетам у вас нет.</p>
<h3 id="heading-3-4">Update</h3>
<p>Операция обновления похожа на создание — с ее помощью мы вносим изменения в базу данных. В случае с обновлением важно корректно сохранять ранее внесенные данные. Например, если вы меняете свой логин на сайте, то при этом не должна затрагиваться информация о пароле и электронной почте.</p>
<h3 id="heading-3-5">Delete</h3>
<p>Удаление — последняя операция для CRUD. Она удаляет записи из базы данных, будь то информация о картине или метка на карте.</p>
<p>Обычно удаление выполняется после проверки прав доступа к данным. Например, если пользователь не имеет право удалять записи, запрос не сработает. Если право есть, система начинает удалять данные.</p>
<p>С точки зрения реализации удаление делится на два типа:</p>
<ul>
<li>Физическое — запись стирается из базы данных без возможности восстановления</li>
<li>Логическое — к записи закрывается доступ на чтение, то есть она скрывается. Доступ всегда можно восстановить при необходимости. Логическое удаление больше похоже на обновление, потому что физически запись в базе данных остается</li>
</ul>
<h2 id="heading-2-6">Как тестировать CRUD</h2>
<p>Попробуем протестировать CRUD-операции вручную. Для этого воспользуемся сервисом Яндекс.Карты, который позволяет создавать списки меток и добавлять интересные точки на карте. Этим сервисом часто пользуются путешественники, чтобы добавить все интересующие их места на карте:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjQzOSwicHVyIjoiYmxvYl9pZCJ9fQ==--959878e799ca15d1f75a596526ea85e7fcb24bc9/yamap-1.jpg" alt="Создание категории на яндекс картах" loading="lazy"/></p>
<p>Представим, что нам нужно протестировать эту функциональность и проверить корректность создания списка, его обновления, чтения и удаления.</p>
<p>Тестировать будем по такому сценарию:</p>
<ol>
<li>Создать список</li>
<li>Добавить две точки на карте</li>
<li>Проверить их отображение в списке</li>
<li>Удалить список</li>
</ol>
<h3 id="heading-3-7">Cоздание списка</h3>
<p>Создадим новый список и назовем его «Отпуск»:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjQ0MCwicHVyIjoiYmxvYl9pZCJ9fQ==--57b6855cf708e897c05dc80a8dac28e33320a314/yamap-2.jpg" alt="Создание категории на яндекс картах" loading="lazy"/></p>
<p>Здесь мы видим операцию создания — в нашей учетной записи появляется новый список. Этот список доступен только для нашей учетной записи.</p>
<p>Он позволяет проводить остальные операции по CRUD:</p>
<ul>
<li>Чтение списка</li>
<li>Обновление записей в списке — добавление или удаление точек на карте</li>
<li>Удаление списка</li>
</ul>
<h3 id="heading-3-8">Добавление точек в список</h3>
<p>Теперь попробуем обновить список. Добавим две точки на карте:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjQ0MSwicHVyIjoiYmxvYl9pZCJ9fQ==--f2fa8dc0336149a0a881a696bdc188ef94fcc569/yamap-3.jpg" alt="Добавление точек в список на яндекс картах" loading="lazy"/></p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjQ0MiwicHVyIjoiYmxvYl9pZCJ9fQ==--eb7a4fca01497e28ac601655d1868ce525fdc8a4/yamap-4.jpg" alt="Добавление точек в список на яндекс картах" loading="lazy"/></p>
<p>На этом шаге мы обновили список. Обратите внимание, что обновляется только список. Сами точки на карте — это лишь часть списка, они не изменяются. Филиал Третьяковской галереи и информация о нем никак не изменится от того, что мы добавили точку в наш список.</p>
<h3 id="heading-3-9">Проверка точек в списке</h3>
<p>Проверим, что точки добавились корректно. Для этого перейдем в список «Отпуск»:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjQ0MywicHVyIjoiYmxvYl9pZCJ9fQ==--832440382775bb931464d7ee36f5547511a3e671/yamap-5.jpg" alt="Просмотр точек в списке на яндекс картах" loading="lazy"/></p>
<p>Здесь мы производим операцию чтения. Во время тестирования мы проверяем, точно ли все данные выведены на экране. Пользователь должен увидеть:</p>
<ul>
<li>Изображения точек</li>
<li>Количество точек</li>
<li>Каждая точка в отдельности</li>
</ul>
<p>Важно проверить, что чтение списка доступно только создателю или тем, кому открыт доступ на чтение списка.</p>
<h3 id="heading-3-10">Удаление категории</h3>
<p>После проверки списка на чтения осталось его удалить:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjQ0NCwicHVyIjoiYmxvYl9pZCJ9fQ==--7cce3deb63e62b58d83ac759a040bc88032a84e0/yamap-6.jpg" alt="Просмотр точек в категории на яндекс картах" loading="lazy"/></p>
<p>Удаление списка стирает не только список, но и все точки на карте, связанные с этим списком. Важно, чтобы точки на карте не были привязаны к несуществующему списку.</p>
<p>Еще одна особенность — подтверждение удаления. Это сделано, чтобы случайно не удалить категорию:</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjQ0NSwicHVyIjoiYmxvYl9pZCJ9fQ==--98e1dac3ff2f6011b1dc7cac1d2816788c7f63a3/yamap-7.jpg" alt="Просмотр точек в категории на яндекс картах" loading="lazy"/></p>
<p>Такой подход часто критикуется, хотя этот вопрос лежит уже в области дизайна и взаимодействия с пользователем. Если кнопка подтверждения есть, то стоит проверить, что список не удаляется до подтверждения.</p>
<h2 id="heading-2-11">Выводы</h2>
<p>В этом уроке мы разобрали механизм, который называется CRUD. Он состоит из четырех базовых операций, которые происходят в базе данных:</p>
<ul>
<li>Create — создание</li>
<li>Read — чтение</li>
<li>Update — обновление</li>
<li>Delete — удаление</li>
</ul>
<p>Каждая из этих операций обладает своими особенностями и зачастую связана с проверкой прав доступа к той или иной операции. Например, студенты Хекслета могут только читать урок, тогда как сотрудники — обновлять уроки и удалять их.</p>
<p>Во время ручного тестирования CRUD мы воссоздаем сценарий использования сайта и проверки всех операций. Этот сценарий называют <strong>«путь пользователя»</strong>.</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="/programs/testing-basics-free?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">1 месяц</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"></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/eyJfcmFpbHMiOnsiZGF0YSI6MzkyOCwicHVyIjoiYmxvYl9pZCJ9fQ==--f60f9dfdd11bed62e5573394ea442764a862e2c8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Mobile%20testing-bro.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">Бесплатно</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/web-testing-basics/lessons/crud/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 / 15</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/web-testing-basics/lessons/crud/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-Bukl1lYy.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-BrRXra1y.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>