В видео есть неточность. Функция факториала, показанная в видео, использует цикл, а не рекурсию
Транскрипт урока
Вот вы программируете, пишете код, функции и все такое. Вы хотите, чтобы все это работало, конечно, поэтому проверяете все ли в порядке с кодом, нет ли в нем ошибок и он делает то, для чего вы его писали. Вы не слишком беспокоитесь о синтаксических ошибках или ошибках типизации, поскольку вы обнаруживаете их сразу же, как только запускаете свои программы.
Но есть один тип подлых ошибок, с которыми все сложнее, и компьютеры не умеют находить их без нашей помощи. Это логические ошибки.
Предположим, вы написали функцию square. Она принимает число и возвращает его квадрат. Даете ей 2, она должна вернуть 4. Даете 6 — она должна вернуть 36.
Если вы сделали ошибку, и эта функция возвращает неверное значение, компьютер или интерпретатор языка программирования будут не в состоянии распознать это как ошибку. Они делают только то, что вы сказали им делать, поэтому если вы укажете, что 2 в квадрате должно быть 11, правильным ответом станет 11 — вот и вся магия.
Один очевидный способ удостовериться, что ваша функция выполняет то, чего вы от нее хотите, это запустить ее и посмотреть.
Мы можем запустить console.log(square(2)); и если она выведет 4, то все в порядке. Ну, может быть в порядке не совсем все… чтобы не сомневаться, давайте проверим square(6). Результат -- 36, значит все в порядке. Ну, еще одну, ладно: 100 в квадрате. Десять тысяч — хорошо.
На данном этапе вы уже можете быть достаточно уверенными, что функция square действительно возводит величину в квадрат. Эта функция простая и вам не нужно сидеть часами и тестировать ее. Но не все ваши функции такие же простые. Даже функция факториала из одного из предыдущих уроков намного-намного сложнее.
Да, и знаете что еще? 2-х, 4-х и 100-а не достаточно. Вам еще нужно проверить отрицательные числа, верно? -2 возведенное в квадрат должно быть 4, а не -4, а мы этого не проверили. О… а 0 в квадрате. А, подождите! Еще кое что: мы должны проверить, что эта квадратная функция не сломается, если вы передадите в нее что-то совсем не то, например строчку. Она должна, вероятно, вернуть, что "собака" в квадрате — это не число.
Писать все эти штуки и выводить на экран вручную как-то странно, особенно учитывая то, что у нас есть эта мегамощная машина, которая делает все, что мы прикажем. Так что, давайте прикажем ей тестировать для нас функции. Мы знаем, что эти функции ДОЛЖНЫ возвращать. Поэтому компьютер может проверить, действительно ли они возвращают то, что надо.
Представьте себе функцию assert, которая работает с тремя элементами:
- функцией, которую мы хотим протестировать
- аргументом, который принимается этой функцией
- значением, которое мы СЧИТАЕМ должна вернуть эта функция
assert скажет нам, действительно ли функция возвращает значение, которое мы ожидаем.
Давайте вызовем assert, чтобы увидеть, действительно ли функция square возвращает 4, когда вызывается с аргументом 2:
Если функция square верна в случае с 2, то она возвратит 4, а раз 4 равно 4, assert не пожалуется. Мы говорим — этот тест пройден.
Другой сценарий: 6 в квадрате определенно не должно быть 4. Поэтому:
Этот тест пройдет, если функция square возвратит любое значение, кроме 4.
Что происходит, когда рассматриваемая функция не работает как нужно? Например, ее результат показывает, что 2 в квадрате — это 6, а не 4. Тогда первый тест провалится, и мы увидим ошибку, типа такой:
Она выглядит очень похожей на синтаксическую ошибку, и благодаря этой магической функции assert, мы видим новый тип ошибки — ошибку утверждения. Это значит какой-то тест не прошел, провалился, поэтому нам нужно починить код.
Тут он говорит Assertion error: false == true. Это потому что тест ожидал, что выражение будет истинным, но оно было ложным. square(2) === 4 должно быть истинным, но раз наша поломанная функция возведения в квадрат возвращает 6, выражение становится ложным. Ложное это не истинное, следовательно — ошибка.
Снизу виден стектрейс, он показывает вам, где найти проваленный тест — название файла, номер строчки в этом файле и позицию в этой строчке.
Есть другие функции assert для более удобного и легкочитаемого вывода. Вот одна из них:
assert.equal — это специальная функция для тестирования равенства, она принимает два аргумента: то, что вы хотите протестировать и значение, которое вы ожидаете. Пройденный тест просто пройдет, а вот проваленный даст вам какую-то информацию:
Вы можете увидеть, какой был фактический результат, а какой ожидаемый, и, в частности, где они не совпали.
Эта функция assert не просто что-то, что я нафантазировал, это библиотека тестирования для JavaScript программ. Библиотека — это набор полезных функций, код других людей, который вы можете добавить в свою программу и упростить себе жизнь.
В JavaScript существуют другие библиотеки для тестирования кода, и в других языках есть свои. Тестирование — очень важная задача, и часто крупный блок всего кода компании — это только тесты для этого самого кода. Например, этот сайт — hexlet.io, состоит из десятков тысяч строчек кода и треть их — тесты, строчки кода вроде: "проверить что что-то — это именно то, что мы думаем".
И каждый раз, когда мы что-то меняем — добавляем новые возможности или меняем существующие — мы запускаем тесты, чтобы удостовериться, что ничего не поломалось.
Продолжайте и переходите к упражнению. Вы напишете несколько тестов самостоятельно!
Дополнение к уроку
Хекслет и console.log
Кнопка "Проверить" запускает на выполнение тесты, которые, в свою очередь, используют написанный вами код. Это означает, что вы легко можете использовать
console.log везде, где хотите, и столько раз, сколько хотите. Весь вывод появится во вкладке OUTPUT. Обратите внимание на одну деталь. Если выполнение кода не дошло до того места, где была вставлена отладочная печать, то она, естественно, не выведется на экран.
Хитрость
Часто отладочную печать сложно заметить в объемном выводе тестов. Но если
сделать так: console.log('!!!!!!', <то что вы хотите посмотреть>), то пропустить вывод станет практически невозможно.
Важное: пограничные случаи
При тестировании важно не забывать про так называемые пограничные случаи. Нетипичные случаи, находящиеся «с края» потенциальных вариантов.
Например, написанная функция factorial может работать на всех числах больше единицы, но не работать, если параметром будет единица или ноль. Это, конечно же, является ошибкой. Пограничные случаи, как правило, требуют дополнительного кода для их обработки.
Важно не забывать писать тесты для таких вариантов использования функции.
Также пограничным случаем для функции factorial является любое отрицательное число.
Не существует никаких правил по тому, что является пограничным случаем, а что нет. Все зависит от конкретной задачи и вашего опыта.
Выводы
Тесты — это кусочки кода, которые проверяют, что другой код работает правильно. assert — это общее название для подобных тестовых функций, а так же название библиотеки тестирования JavaScript. Библиотека — коллекция полезных функций, небольшое количество кода других людей, которое вы можете добавлять в программу и упрощать свою работу.
Assert принимает выражение и жалуется, если оно не истинно. Следующий тест проверяет действительно ли square(2) это 4:
А в данном случае не равно ли square(6) числу 4:
Если выражение утверждения истинно, то тест проходит.
А вот как он жалуется, если выражение не истинно:
Это специальная отдельная функция для проверки равенства:
А вот как она жалуется, если тест валится:
Так лучше, потому что вы можете видеть реальные результаты (6) и ожидаемые результаты (4), и насколько они не соответствуют друг другу.
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 17:19:45 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="VMdODcgeZYoZIpj2MJnUK080Se1qMsWMIyYN0QZntMa7FoU6OmDI6q9hvG48liRcjz1kR2IFOy6expeFVGBTqA";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Тесты и автоматическая проверка кода | Введение в программирование</title>
<meta name="description" content="Тесты и автоматическая проверка кода / Введение в программирование: Каждый раз, когда что-то меняем в коде, мы запускаем тесты, чтобы удостовериться, что ничего не поломалось. Знакомимся с автоматическим тестированием кода и пишем свои первые простые тесты.">
<link rel="canonical" href="https://ru.hexlet.io/courses/introduction_to_programming/lessons/tests/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Тесты и автоматическая проверка кода">
<meta property="og:title" content="Введение в программирование">
<meta property="og:description" content="Тесты и автоматическая проверка кода / Введение в программирование: Каждый раз, когда что-то меняем в коде, мы запускаем тесты, чтобы удостовериться, что ничего не поломалось. Знакомимся с автоматическим тестированием кода и пишем свои первые простые тесты.">
<meta property="og:url" content="https://ru.hexlet.io/courses/introduction_to_programming/lessons/tests/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="yJZ2CS53IvgEHtvyjgkoakKqaacPbgBJVIAv2OQ1u6knR70-3AmPmLJd_2qCBtgdgqNEDQdZ_uvpYLWMtjJcxw" />
<script src="/vite/assets/inertia-INZxX8jp.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-nkZBEvfU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-6pOtQ3OW.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T17:19:45.655Z","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":"29_E8kMRC1c8ezsMdVeaV8512vqbcpSj5pn-hSkc6Ls0Dg_FsW-mN4o4H5R5WGogDnz3UJNFagFbeWTRexsP1Q","topics":[{"id":30753,"title":"Подскажите, как с помощью алерта проверить в данном задании пограничный случай \"Отрицательное число\"?Пробовал NaN в ожидаемом - не получилось. (не уверен, что правильно выражаюсь, но простите, как есть)","plain_title":"Подскажите, как с помощью алерта проверить в данном задании пограничный случай \"Отрицательное число\"?Пробовал NaN в ожидаемом - не получилось. (не уверен, что правильно выражаюсь, но простите, как есть) ","creator":{"public_name":"Павел Дикарев","id":179353,"is_tutor":false},"comments":[{"creator":{"public_name":"Павел Дикарев","id":179353,"is_tutor":false},"id":66679,"body":"Заранее спасибо за ответ!","topic_id":30753},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":66727,"body":"Приветствую!\n\nНа самом деле вам надо добавить несколько других проверок, посмотрите внимательно этот абзац из задания:\n\n> Вам необходимо добавить минимум три дополнительные проверки (помимо уже существующих в модуле), а именно: протестировать пограничные случаи (вызов с параметрами 0 и 1) и сделать проверку на работу с другими неотрицательными целыми числами. ","topic_id":30753}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":18531,"title":"Сделал так что вместо числа отправляю строку и должно прийти NaN, но когда жму проверить, результат бесконечный цикл","plain_title":"Сделал так что вместо числа отправляю строку и должно прийти NaN, но когда жму проверить, результат бесконечный цикл ","creator":{"public_name":"Alex Sandr","id":67099,"is_tutor":false},"comments":[{"creator":{"public_name":"R. D.","id":3,"is_tutor":false},"id":39124,"body":"Посмотрите в исходный код проверяемой функции: учитывает ли она реально такой сценарий?","topic_id":18531}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":46905,"title":"Добрый день.\nВ теории в разделе Хитрость есть **console.log('!!!!!!', <то что вы хотите посмотреть>)**\nНе совсем пойму как ее применять. Помогите пожалуйста с конкретным примером и сравнением с обычным console.log\nЗаранее спасибо.\n","plain_title":"Добрый день. В теории в разделе Хитрость есть console.log('!!!!!!', <то что вы хотите посмотреть>) Не совсем пойму как ее применять. Помогите пожалуйста с конкретным примером и сравнением с обычным console.log Заранее спасибо. ","creator":{"public_name":"Timur Ermeshev","id":310131,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":101061,"body":"Добрый день. Например, у вас в коде есть какая-то переменная i, значение которой вы хотите увидеть. Чтобы это сделать, нужно поставить послее нее отладочную печать:\n```\nlet i = 4\nconsole.log('!!!!', i)\n```\n\nВывод будет примерно такой: `'!!!!' 4`\n\nВосклицательные знаки нужны, чтобы вывод не затерялся в общей массе, так его проще заметить. В остальном это все тот же console.log. Он будет в одну строку выводить на экран все аргументы, которые вы ему передали через запятую","topic_id":46905},{"creator":{"public_name":"Антон Рождественский","id":302869,"is_tutor":false},"id":101060,"body":"Добрый день!\nА в чем в вашем понимании различие между \"хитростью\" и обычным console.log? Как не крути, а это все та же отладочная печать. Приведу простой пример простой функции:\n\n```\nconst item = (a, b) => {\n const result = a + b;\n console.log('!!!!!', result);\n return result;\n};\n```\nЧто в данном случае выведет функция? Она вернет сумму переданных значений. Но ведь функция может быть и более сложной, верно? В таких случаях один из способов отладки спорных моментов - console.log, а синтаксис **console.log('!!!!!!', <то что вы хотите посмотреть>)** больше для удобства. Благодаря !!!!!! ваш вывод будет более заметным. \n\nКак можно это использовать? Например, у вас сложная функция, которая имеет цикл по массиву, и вам нужно понимать какое значение она возвращает. В таком случае будет иметь примерно следующий вид:\n\n```\nfor (const item of array) {\n console.log('!!!', item);\n// какой-то код c вычислениями\n console.log('!!!!', result);\n}\n```\n\n ","topic_id":46905}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":16293,"title":"Совсем запуталась в этом задании. я писала код факториала. а его оказывается и не надо было?? просто написать проверку через assert, используя factorial, и не писать формулу?..\nстранно. а я так долго мучалась","plain_title":"Совсем запуталась в этом задании. я писала код факториала. а его оказывается и не надо было?? просто написать проверку через assert, используя factorial, и не писать формулу?.. странно. а я так долго мучалась ","creator":{"public_name":"Лариса Ф","id":178561,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":34501,"body":"Добрый день, Лариса! Да, в этом задании вам надо добавить только проверки, саму функцию реализовывать не надо.","topic_id":16293}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":43325,"title":" в чем ошибка ? https://ru.hexlet.io/code_reviews/272487","plain_title":" в чем ошибка ? https://ru.hexlet.io/code_reviews/272487 ","creator":{"public_name":"Aidan Mustafin","id":289465,"is_tutor":false},"comments":[{"creator":{"public_name":"Stanislav Dzisiak","id":212236,"is_tutor":true},"id":94388,"body":"**Aidan Mustafin**, приветствую!\n\nУ вас получилось разобраться и справиться с упражнением?","topic_id":43325},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":94234,"body":"Добрый день. Вам не нужно писать саму функцию, нужно только дописать тесты для ее проверки.\n\n>Саму функцию в этой практике реализовывать не надо, она уже готова. Вам же сейчас надо дописать тесты для проверки её работы.","topic_id":43325}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":41761,"title":"А на каком языке написан, собственно, сам **Hexlet**? В видеоролике по синтаксису не смог определить. Из любопытства спрашиваю","plain_title":"А на каком языке написан, собственно, сам Hexlet? В видеоролике по синтаксису не смог определить. Из любопытства спрашиваю ","creator":{"public_name":"Roman Hnatiuk","id":282970,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр-C Вагин","id":6409,"is_tutor":false},"id":90995,"body":"Ruby on rails + React :)","topic_id":41761},{"creator":{"public_name":"Roman Hnatiuk","id":282970,"is_tutor":false},"id":91004,"body":"Спасибо!)","topic_id":41761}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":11000,"title":"Добрый день. Что-то я понять не могу, правильно ли делаю? Или нужно прописать условия assert? repl.it ругается, что он не задан (ReferenceError: assert is not defined). Благодарю!\n\n`\nfunction factorial (n) {\n let i = 1;\n while (i <= n) {\n // i = factorial(num) + (i + 1);\n return (n != 1) ? n * factorial(n - 1) : 1;\n }\n}\nassert.equal(factorial(0), undefined);\nassert.equal(factorial(1), 1);\nassert.equal(factorial(7), 5040);\n`","plain_title":"Добрый день. Что-то я понять не могу, правильно ли делаю? Или нужно прописать условия assert? repl.it ругается, что он не задан (ReferenceError: assert is not defined). Благодарю! function factorial (n) { let i = 1; while (i <= n) { // i = factorial(num) + (i + 1); return (n != 1) ? n * factorial(n - 1) : 1; } } assert.equal(factorial(0), undefined); assert.equal(factorial(1), 1); assert.equal(factorial(7), 5040); ","creator":{"public_name":"Bulat Kaliev","id":145955,"is_tutor":false},"comments":[{"creator":{"public_name":"Дмитрий Храпонов","id":145876,"is_tutor":false},"id":22891,"body":"Ну так правильно, если он не задан, то всегда будет такая ошибка. Если хотите в repl.it его использовать, то допишите первой строчкой импорт `var assert = require('assert');`. Только зачем делать это в repl, если можно сделать в среде задания, где все эти мелочи уже учтены...","topic_id":11000},{"creator":{"public_name":"Bulat Kaliev","id":145955,"is_tutor":false},"id":22910,"body":"Упс...благодарю","topic_id":11000},{"creator":{"public_name":"Дмитрий Храпонов","id":145876,"is_tutor":false},"id":22906,"body":"Да, вы ошибаетесь, поэтому решение и не работает. Почему в вашем тесте факториал числа 0 ожидает `undefined`?","topic_id":11000},{"creator":{"public_name":"Bulat Kaliev","id":145955,"is_tutor":false},"id":22894,"body":"У меня почему-то repl.it быстрее работает... \nВ таком случае \n`assert.equal(factorial(0), undefined); assert.equal(factorial(1), 1); assert.equal(factorial(7), 5040);` \nдолжно быть решением задачи или я ошибаюсь? Не могу понять что именно мне нужно написать для прохождения урока...","topic_id":11000}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":40811,"title":"пишет что ожидалось что тест не пройдет а он пройдем, и дальше не пускает, делаю так чтобы тест не прошел - пишет что ожидалось что тест пройдет) \nhttps://ru.hexlet.io/code_reviews/245468","plain_title":"пишет что ожидалось что тест не пройдет а он пройдем, и дальше не пускает, делаю так чтобы тест не прошел - пишет что ожидалось что тест пройдет) https://ru.hexlet.io/code_reviews/245468 ","creator":{"public_name":"Paul Pugovkin","id":271395,"is_tutor":false},"comments":[{"creator":{"public_name":"Paul Pugovkin","id":271395,"is_tutor":false},"id":89061,"body":"внимательно прочитал задание, все готово)","topic_id":40811},{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":89112,"body":"Приветствую!\n\nЗдорово, что все получилось. Бывает пропустишь одно слово в формулировке задачи и процесс решения может затянуться на часы. :)","topic_id":40811}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":39878,"title":"Факториал нуля равен единице, не благодарите","plain_title":"Факториал нуля равен единице, не благодарите ","creator":{"public_name":"Артем Еремин","id":270770,"is_tutor":false},"comments":[{"creator":{"public_name":"Николай Ткач","id":90517,"is_tutor":false},"id":94490,"body":"Вот это реально помогло:) уже и забыл, и думаю что не так:)","topic_id":39878}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}},{"id":48247,"title":"Здравствуйте! Подскажите, что не так с кодом? \nПосмотрела ответ. И почему не проходит проверка отрицательных значений? Ведь из темы ясно, что проверка нужна.\nhttps://ru.hexlet.io/code_reviews/325495","plain_title":"Здравствуйте! Подскажите, что не так с кодом? Посмотрела ответ. И почему не проходит проверка отрицательных значений? Ведь из темы ясно, что проверка нужна. https://ru.hexlet.io/code_reviews/325495 ","creator":{"public_name":"Анна Калмынина","id":282519,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":103687,"body":"Приветствую, Анна!\n\nА почему она нужна? Взгляните на определение.\n\n> Факториал — функция, определённая на множестве неотрицательных целых чисел.\n\nОб этом говорится и в условиях задачи: \"Общепринятого факториала от целых отрицательных чисел не существует\". Таким образом мы тестируем пограничные случаи исходя из этого факта. Более того, реализация функции также опирается на это определение.","topic_id":48247}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Тесты и автоматическая проверка кода","entity_url":null,"active":true}}],"lesson":{"exercise":null,"units":[{"id":1858,"name":"theory","url":"/courses/introduction_to_programming/lessons/tests/theory_unit"}],"links":[{"id":427081,"name":"Code Checking Automation - Computerphile","url":"https://www.youtube.com/watch?v=AfaNEebCDos"}],"ordered_units":[{"id":1858,"name":"theory","url":"/courses/introduction_to_programming/lessons/tests/theory_unit"}],"id":902,"slug":"tests","state":"approved","name":"Тесты и автоматическая проверка кода","course_order":140,"goal":"Каждый раз, когда что-то меняем в коде, мы запускаем тесты, чтобы удостовериться, что ничего не поломалось. Знакомимся с автоматическим тестированием кода и пишем свои первые простые тесты.","self_study":null,"theory_video_provider":"youtube","theory_video_uid":"Z3w6eMq7etw","theory":"В видео есть неточность. Функция факториала, показанная в видео, использует цикл, а не рекурсию\n\n## Транскрипт урока\n\nВот вы программируете, пишете код, функции и все такое. Вы хотите, чтобы все это работало, конечно, поэтому проверяете все ли в порядке с кодом, нет ли в нем ошибок и он делает то, для чего вы его писали. Вы не слишком беспокоитесь о синтаксических ошибках или ошибках типизации, поскольку вы обнаруживаете их сразу же, как только запускаете свои программы.\n\nНо есть один тип подлых ошибок, с которыми все сложнее, и компьютеры не умеют находить их без нашей помощи. Это логические ошибки.\n\nПредположим, вы написали функцию `square`. Она принимает число и возвращает его квадрат. Даете ей 2, она должна вернуть 4. Даете 6 — она должна вернуть 36.\n\nЕсли вы сделали ошибку, и эта функция возвращает неверное значение, компьютер или интерпретатор языка программирования будут не в состоянии распознать это как ошибку. Они делают только то, что вы сказали им делать, поэтому если вы укажете, что 2 в квадрате должно быть 11, правильным ответом станет 11 — вот и вся магия.\n\nОдин очевидный способ удостовериться, что ваша функция выполняет то, чего вы от нее хотите, это запустить ее и посмотреть.\n\nМы можем запустить `console.log(square(2));` и если она выведет 4, то все в порядке. Ну, может быть в порядке не совсем **все**… чтобы не сомневаться, давайте проверим `square(6)`. Результат -- 36, значит все в порядке. Ну, еще одну, ладно: 100 в квадрате. Десять тысяч — хорошо.\n\nНа данном этапе вы уже можете быть достаточно уверенными, что функция `square` действительно возводит величину в квадрат. Эта функция простая и вам не нужно сидеть часами и тестировать ее. Но не все ваши функции такие же простые. Даже функция факториала из одного из предыдущих уроков намного-намного сложнее.\n\nДа, и знаете что еще? 2-х, 4-х и 100-а не достаточно. Вам еще нужно проверить отрицательные числа, верно? -2 возведенное в квадрат должно быть 4, а не -4, а мы этого не проверили. О… а 0 в квадрате. А, подождите! Еще кое что: мы должны проверить, что эта квадратная функция не сломается, если вы передадите в нее что-то совсем не то, например строчку. Она должна, вероятно, вернуть, что \"собака\" в квадрате — это не число.\n\nПисать все эти штуки и выводить на экран вручную как-то странно, особенно учитывая то, что у нас есть эта мегамощная машина, которая делает все, что мы прикажем. Так что, давайте прикажем ей тестировать для нас функции. Мы знаем, что эти функции ДОЛЖНЫ возвращать. Поэтому компьютер может проверить, действительно ли они возвращают то, что надо.\n\nПредставьте себе функцию `assert`, которая работает с тремя элементами:\n\n- функцией, которую мы хотим протестировать\n- аргументом, который принимается этой функцией\n- значением, которое мы СЧИТАЕМ должна вернуть эта функция\n\n`assert` скажет нам, действительно ли функция возвращает значение, которое мы ожидаем.\n\nДавайте вызовем `assert`, чтобы увидеть, действительно ли функция `square` возвращает 4, когда вызывается с аргументом 2:\n\n```javascript\nassert(square(2) === 4)\n```\n\nЕсли функция `square` верна в случае с 2, то она возвратит 4, а раз 4 равно 4, `assert` не пожалуется. Мы говорим — этот тест пройден.\n\nДругой сценарий: 6 в квадрате определенно не должно быть 4. Поэтому:\n\n```javascript\nassert(square(6) !== 4)\n```\n\nЭтот тест пройдет, если функция `square` возвратит любое значение, кроме 4.\n\nЧто происходит, когда рассматриваемая функция не работает как нужно? Например, ее результат показывает, что 2 в квадрате — это 6, а не 4. Тогда первый тест провалится, и мы увидим ошибку, типа такой:\n\n```javascript\nAssertion error: false == true\n at square (/home/rakhim/code/planets.js:4:3)\n at surfaceArea (/home/rakhim/code/planets.js:8:10)\n```\n\nОна выглядит очень похожей на синтаксическую ошибку, и благодаря этой магической функции assert, мы видим новый тип ошибки — ошибку утверждения. Это значит какой-то тест не прошел, провалился, поэтому нам нужно починить код.\n\nТут он говорит `Assertion error: false == true`. Это потому что тест ожидал, что выражение будет истинным, но оно было ложным. `square(2) === 4` должно быть истинным, но раз наша поломанная функция возведения в квадрат возвращает 6, выражение становится ложным. Ложное это не истинное, следовательно — ошибка.\n\nСнизу виден стектрейс, он показывает вам, где найти проваленный тест — название файла, номер строчки в этом файле и позицию в этой строчке.\n\nЕсть другие функции `assert` для более удобного и легкочитаемого вывода. Вот одна из них:\n\n```javascript\nassert.equal(square(2), 4)\n```\n\nassert.equal — это специальная функция для тестирования равенства, она принимает два аргумента: то, что вы хотите протестировать и значение, которое вы ожидаете. Пройденный тест просто пройдет, а вот проваленный даст вам какую-то информацию:\n\n```javascript\nAssertion error: 6 == 4\n at square (/home/rakhim/code/planets.js:4:3)\n at surfaceArea (/home/rakhim/code/planets.js:8:10)\n```\n\nВы можете увидеть, какой был фактический результат, а какой ожидаемый, и, в частности, где они не совпали.\n\nЭта функция `assert` не просто что-то, что я нафантазировал, это библиотека тестирования для JavaScript программ. Библиотека — это набор полезных функций, код других людей, который вы можете добавить в свою программу и упростить себе жизнь.\n\nВ JavaScript существуют другие библиотеки для тестирования кода, и в других языках есть свои. Тестирование — очень важная задача, и часто крупный блок всего кода компании — это только тесты для этого самого кода. Например, этот сайт — hexlet.io, состоит из десятков тысяч строчек кода и треть их — тесты, строчки кода вроде: \"проверить что что-то — это именно то, что мы думаем\".\n\nИ каждый раз, когда мы что-то меняем — добавляем новые возможности или меняем существующие — мы запускаем тесты, чтобы удостовериться, что ничего не поломалось.\n\nПродолжайте и переходите к упражнению. Вы напишете несколько тестов самостоятельно!\n\n## Дополнение к уроку\n\n### Хекслет и console.log\n\nКнопка \"Проверить\" запускает на выполнение тесты, которые, в свою очередь, используют написанный вами код. Это означает, что вы легко можете использовать\n`console.log` везде, где хотите, и столько раз, сколько хотите. Весь вывод появится во вкладке `OUTPUT`. Обратите внимание на одну деталь. Если выполнение кода не дошло до того места, где была вставлена отладочная печать, то она, естественно, не выведется на экран.\n\n### Хитрость\n\nЧасто отладочную печать сложно заметить в объемном выводе тестов. Но если\nсделать так: `console.log('!!!!!!', <то что вы хотите посмотреть>)`, то пропустить вывод станет практически невозможно.\n\n### Важное: пограничные случаи\n\nПри тестировании важно не забывать про так называемые *пограничные случаи*. Нетипичные случаи, находящиеся «с края» потенциальных вариантов.\n\nНапример, написанная функция `factorial` может работать на всех числах больше единицы, но не работать, если параметром будет единица или ноль. Это, конечно же, является ошибкой. Пограничные случаи, как правило, требуют дополнительного кода для их обработки.\n\nВажно не забывать писать тесты для таких вариантов использования функции.\n\nТакже пограничным случаем для функции `factorial` является любое отрицательное число.\n\nНе существует никаких правил по тому, что является пограничным случаем, а что нет. Все зависит от конкретной задачи и вашего опыта.\n\n## Выводы\n\n**Тесты** — это кусочки кода, которые проверяют, что другой код работает правильно. `assert` — это общее название для подобных тестовых функций, а так же название библиотеки тестирования JavaScript. Библиотека — коллекция полезных функций, небольшое количество кода других людей, которое вы можете добавлять в программу и упрощать свою работу.\n\nAssert принимает выражение и жалуется, если оно не истинно. Следующий тест проверяет действительно ли `square(2)` это 4:\n\n```javascript\nassert(square(2) === 4)\n```\n\nА в данном случае **не** равно ли `square(6)` числу 4:\n\n```javascript\nassert(square(6) !== 4)\n```\n\nЕсли выражение утверждения истинно, то тест проходит.\n\nА вот как он жалуется, если выражение не истинно:\n\n```javascript\nAssertion error: false == true\n at square (/home/rakhim/code/planets.js:4:3)\n at surfaceArea (/home/rakhim/code/planets.js:8:10)\n```\n\nЭто специальная отдельная функция для проверки равенства:\n\n```javascript\nassert.equal(square(2), 4)\n```\n\nА вот как она жалуется, если **тест валится**:\n\n```javascript\nAssertion error: 6 == 4\n at square (/home/rakhim/code/planets.js:4:3)\n at surfaceArea (/home/rakhim/code/planets.js:8:10)\n```\n\nТак лучше, потому что вы можете видеть реальные результаты (6) и ожидаемые результаты (4), и насколько они не соответствуют друг другу.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":1801,"name":"theory","url":"/courses/introduction_to_programming/lessons/intro/theory_unit"}],"links":[{"id":427047,"name":"Inside your computer - Bettina Bair","url":"https://www.youtube.com/watch?v=AkFi90lZmXA"},{"id":427048,"name":"Punch Card Programming - Computerphile","url":"https://www.youtube.com/watch?v=KG2M4ttzBnY"},{"id":427049,"name":"The Future of Computer Intelligence Is Everything but Artificial","url":"https://www.wired.com/2014/06/the-future-of-computer-intelligence-is-everything-but-artificial/"}],"ordered_units":[{"id":1801,"name":"theory","url":"/courses/introduction_to_programming/lessons/intro/theory_unit"}],"id":876,"slug":"intro","state":"approved","name":"Что такое компьютер?","course_order":10,"goal":"Первый урок посвящен знакомству с Тотой – главным персонажем Хекслета. Вместе с нашим героем отвечаем на простые, но важные вопросы: Что такое компьютер? Умный ли он? Говорит ли он на языке программирования?","self_study":null,"theory_video_provider":"youtube","theory_video_uid":"rrRZZ_3licM","theory":"## Транскрипт урока\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```javascript\nconst factorial = (n) => {\n const iter = (current, acc) => {\n if (current === 0) {\n return acc\n }\n return iter(current - 1, acc * current)\n }\n\n return iter(n, 1)\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"},"id":143,"slug":"introduction_to_programming","challenges_count":0,"name":"Введение в программирование","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"free","description":"В этом курсе вы изучите основы программирования. Вы узнаете больше о языках программирования, их синтаксисе и правильном выборе языка для обучения. В итоге вы научитесь использовать функции, условия и циклы, а также напишите свои первые программы на JavaScript. Знания из этого курса пригодятся, если вы решите заниматься программированием и познакомиться с ключевыми принципами написания хорошего кода.","kind":"sandbox","updated_at":"2026-02-06T11:25:43.725Z","language":"javascript","duration_cache":18000,"skills":["Использовать основные языковые конструкции: условия, циклы, функции и другие","Разделять код на модули для повторного использования и отсутствия конфликта имён","Понимать ключевые концепции для написания хорошего кода, такие как чистота и детерминированность"],"keywords":["основы javascript","чистый код","алгоритмы","логика"],"lessons_count":20,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTI1NzIsInB1ciI6ImJsb2JfaWQifX0=--3444a6a2b32ba0a1b370387ae75d0c63f10ad54f/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[],"lessonMemberUnit":null,"accessToLearnUnitExists":true,"accessToCourseExists":true},"url":"/courses/introduction_to_programming/lessons/tests/theory_unit","version":"0b0c6d4ebbd40fd58630a0dd89cc25544ccdf24e","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Введение в программирование</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Тесты и автоматическая проверка кода</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Тесты и автоматическая проверка кода","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"Введение в программирование"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div style="margin-bottom:var(--mantine-spacing-xl)" class=""><div class="ratio ratio-16x9"><iframe width="100%" height="auto" src="//www.youtube.com/embed/Z3w6eMq7etw" loading="lazy" allowFullScreen="" title="video"></iframe></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>В видео есть неточность. Функция факториала, показанная в видео, использует цикл, а не рекурсию</p>
<h2 id="heading-2-1">Транскрипт урока</h2>
<p>Вот вы программируете, пишете код, функции и все такое. Вы хотите, чтобы все это работало, конечно, поэтому проверяете все ли в порядке с кодом, нет ли в нем ошибок и он делает то, для чего вы его писали. Вы не слишком беспокоитесь о синтаксических ошибках или ошибках типизации, поскольку вы обнаруживаете их сразу же, как только запускаете свои программы.</p>
<p>Но есть один тип подлых ошибок, с которыми все сложнее, и компьютеры не умеют находить их без нашей помощи. Это логические ошибки.</p>
<p>Предположим, вы написали функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square</code>. Она принимает число и возвращает его квадрат. Даете ей 2, она должна вернуть 4. Даете 6 — она должна вернуть 36.</p>
<p>Если вы сделали ошибку, и эта функция возвращает неверное значение, компьютер или интерпретатор языка программирования будут не в состоянии распознать это как ошибку. Они делают только то, что вы сказали им делать, поэтому если вы укажете, что 2 в квадрате должно быть 11, правильным ответом станет 11 — вот и вся магия.</p>
<p>Один очевидный способ удостовериться, что ваша функция выполняет то, чего вы от нее хотите, это запустить ее и посмотреть.</p>
<p>Мы можем запустить <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">console.log(square(2));</code> и если она выведет 4, то все в порядке. Ну, может быть в порядке не совсем <strong>все</strong>… чтобы не сомневаться, давайте проверим <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square(6)</code>. Результат -- 36, значит все в порядке. Ну, еще одну, ладно: 100 в квадрате. Десять тысяч — хорошо.</p>
<p>На данном этапе вы уже можете быть достаточно уверенными, что функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square</code> действительно возводит величину в квадрат. Эта функция простая и вам не нужно сидеть часами и тестировать ее. Но не все ваши функции такие же простые. Даже функция факториала из одного из предыдущих уроков намного-намного сложнее.</p>
<p>Да, и знаете что еще? 2-х, 4-х и 100-а не достаточно. Вам еще нужно проверить отрицательные числа, верно? -2 возведенное в квадрат должно быть 4, а не -4, а мы этого не проверили. О… а 0 в квадрате. А, подождите! Еще кое что: мы должны проверить, что эта квадратная функция не сломается, если вы передадите в нее что-то совсем не то, например строчку. Она должна, вероятно, вернуть, что "собака" в квадрате — это не число.</p>
<p>Писать все эти штуки и выводить на экран вручную как-то странно, особенно учитывая то, что у нас есть эта мегамощная машина, которая делает все, что мы прикажем. Так что, давайте прикажем ей тестировать для нас функции. Мы знаем, что эти функции ДОЛЖНЫ возвращать. Поэтому компьютер может проверить, действительно ли они возвращают то, что надо.</p>
<p>Представьте себе функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">assert</code>, которая работает с тремя элементами:</p>
<ul>
<li>функцией, которую мы хотим протестировать</li>
<li>аргументом, который принимается этой функцией</li>
<li>значением, которое мы СЧИТАЕМ должна вернуть эта функция</li>
</ul>
<p><code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">assert</code> скажет нам, действительно ли функция возвращает значение, которое мы ожидаем.</p>
<p>Давайте вызовем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">assert</code>, чтобы увидеть, действительно ли функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square</code> возвращает 4, когда вызывается с аргументом 2:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">assert(square(2) === 4)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Если функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square</code> верна в случае с 2, то она возвратит 4, а раз 4 равно 4, <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">assert</code> не пожалуется. Мы говорим — этот тест пройден.</p>
<p>Другой сценарий: 6 в квадрате определенно не должно быть 4. Поэтому:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">assert(square(6) !== 4)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Этот тест пройдет, если функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square</code> возвратит любое значение, кроме 4.</p>
<p>Что происходит, когда рассматриваемая функция не работает как нужно? Например, ее результат показывает, что 2 в квадрате — это 6, а не 4. Тогда первый тест провалится, и мы увидим ошибку, типа такой:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">Assertion error: false == true
at square (/home/rakhim/code/planets.js:4:3)
at surfaceArea (/home/rakhim/code/planets.js:8:10)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Она выглядит очень похожей на синтаксическую ошибку, и благодаря этой магической функции assert, мы видим новый тип ошибки — ошибку утверждения. Это значит какой-то тест не прошел, провалился, поэтому нам нужно починить код.</p>
<p>Тут он говорит <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">Assertion error: false == true</code>. Это потому что тест ожидал, что выражение будет истинным, но оно было ложным. <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square(2) === 4</code> должно быть истинным, но раз наша поломанная функция возведения в квадрат возвращает 6, выражение становится ложным. Ложное это не истинное, следовательно — ошибка.</p>
<p>Снизу виден стектрейс, он показывает вам, где найти проваленный тест — название файла, номер строчки в этом файле и позицию в этой строчке.</p>
<p>Есть другие функции <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">assert</code> для более удобного и легкочитаемого вывода. Вот одна из них:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">assert.equal(square(2), 4)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>assert.equal — это специальная функция для тестирования равенства, она принимает два аргумента: то, что вы хотите протестировать и значение, которое вы ожидаете. Пройденный тест просто пройдет, а вот проваленный даст вам какую-то информацию:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">Assertion error: 6 == 4
at square (/home/rakhim/code/planets.js:4:3)
at surfaceArea (/home/rakhim/code/planets.js:8:10)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Вы можете увидеть, какой был фактический результат, а какой ожидаемый, и, в частности, где они не совпали.</p>
<p>Эта функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">assert</code> не просто что-то, что я нафантазировал, это библиотека тестирования для JavaScript программ. Библиотека — это набор полезных функций, код других людей, который вы можете добавить в свою программу и упростить себе жизнь.</p>
<p>В JavaScript существуют другие библиотеки для тестирования кода, и в других языках есть свои. Тестирование — очень важная задача, и часто крупный блок всего кода компании — это только тесты для этого самого кода. Например, этот сайт — hexlet.io, состоит из десятков тысяч строчек кода и треть их — тесты, строчки кода вроде: "проверить что что-то — это именно то, что мы думаем".</p>
<p>И каждый раз, когда мы что-то меняем — добавляем новые возможности или меняем существующие — мы запускаем тесты, чтобы удостовериться, что ничего не поломалось.</p>
<p>Продолжайте и переходите к упражнению. Вы напишете несколько тестов самостоятельно!</p>
<h2 id="heading-2-2">Дополнение к уроку</h2>
<h3 id="heading-3-3">Хекслет и console.log</h3>
<p>Кнопка "Проверить" запускает на выполнение тесты, которые, в свою очередь, используют написанный вами код. Это означает, что вы легко можете использовать
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">console.log</code> везде, где хотите, и столько раз, сколько хотите. Весь вывод появится во вкладке <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">OUTPUT</code>. Обратите внимание на одну деталь. Если выполнение кода не дошло до того места, где была вставлена отладочная печать, то она, естественно, не выведется на экран.</p>
<h3 id="heading-3-4">Хитрость</h3>
<p>Часто отладочную печать сложно заметить в объемном выводе тестов. Но если
сделать так: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">console.log('!!!!!!', <то что вы хотите посмотреть>)</code>, то пропустить вывод станет практически невозможно.</p>
<h3 id="heading-3-5">Важное: пограничные случаи</h3>
<p>При тестировании важно не забывать про так называемые <em>пограничные случаи</em>. Нетипичные случаи, находящиеся «с края» потенциальных вариантов.</p>
<p>Например, написанная функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">factorial</code> может работать на всех числах больше единицы, но не работать, если параметром будет единица или ноль. Это, конечно же, является ошибкой. Пограничные случаи, как правило, требуют дополнительного кода для их обработки.</p>
<p>Важно не забывать писать тесты для таких вариантов использования функции.</p>
<p>Также пограничным случаем для функции <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">factorial</code> является любое отрицательное число.</p>
<p>Не существует никаких правил по тому, что является пограничным случаем, а что нет. Все зависит от конкретной задачи и вашего опыта.</p>
<h2 id="heading-2-6">Выводы</h2>
<p><strong>Тесты</strong> — это кусочки кода, которые проверяют, что другой код работает правильно. <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">assert</code> — это общее название для подобных тестовых функций, а так же название библиотеки тестирования JavaScript. Библиотека — коллекция полезных функций, небольшое количество кода других людей, которое вы можете добавлять в программу и упрощать свою работу.</p>
<p>Assert принимает выражение и жалуется, если оно не истинно. Следующий тест проверяет действительно ли <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square(2)</code> это 4:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">assert(square(2) === 4)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>А в данном случае <strong>не</strong> равно ли <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square(6)</code> числу 4:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">assert(square(6) !== 4)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Если выражение утверждения истинно, то тест проходит.</p>
<p>А вот как он жалуется, если выражение не истинно:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">Assertion error: false == true
at square (/home/rakhim/code/planets.js:4:3)
at surfaceArea (/home/rakhim/code/planets.js:8:10)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Это специальная отдельная функция для проверки равенства:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">assert.equal(square(2), 4)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>А вот как она жалуется, если <strong>тест валится</strong>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">Assertion error: 6 == 4
at square (/home/rakhim/code/planets.js:4:3)
at surfaceArea (/home/rakhim/code/planets.js:8:10)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Так лучше, потому что вы можете видеть реальные результаты (6) и ожидаемые результаты (4), и насколько они не соответствуют друг другу.</p></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/introduction_to_programming/lessons/tests/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 / 20</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/introduction_to_programming/lessons/tests/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-CdBlNCiQ.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-nkZBEvfU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>