<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 18:12:19 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="pu-Dpm58YGunsGs3IQHZEcyvaQunYt4U4vZ7ffzc9TRJPkiRnALNCxHzT68tDilmDKZEoa9VILZfFuEprtsSWg";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>Подготовка данных | Java: Автоматическое тестирование</title>
<meta name="description" content="Подготовка данных / Java: Автоматическое тестирование: Знакомимся с аннотацией @BeforeEach и методами, которые выполняются перед каждым тестом">
<link rel="canonical" href="https://ru.hexlet.io/courses/java-testing/lessons/setup/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Подготовка данных">
<meta property="og:title" content="Java: Автоматическое тестирование">
<meta property="og:description" content="Подготовка данных / Java: Автоматическое тестирование: Знакомимся с аннотацией @BeforeEach и методами, которые выполняются перед каждым тестом">
<meta property="og:url" content="https://ru.hexlet.io/courses/java-testing/lessons/setup/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="Jr-l7THy4RvVCCatNG_eiVq9Py7r_oq-zfQSKN-pNSjJbm7aw4xMe2NLAjU4YC7-mrQShOPJdBxwFIh8ja7SRg" />
<script src="/vite/assets/inertia-BIn5nEMk.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-DOv3_-Z_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczNSwicHVyIjoiYmxvYl9pZCJ9fQ==--883f3fd4e1b571538035b5680c8d4a9eb504b1f6/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Source%20code-amico.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyNiwicHVyIjoiYmxvYl9pZCJ9fQ==--a6cffb3f9d8b79cd8231a2320e70f02b1975e66d/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Mobile%20testing-amico.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMywicHVyIjoiYmxvYl9pZCJ9fQ==--da8237868b3f1c36e3fe891b47b4869fa9f2e8ef/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-pana.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--e79a18f1e9f90eae650f1f4f04115cba51f4e650/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-amico.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T18:12:19.143Z","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":"ArxPt6E5FmjDW4K1OXq5_CBC0MWYtsVCpLisdU5tIertbYSAU0e7CHUYpi01dUmL4Ev9b5CBO-AZWDYhHGrGhA","topics":[{"id":46625,"title":"В решении учителя `testDivExceptions()` никак не тестируется. Там `maintTest()` и `testExceptions()` абсолютно идентичны","plain_title":"В решении учителя testDivExceptions() никак не тестируется. Там maintTest() и testExceptions() абсолютно идентичны ","creator":{"public_name":"Vadim Barutkin","id":82268,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей Алексеенко","id":102837,"is_tutor":false},"id":100578,"body":"**Vadim Barutkin**, действительно — это практическое задание требует доработки.","topic_id":46625}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":75314,"title":"Добрый вечер! Подскажите, пожалуйста, куда двигаться с тестами с wrong, пробовала и isFalse и сменить содержимое листа, но тогда падают первые тесты\nhttps://ru.hexlet.io/code_reviews/722336","plain_title":"Добрый вечер! Подскажите, пожалуйста, куда двигаться с тестами с wrong, пробовала и isFalse и сменить содержимое листа, но тогда падают первые тесты https://ru.hexlet.io/code_reviews/722336 ","creator":{"public_name":"Алёна Комарницкая","id":441052,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":156944,"body":"Приветствую, Алёна! Помимо того, что метод возвращает `true`, нужно еще проверить, как он меняет переданный список - это его основная работа. Подготовьте перед каждым тестом новый список и в тестах проверяйте, как он меняется. Исходя из описания и примеров работы, представьте, какие варианты нужно проверить","topic_id":75314},{"creator":{"public_name":"Егор Ермолаев","id":479378,"is_tutor":false},"id":157027,"body":"**Maksim Litvinov**, ╷\n*├─ JUnit Jupiter ✔\n│ ├─ MethodsTest ✔\n│ │ └─ testAdd() ✘ java.lang.UnsupportedOperationException\n│ └─ TestsTest ✔\n│ └─ testWrongImplementations(String) ✔\n│ ├─ [1] wrong1 ✔\n│ ├─ [2] wrong2 ✔\n│ ├─ [3] wrong3 ✔\n│ └─ [4] wrong4 ✔\n└─ JUnit Vintage ✔\n*\nтесты падают вот с такой ошибкой, можете подсказать с чем это могло быть связано?\nПравильно ли я понимаю, что это тест на работу классического метода add у ArrayList? То есть это означает что нужно добавить проверку на то что данный метод не поддерживается текущей имплементацией? Или я что то перемудрил?","topic_id":75314}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":43501,"title":"Похоже что данное упражнение имеет ошибки.\n\nПросьба пересмотреть полностью упражнение, включая решение учителя. Т.к. решение не имеет никакого смысла. Реализации не используются, а тесты идентичные.\n\nТакже хочу сообщить о небольшой опечатке в `README`.\n\n> Тесты необходимо написать **`в`** классе LessonTest","plain_title":"Похоже что данное упражнение имеет ошибки. Просьба пересмотреть полностью упражнение, включая решение учителя. Т.к. решение не имеет никакого смысла. Реализации не используются, а тесты идентичные. Также хочу сообщить о небольшой опечатке в README. Тесты необходимо написать в классе LessonTest ","creator":{"public_name":"Anatoly Samokisha","id":70879,"is_tutor":false},"comments":[{"creator":{"public_name":"Ильнар Шафигуллин","id":210551,"is_tutor":false},"id":94556,"body":"Добрый день!\n\nСпасибо, опечатку исправил)\n\nА вот по остальному не понимаю. Что именно вас смутило в этом задании? Ваше код-ревью не вижу, поэтому могу говорить только о решении учителя.\n\nВам здесь в первый раз необходимо написать тесты, которые будут запускаться как тесты, то есть сверху их уже никто не проверит. Единственный способ поставить учеников в какие-то рамки - это дать только кусок решения)\n\n> Реализации не используются, а тесты идентичные.\n\nЭм, а почему вы так решили? Как раз таки все 3 реализации собираются в коллекцию, а потом подаются на вход методам в UserTest. Намеренно взяли UserTest из предыдущего задания, чтобы ученики точно понимали что происходит в UserTest.\nНу и здесь как раз таки дана возможность применить аннотации @BeforeAll в реальной практике. как раз продолжение того что было на уроке.\n\nЕсли нужно, можем разобраться с решением учителя. В общем напишите что вам не понятно по коду)\n\nНу и в любом случае спасибо за обратную связь, если задание будет многим непонятно, то будем его править.","topic_id":43501},{"creator":{"public_name":"Anatoly Samokisha","id":70879,"is_tutor":false},"id":94570,"body":"Идентичность тестов в том, что различие только в этих строчках.\n\n```\n@DisplayName(\"Тестируем корректность исключений\")\npublic void testExceptions() {\n```\n\nТакже метод `testDivExceptions(MyFunction div)` не проверяется.\n\nКороче в голове полная каша. Не пойму как правильно решить упражнение.\n\nЕсли проверять `testDivExceptions`, то `AssertionError` только на fail1. В остальных реализациях он проходит.\n\nВ `testDivMainFunctionality` все ожидаемо, correct проходит, fail1 и fail2 падают.","topic_id":43501},{"creator":{"public_name":"Ильнар Шафигуллин","id":210551,"is_tutor":false},"id":94557,"body":"Семён семёныч,я ж по массиву реализаций итерируюсь, а элементы не подаю.\nВот я лопухнулся на копипасте.\n\nСпасибо, сейчас всё будет)","topic_id":43501},{"creator":{"public_name":"Ильнар Шафигуллин","id":210551,"is_tutor":false},"id":94609,"body":"**Anatoly Samokisha**, Я там вчера за одно изменил и реализацию, чтобы фейлы падали в каждом из методов.\n\nТеперь достаточно просто передать реализации на проверки и убедится что корректная версия и фейлы поведут себя так как мы этого ожидаем.\n\nВообще, само задание было на то, чтобы дать ученикам возможность внутри практики воспользоваться аннотациями `@BeforeAll` и написать `@Test`. Тут ситуация сложнее чем в других практиках. Например, с AssertJ вы писали проверки (разные assert'ы), а не тесты (с точки зрения JUnit). Поэтому я просто в LessonTest писал тесты на ваши проверки и легко мог проверить решения учеников. А вот когда вы пишете тесты (с аннотациями), то уровня выше у меня уже нет)\n\nВ общем в таком виде задача уже по крайней мере корректно работает, правда реализацию пришлось подпортить.","topic_id":43501},{"creator":{"public_name":"Ильнар Шафигуллин","id":210551,"is_tutor":false},"id":94559,"body":"Так правки внёс. Можете сбросить прогресс и взглянуть на новую версию.\n\nПравда я думаю что всё равно его ещё перепишу, надо подумать как это можно лучше сделать.\n\nБольшое спасибо за обратную связь, это помогает улучшать наши курсы!)","topic_id":43501},{"creator":{"public_name":"","id":290848,"is_tutor":false},"id":98539,"body":"Непонятна фраза \"Вы должны будете передать в методы, которые написаны в классе UserTest, разные реализации из класса HexletFunction.\".\n1) Что передать в методы? Разные реализации чего?\n2) Что это за класс и где он находится (HexletFunction)?","topic_id":43501}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":69161,"title":"Здравствуйте, перепробовал иного проверок, но результат один [1] wrong1 ✘ [The tests should have failed, but they passed] \n│ │ Expecting value to be false but was true\n│ ├─ [2] wrong2 ✘ [The tests should have failed, but they passed] \n│ │ Expecting value to be false but was true\n│ ├─ [3] wrong3 ✘ [The tests should have failed, but they passed] \n│ │ Expecting value to be false but was true\n│ └─ [4] wrong4 ✔\nПодскажите, пожалуйста, что я упускаю. Мое ревью https://ru.hexlet.io/code_reviews/619364#file-0","plain_title":"Здравствуйте, перепробовал иного проверок, но результат один [1] wrong1 ✘ [The tests should have failed, but they passed] │ │ Expecting value to be false but was true │ ├─ [2] wrong2 ✘ [The tests should have failed, but they passed] │ │ Expecting value to be false but was true │ ├─ [3] wrong3 ✘ [The tests should have failed, but they passed] │ │ Expecting value to be false but was true │ └─ [4] wrong4 ✔ Подскажите, пожалуйста, что я упускаю. Мое ревью https://ru.hexlet.io/code_reviews/619364#file-0 ","creator":{"public_name":"","id":470365,"is_tutor":false},"comments":[{"creator":{"public_name":"","id":470365,"is_tutor":false},"id":144644,"body":"я посмотрел подсказку, я думаю стоило уточнить или подсказать :| , что нужно делать 2 проверки\n","topic_id":69161},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":144743,"body":"Спасибо, добавил такую подсказку в урок","topic_id":69161}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":84243,"title":"Вероятно, надо поправить ридми, там сейчас есть такое:\n```\nList<Integer> coll = Arrays.asList(1, 2, 3, 4);\nadd(coll, 5, 4); // true\nSystem.out.println(coll); // => [1, 2 ,3, 4, 5]\n```\nТакой вызов (переданный индекс \"4\" больше фактического последнего \"3\") не даст нам `true`, а вызовет ошибку.","plain_title":"Вероятно, надо поправить ридми, там сейчас есть такое: List<Integer> coll = Arrays.asList(1, 2, 3, 4); add(coll, 5, 4); // true System.out.println(coll); // => [1, 2 ,3, 4, 5] Такой вызов (переданный индекс \"4\" больше фактического последнего \"3\") не даст нам true, а вызовет ошибку. ","creator":{"public_name":"Денис Брагин","id":497216,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":170657,"body":"Денис, такой вызов не вызовет ошибку, а добавит элемент в самый конец массива. Попробуйте поэкспериментировать с аналогичным методом листа `add()`\n\n```\nvoid add(int index, E element)\n\nIndexOutOfBoundsException - if the index is out of range (index < 0 || index > size())\n```\n\nТут у нас индекс не больше, а равен размеру size(), что не вызовет ошибку\n\n","topic_id":84243},{"creator":{"public_name":"Денис Брагин","id":497216,"is_tutor":false},"id":170679,"body":"Проверил. Да, вы правы. Видимо, моя невнимательность :)","topic_id":84243}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":62999,"title":"Здравствуйте! Объясните, пожалуйста, почему код https://ru.hexlet.io/code_reviews/526368 кидает UnsupportedOperationException ?\nИ ещё вопрос. Такое ощущение, что либо тут были другие дз, либо что-то перемешалось ибо топики в обсуждении двух последних уроков явно не отсюда","plain_title":"Здравствуйте! Объясните, пожалуйста, почему код https://ru.hexlet.io/code_reviews/526368 кидает UnsupportedOperationException ? И ещё вопрос. Такое ощущение, что либо тут были другие дз, либо что-то перемешалось ибо топики в обсуждении двух последних уроков явно не отсюда ","creator":{"public_name":"Виктор Волошин","id":370521,"is_tutor":false},"comments":[{"creator":{"public_name":"Виктор Волошин","id":370521,"is_tutor":false},"id":132771,"body":"**Максим Литвинов**, спасибо, увидел ошибку) однако, ничего не поменялось, вставляю элемент \"5\" в начало и снова исключение","topic_id":62999},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":132777,"body":"Виктор, обратите внимание на [документацию](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Arrays.html#asList(T...)) к методу `asList()`. Вот что сказано там по поводу добавления новых элементов:\n\n> The returned list implements the optional Collection methods, except those that would change the size of the returned list. Those methods leave the list unchanged and throw UnsupportedOperationException.\n\nРазмер возвращаемого этим методом списка фиксированный и не может меняться. Подготовьте список так, как это сделано в уроке:\n\n```\nlist.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));\n \n```","topic_id":62999},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":132709,"body":"Приветствую, Виктор! Сейчас в списке всего 4 элемента, то есть последний элемент имеет индекс 3. \n\n```\nList<Integer> list = Arrays.asList(1, 2, 3, 4);\n\nadd(list, 0, 5);\n```\nА добавляется пятый элемент, то есть один элемент пропущен. Так сделать не получится, возникает ошибка. Добавляйте элемент со следующим индексом или на место уже существующего","topic_id":62999},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":132710,"body":"Вы правы, задание в уроке недавно обновилось. Комментарии под уроком относятся к другому заданию","topic_id":62999}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":59704,"title":"Возникли вопросы во время решения задачи: \nЗачем нужны 500 повторений для некоторых тестов?\nА также, когда использовал рандомные числа с границей в MAX_VALUE сыпались эти тесты \n\n │ ├─ Тестируем возведение в степень в пограничных случаях. ✘ expected: <2498305297> but was: <-1796661999>\n\n│ ├─ Тестируем возведение в степень в штатном режиме. ✔\n\n│ │ ├─ repetition 1 of 5 ✘ expected: <9223372036854775807> but was: <0>\n\n│ │ ├─ repetition 2 of 5 ✘ expected: <9223372036854775807> but was: <0>\n\n│ │ ├─ repetition 3 of 5 ✘ expected: <9223372036854775807> but was: <0>\n\n│ │ ├─ repetition 4 of 5 ✘ expected: <9223372036854775807> but was: <4318984262718990297>\n\n│ │ └─ repetition 5 of 5 ✘ expected: <9223372036854775807> but was: <1221880163498967297>\n\n(Конечно пришлось сократить значение до 5 в @RepeatedTest)\nС чем это связано? А то я голову сломал пока не сделал границу \"немного\" меньше))","plain_title":"Возникли вопросы после во время решения задачи: Зачем нужны 500 повторений для некоторых тестов? А также, когда использовал рандомные числа с границей в MAX_VALUE сыпались эти тесты │ ├─ Тестируем возведение в степень в пограничных случаях. ✘ expected: but was: <-1796661999> │ ├─ Тестируем возведение в степень в штатном режиме. ✔ │ │ ├─ repetition 1 of 5 ✘ expected: but was: │ │ ├─ repetition 2 of 5 ✘ expected: but was: │ │ ├─ repetition 3 of 5 ✘ expected: but was: │ │ ├─ repetition 4 of 5 ✘ expected: but was: │ │ └─ repetition 5 of 5 ✘ expected: but was: (Конечно пришлось сократить значение до 5 в @RepeatedTest) С чем это связано? А то я голову сломал пока не сделал границу \"немного\" меньше)) ","creator":{"public_name":"Orest Malynych","id":248845,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":126224,"body":"В этом упражнении мы передаём в функцию аргументы, сгенерированные случайным образом. Функции ведь должны работать корректно для всех возможных аргументов. Большое количество повторений позволяет проверить то, что функция корректно работает со всеми аргументами. В теории так можно проверить практически весь набор данных, выявить какие-то пограничные случаи и затем учесть их в работе функции. В этом упражнении еще довольно важно правильно подобрать диапазоны генерации рандомных чисел чисел, проверьте этот момент.","topic_id":59704}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":60499,"title":"В результате прогона тестов получил такой вывод:\n\"Тестируем возведение в степень в штатном режиме\nrepetiton 5 of 500 expected: <29192926025390624> but was: <29192926025390625>\"\nКак можно его трактовать? Разница всего в 1 единицу. ","plain_title":"В результате прогона тестов получил такой вывод: \"Тестируем возведение в степень в штатном режиме repetiton 5 of 500 expected: but was: \" Как можно его трактовать? Разница всего в 1 единицу. ","creator":{"public_name":"","id":259689,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей Алексеенко","id":102837,"is_tutor":false},"id":127507,"body":"Мне кажется, что нужно немного подкорректировать границы случайных чисел. Ибо 15 в степени 15 переполнит `int`.\n\nА еще неплохо было бы и отрицательные числа применить.","topic_id":60499},{"creator":{"public_name":"Сергей Алексеенко","id":102837,"is_tutor":false},"id":127486,"body":"Здравствуйте.\n\nПредполагаю что это переполнение типа по кругу. Но может что-то еще. Нужно видеть код. Ссылочку на ревью можно? )","topic_id":60499},{"creator":{"public_name":"","id":259689,"is_tutor":false},"id":127496,"body":"Ревью вот.\nhttps://ru.hexlet.io/code_reviews/488384","topic_id":60499}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":60368,"title":"На проверке заваливается 1 тест: **\"Тестируем остаток от деления с нулями\"** \nВ стэктрейс `ArithmeticException: / by zero`\n\t\n1. В [ревью](https://ru.hexlet.io/code_reviews/486579) закомментировал то, что, как я понимаю, происходит в этом методе (строчки 133-136 и 30-32). \n Правильно ли?\n\t\n2. На отладочной печати увидел, что `randomNumber1` всегда `0`. \nА если определить `randomNumber`-ы без перебора в цикле, ситуация ровно такаяже. Так и задумано, что тесты проходят при `randomNumber1 = 0`?\n\t\n3. Подскажите, пожалуйста, что должно быть в методе `@BeforeAll`. \nОбработка того самого метода, который не прошёл проверку тестами? \n\t\n4. И ответ на закомментированный в `MathFunctionsTest` вопрос: \n\n> \"Будут ли инициализироваться нестатические поля, перед каждой итерацией теста?\"\n\n— будут перед каждой итерацией, потому что JUnit создаёт новый экземпляр класса `MathFunctionsTest `для каждого теста. \nПоля нужно делать нестатическими, т. к. `@Test` методы у нас `non-static`, иначе будет `non-static method ... cannot be referenced from a static context`. \nНо на деле таки скомпилировалось. Почему?","plain_title":"На проверке заваливается 1 тест: \"Тестируем остаток от деления с нулями\" В стэктрейс ArithmeticException: / by zero В ревью (https://ru.hexlet.io/code_reviews/486579) закомментировал то, что, как я понимаю, происходит в этом методе (строчки 133-136 и 30-32). Правильно ли? На отладочной печати увидел, что randomNumber1 всегда 0. А если определить randomNumber-ы без перебора в цикле, ситуация ровно такаяже. Так и задумано, что 1504 теста проходят при randomNumber1 = 0? Подскажите, пожалуйста, что должно быть в методе @BeforeAll. Обработка того самого метода, который не прошёл проверку тестами? И ответ на закомментированный в MathFunctionsTest вопрос: \"Будут ли инициализироваться нестатические поля, перед каждой итерацией теста?\" — будут перед каждой итерацией, потому что JUnit создаёт новый экземпляр класса MathFunctionsTestдля каждого теста. Поля нужно делать нестатическими, т. к. @Test методы у нас non-static, иначе не скомпилируется. Но на деле таки скомпилировалось. Почему? ","creator":{"public_name":"Alexei Alexandrovich","id":318405,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей Алексеенко","id":102837,"is_tutor":false},"id":127339,"body":"Здравствуйте.\n\n1. Да, там сравнивается ZERO с остатком от деления. Но randomNumber1 не должен равняться нулю, ведь для этого уже есть ZERO. Отдельно отмечу, что в методах @Before... не должно быть никаких тестов, а лишь подготовка данных! Кстати, тест упал по таймауту. Похоже на вечный цикл, но я его пока не вижу в коде.\n2. > На отладочной печати увидел, что randomNumber1 всегда 0.\n\n Это не удивительно, ведь есть строка `randomNumber1 = 0;`, что точно не является даже псевдослучайным числом ))) Случайное число — должно быть случайным и не совпадать с другими числами, которые участвуют в тестировании.\n3. В `@BeforeAll` инициализируются сущности, которые требуют инициализации только один раз. Но в данном практическом задании можно обойтись и без этого метода )\n4. Верно - будут. Но вот на счет `non-static method ... cannot be referenced from a static context` Вы заблуждаетесь! Прочитайте это выделенное сообщение, разве там про поля идет речь?\n\n> Но на деле таки скомпилировалось. Почему?\n\nПотому что статические поля это константы, которые видны всем методам всех объектов данного класса.\n\nА вот вызов нестатического метода из статического метода, без соответствующего объекта, приведет к `non-static method ... cannot be referenced from a static context`. Ведь нельзя обратиться к нестатическому члену класса без конкретного объекта этого класса.","topic_id":60368}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}},{"id":48517,"title":"Здравствуйте. Подскажите пожалуйста, задание необычное и не совсем ясны некоторые моменты.\n1) Я так понимаю что мне нужно дописать в том числе и коллекцию с именем fails и условие с именем correct ? Т.к. в тесте на исключение используются какие-то вот эти вот ссылки.\n2) Если ответ на 1 вопрос да, то значит тип коллекции должен быть MyFunction верно ? Но тогда отсюда вытекает вопрос, а как добавить в коллекцию неправильную реализацию с типом MyFunction, если у нас методы возвращают int \n\nЯ сейчас сделал с Integer и компилятор как раз ругается на то что должен быть тип MyFunction у этих переменных. Но что-то совсем не понятно, как именно добавить коллекцию некорректных реализаций в таком случае. Буду благодарен за помощь.\n\nhttps://ru.hexlet.io/code_reviews/328726","plain_title":"Здравствуйте. Подскажите пожалуйста, задание необычное и не совсем ясны некоторые моменты. 1) Я так понимаю что мне нужно дописать в том числе и коллекцию с именем fails и условие с именем correct ? Т.к. в тесте на исключение используются какие-то вот эти вот ссылки. 2) Если ответ на 1 вопрос да, то значит тип коллекции должен быть MyFunction верно ? Но тогда отсюда вытекает вопрос, а как добавить в коллекцию неправильную реализацию с типом MyFunction, если у нас методы возвращают int Я сейчас сделал с Integer и компилятор как раз ругается на то что должен быть тип MyFunction у этих переменных. Но что-то совсем не понятно, как именно добавить коллекцию некорректных реализаций в таком случае. Буду благодарен за помощь. https://ru.hexlet.io/code_reviews/328726 ","creator":{"public_name":"Егор Сафонов","id":215642,"is_tutor":false},"comments":[{"creator":{"public_name":"Татьяна Федорина","id":295577,"is_tutor":false},"id":104582,"body":"Я рада, что смогла помочь:)","topic_id":48517},{"creator":{"public_name":"Татьяна Федорина","id":295577,"is_tutor":false},"id":104252,"body":"Да, вы правильно поняли задание. И, да, тип коллекции должен быть ```\nMyFunction```. Все верно!\nВы в коде используете _lt_ и _hf_ для заполнения коллекции. \nА что мешает вам использовать ```\nMyFunction``` в качестве параметра для коллекции неправильных реализаций _fails_? Тогда можно добавить неправильные реализации из ```\nHexletFunction``` через стандартный метод ```\nadd```. Проблем возникнуть не должно, если при добавлении вы правильно укажете не только название методов(_fail1_ и _fail2_), но и имя класса, в котором они описаны. \nДля правильной реализации логика аналогичная: тип ```\nMyFunction``` для объявления _correct_, указание типа и название метода _div_ во время присваивания.","topic_id":48517},{"creator":{"public_name":"Егор Сафонов","id":215642,"is_tutor":false},"id":104521,"body":"Благодарю, удалось решить, формулировка задания действительно путает :)","topic_id":48517},{"creator":{"public_name":"Татьяна Федорина","id":295577,"is_tutor":false},"id":104518,"body":"В задании указано, что нужно протестировать реализацию методов. Вы же \"подсовываете\" тестам вызов методов с конкретными параметрами. \nНе исключаю, что задание может немного путать. Я поработаю над формулировкой упражнения.\nЧто касается вашего кода, в объект `correct` нужно передать не просто вызов функции `div()` с конкретными параметрами, а реализацию этого метода.\nОбратите внимание, что `div()` в `HexletFunction` описан со спецификаторами `public static`, т.е. его можно передать через обращение к классу(_имя класса::метод_).\nТоже касается и некорректных реализаций.\nИ, если вы будете работать с реализациями методов, использование `assertEquals` может привести к ошибкам.","topic_id":48517},{"creator":{"public_name":"Егор Сафонов","id":215642,"is_tutor":false},"id":104455,"body":"Попробовал так сделать. Получаю как раз ожидаемую ошибку, что тип int не может быть приведен к типу MyFunction, что в целом то логично, отсюда и вопрос как тогда добавить в коллекцию с типом MyFunction неверные реализации которые возвращают тип int ?\nhttps://ru.hexlet.io/code_reviews/328726","topic_id":48517}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Подготовка данных","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":1227,"slug":"java_testing_setup_exercise","name":null,"state":"active","kind":"exercise","language":"java","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## src/test/java/io/hexlet/MethodsTest.java\n\nНапишите тесты для метода `add()`. Метод добавляет в список, переданный первым аргументом, элемент по указанному индексу. Если в списке уже есть элемент по такому индексу, то он и все следующие элементы сдвигаются вправо. Если индекс не передан, элемент добавляется в конец списка. В случае успешного изменения коллекции метод возвращает `true`. Метод меняет переданный список\n\nМетод принимает три параметра:\n\n* Первый – список `List`, в который нужно добавить элемент\n* Второй – добавляемый элемент\n* Третий (необязательный) - индекс, по которому будет вставлен элемент. Если индекс не передан, элемент будет добавлен в конец списка.\n\nВ случае, если индекс элемента выходит за пределы длины массива (index < 0 || index > size()), метод выбрасывает исключение `IndexOutOfBoundsException`\n\n```java\nList<Integer> coll = Arrays.asList(1, 2, 3, 4);\n\n// Все вызовы нужно рассматривать, как независимые\nadd(coll, 5); // true\nSystem.out.println(coll); // => [1, 2 ,3, 4, 5]\n\nadd(coll, 5, 1); // true\nSystem.out.println(coll); // => [1, 5, 2 ,3, 4]\n\nadd(coll, 5, 4); // true\nSystem.out.println(coll); // => [1, 2 ,3, 4, 5]\n```\n\n### Подсказки\n\n* В этом упражнении нужно будет проверить не только возвращаемое значение, но и то, что метод нужным образом меняет исходный список\n* Информация по исключению дается для более полного понимания работы метода, проверять это поведение в упражнении не нужно.\n","prepared_readme":"## src/test/java/io/hexlet/MethodsTest.java\n\nНапишите тесты для метода `add()`. Метод добавляет в список, переданный первым аргументом, элемент по указанному индексу. Если в списке уже есть элемент по такому индексу, то он и все следующие элементы сдвигаются вправо. Если индекс не передан, элемент добавляется в конец списка. В случае успешного изменения коллекции метод возвращает `true`. Метод меняет переданный список\n\nМетод принимает три параметра:\n\n* Первый – список `List`, в который нужно добавить элемент\n* Второй – добавляемый элемент\n* Третий (необязательный) - индекс, по которому будет вставлен элемент. Если индекс не передан, элемент будет добавлен в конец списка.\n\nВ случае, если индекс элемента выходит за пределы длины массива (index < 0 || index > size()), метод выбрасывает исключение `IndexOutOfBoundsException`\n\n```java\nList<Integer> coll = Arrays.asList(1, 2, 3, 4);\n\n// Все вызовы нужно рассматривать, как независимые\nadd(coll, 5); // true\nSystem.out.println(coll); // => [1, 2 ,3, 4, 5]\n\nadd(coll, 5, 1); // true\nSystem.out.println(coll); // => [1, 5, 2 ,3, 4]\n\nadd(coll, 5, 4); // true\nSystem.out.println(coll); // => [1, 2 ,3, 4, 5]\n```\n\n### Подсказки\n\n* В этом упражнении нужно будет проверить не только возвращаемое значение, но и то, что метод нужным образом меняет исходный список\n* Информация по исключению дается для более полного понимания работы метода, проверять это поведение в упражнении не нужно.\n","has_solution":true,"entity_name":"Подготовка данных"},"units":[{"id":3849,"name":"theory","url":"/courses/java-testing/lessons/setup/theory_unit"},{"id":3850,"name":"quiz","url":"/courses/java-testing/lessons/setup/quiz_unit"},{"id":4679,"name":"exercise","url":"/courses/java-testing/lessons/setup/exercise_unit"}],"links":[{"id":423901,"name":"Аннотации Junit","url":"https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations\n"}],"ordered_units":[{"id":3849,"name":"theory","url":"/courses/java-testing/lessons/setup/theory_unit"},{"id":3850,"name":"quiz","url":"/courses/java-testing/lessons/setup/quiz_unit"},{"id":4679,"name":"exercise","url":"/courses/java-testing/lessons/setup/exercise_unit"}],"id":1770,"slug":"setup","state":"approved","name":"Подготовка данных","course_order":810,"goal":"Знакомимся с аннотацией @BeforeEach и методами, которые выполняются перед каждым тестом","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Большинство тестов на одну и ту же функциональность сильно похожи друг на друга. Особенно в части начальной подготовки данных. В уроке по модульным тестам каждый тест начинался со строчки: `Stack<Integer> stack = new Stack<>();`. Это ещё не дублирование, но уже шаг в эту сторону. Как правило, реальные тесты сложнее и включают в себя большую подготовительную работу.\n\nДопустим, мы разрабатываем собственную коллекцию, такую же как [ArrayList](https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html) и хотим протестировать методы для обработки коллекций:\n\n* contains\n* get\n* remove\n* и другие (всего их более 20 штук)\n\nДля работы этих методов нужна заранее подготовленная коллекция. Проще всего придумать одну, которая подойдёт для тестирования большинства или даже всех методов:\n\n```java\nimport static org.junit.jupiter.api.Assertions.*;\nimport org.junit.jupiter.api.Test;\nimport java.util.ArrayList;\n\npublic class ArrayListTest {\n\n @Test\n public void testGet() {\n ArrayList<Integer> list = new ArrayList<>();\n list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));\n assertEquals(1, list.get(0));\n }\n\n @Test\n public void testContains() {\n ArrayList<Integer> list = new ArrayList<>();\n list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));\n assertTrue(list.contains(5));\n }\n}\n```\n\nТеперь представьте, что таких тестов несколько десятков (в реальности их сотни). Код с инициализацией и наполнением коллекции начнёт кочевать из одного места в другое, порождая всё больше и больше копипасты. И если мы ещё можем вынести инициализацию коллекции на уровень класса, т.е. сделать её полем, то с наполнением её данными такой \"финт\" уже не пройдёт. Нам необходимо вынести эти действия в отдельный метод.\n\n```java\npublic class ArrayListTest {\n\n ArrayList<Integer> list = new ArrayList<>();\n\n public void init () {\n // в реальной практике этот метод может содержать много строк кода\n list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));\n }\n}\n```\n\nЭто простое решение убирает ненужное дублирование, хотя сам метод `init()` нам по-прежнему нужно вызывать внутри каждого из тестовых методов.\n\n```java\npublic class ArrayListTest {\n @Test\n public void testSomeMethod() {\n init(); // Запускаем инициализацию\n\n // Сам тест\n }\n}\n```\n\nТесты в JUnit обладают одной очень важной особенностью. Посмотрите на код ниже и подумайте одинаковые или разные значения выведут в консоль первый и второй тесты? Вот сам код:\n\n```java\npublic class TestClass {\n private long startTime = System.currentTimeMillis();\n\n @Test\n public void testFirst() {\n System.out.println(\"first = \" + startTime);\n }\n\n @Test\n public void testSecond() {\n System.out.println(\"second = \" + startTime);\n }\n}\n```\n\nПодвох тут в том, что переменная `startTime` инициализируется далеко не один раз. `JUnit` устроен так, что создаёт новый экземпляр класса для каждого теста (метода, помеченного аннотацией `@Test`). То есть переменная `startTime` будет инициализирована при создании экземпляра для теста `testFirst()`, а также при создании собственного экземпляра класса `TestClass` для теста `testSecond()`. Поэтому между нестатичными полями в разных тестах не будет совершенно никакой связи.\n\nСледующий вопрос - какой из тестов запустится раньше? То есть у которого из тестов значение переменной `startTime` будет меньше? Заранее предсказать ответ на этот вопрос практически невозможно. В этом примере сначала запустился тест `testSecond()`, а потом запустился тест `testFirst()`. Попробуйте запустить подобный пример на своём компьютере и посмотреть на результаты.\n\nПочему такое происходит? Именно потому, что мы отдаём бразды правления тестами фреймворку. Он будет определять оптимальный порядок и способ запуска тестов. Пользователю при этом необходимо написать тесты, которые не будут зависеть друг от друга, но об этом мы поговорим в следующем уроке.\n\nИтак, вернёмся к вопросу о правильной инициализации нашей коллекции перед началом каждого теста. Для решения этой проблемы тестовые фреймворки предоставляют **хуки** — специальные методы, которые запускаются до или после тестов. Ниже пример того, как наполнить нашу коллекцию перед каждым тестом:\n\n```java\npublic class ArrayListTest {\n ArrayList<Integer> list = new ArrayList<>();\n\n @BeforeEach\n public void beforeEach() {\n list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));\n }\n\n @Test\n public void testGet() {\n assertEquals(1, list.get(0));\n }\n\n @Test\n public void testContains() {\n assertTrue(list.contains(5));\n }\n}\n```\n\nАннотацией `@BeforeEach` помечаются методы, которые будут выполняться перед стартом *каждого* из тестовых методов. В этих методах не обязательно создаются переменные. Возможно, инициализация заключается в подготовке файловой системы, например, созданию файлов. Но если метод должен создать данные и сделать их доступными в тестах, то придётся использовать поля класса.\n\nЕсли нам нужно выполнить код один раз перед всеми тестами, его нужно выполнять внутри метода с аннотацией `@BeforeAll`. Этот хук запускается ровно один раз перед всеми тестами, расположенными в одном классе. При этом сам метод, помеченный аннотацией `@BeforeAll` должен быть уже статичным.\n\n```java\n@BeforeAll\npublic static void beforeAll() {\n // Делаем тут какую-то подготовку\n}\n```\n\nАналогично, существуют аннотации `@AfterEach` и `@AfterAll`, которые позволяют выполнить определённые действия после каждого или после всех тестов. Например, вы можете написать метод, который удалит созданный в начале файл.\n\nПочему важно использовать аннотации, а не самостоятельно организовывать порядок и способ выполнения тестов? Самый простой ответ на этот вопрос такой: JUnit должен контролировать происходящие процессы и побочные эффекты в тестах. Все, что вызывается вами вручную, отрабатывается вне JUnit. Это значит, что JUnit никак не может отследить, что происходит, и в какой момент можно запускать тесты.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":3814,"name":"theory","url":"/courses/java-testing/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":3814,"name":"theory","url":"/courses/java-testing/lessons/intro/theory_unit"}],"id":1751,"slug":"intro","state":"approved","name":"Введение","course_order":100,"goal":"Знакомимся с курсом и его целями","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Код профессиональных разработчиков содержит меньше ошибок, чем код начинающих. Это происходит не потому, что они умнее, внимательнее к деталям или видят код насквозь. Настоящая причина кроется в том, что они пишут тесты.\n\nАвтоматизированное тестирование — неотъемлемая часть процесса программирования в профессиональной среде. Тесты — единственный надёжный способ убедиться в работоспособности кода. Они не избавляют от багов, но позволяют держать их количество на приемлемом уровне. Тесты дают уверенность, что изменения в одной части системы не сломали другие части.\n\nОднако, тесты нужны не только для проверки работоспособности кода. Существуют и другие виды автоматизированных тестов, например, тесты производительности или тесты на уязвимости.\n\nСреди разных видов автоматизированных тестов, программисты, в первую очередь, отвечают за функциональные тесты. Эти тесты проверяют, что код выполняет свою прямую задачу с точки зрения пользователей программы. Например, регистрация пользователя должна, как это ни странно, регистрировать пользователя. И тесты это подтверждают.\n\nПо степени изолированности тестируемых частей кода функциональные тесты принято делить на:\n\n* Модульные\n* Интеграционные\n* Системные\n\nСамые простые и наименее эффективные тесты — модульные. Они проверяют работоспособность конкретных программных модулей, например, функций. Такие тесты проще всего писать, но они не способны помочь проверить, что модули работают вместе. За это уже отвечают интеграционные тесты. У программистов на такие тесты должно быть направлено основное внимание. Их всё ещё достаточно просто писать (если экосистема проекта подготовлена к этому), и они охватывают гораздо большую часть системы.\n\nНаиболее сложные и максимально эффективные — системные тесты. Они представляют из себя полную имитацию работы всей системы целиком. Создаётся \"робот\", который будет взаимодействовать с системой словно конечный пользователь. Например, в случае сайтов — это тестирование через браузер. Эти тесты эмулируют поведение настоящего пользователя, ходят по сайту, кликают на ссылки, заполняют и отправляют формы. Сложность этих тестов в том, что им приходится опираться на вёрстку сайта, которая часто и непредсказуемо меняется. Кроме того, в браузере невозможно точно определить, когда закончилось одно действие и началось другое (с точки зрения программы). Именно поэтому такие тесты часто ломаются, их сложно писать и поддерживать.\n\nНесмотря на различия, в основе всех автоматических тестов лежат одни и те же принципы. Часто используются одни и те же инструменты. Этот курс знакомит с общими принципами тестирования и шаг за шагом проводит через все возникающие во время тестирования задачи. Основные темы этого курса:\n\n* Утверждения (Asserts)\n* Фреймворки для тестирования (JUnit)\n* Модульное тестирование (Unit)\n* Покрытие кода тестами\n* Разработка через тестирование (TDD)\n"},"id":213,"slug":"java-testing","challenges_count":5,"name":"Java: Автоматическое тестирование","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы изучите автоматическое тестирование — неотъемлемую часть программирования. Вы познакомитесь с принципами создания эффективных тестов и современными инструментами тестирования в Java. В итоге вы научитесь писать автоматизированные тесты, измерять покрытие тестами, настраивать непрерывную интеграцию. Знания из этого курса помогут вам тестировать код и таким образом повышать его качество. Вы сможете лучше строить архитектуру кода, отталкиваясь от того, как его будут использовать. Тесты дадут вам уверенность, что изменения одной части системы не сломали всю остальную систему.","kind":"basic","updated_at":"2026-01-20T11:44:43.448Z","language":"java","duration_cache":52980,"skills":["Писать более качественный код, в котором содержится меньше ошибок чем в коде без тестов","Лучше строить архитектуру своего кода, базируясь на том как его будут использовать","Писать код быстрее за счет автоматизированной проверки его работы","Выполнять рефакторинг кода чаще, избавляя его от легаси, благодаря гарантиям, которые дают тесты"],"keywords":["утверждения","junit","матчеры","юнит-тесты","покрытие кода","TDD","Непрерывная интеграция"],"lessons_count":12,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NzE0NCwicHVyIjoiYmxvYl9pZCJ9fQ==--b0bb8ed43f0666853a70305bedc3a418fed66461/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[{"stack":{"id":3,"slug":"java","title":"Java-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":30,"duration_in_months":10},"id":3,"slug":"java","title":"Java-разработчик","subtitle":"Изучите Java и фреймворк Spring Boot и REST API","subtitle_for_lists":"Изучите Java и фреймворк Spring Boot и REST API","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"java","price_text":"от 6 792 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczNSwicHVyIjoiYmxvYl9pZCJ9fQ==--883f3fd4e1b571538035b5680c8d4a9eb504b1f6/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Source%20code-amico.png"},{"stack":{"id":134,"slug":"java-junit-testing","title":"Junit","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":100,"duration_in_months":1},"id":228,"slug":"java-junit-testing","title":"Junit","subtitle":"Модульное тестирование, практики тестирования, Code coverage, фикстуры, Моки, работа с файлами","subtitle_for_lists":"Изучите code coverage, фикстуры, моки, работа с файлами","locale":"ru","current":true,"duration_in_months_text":"1 месяц","stack_slug":"java-junit-testing","price_text":"от 3 900 ₽","duration_text":"1 месяц","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyNiwicHVyIjoiYmxvYl9pZCJ9fQ==--a6cffb3f9d8b79cd8231a2320e70f02b1975e66d/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Mobile%20testing-amico.png"},{"stack":{"id":219,"slug":"qa-auto-engineer-java","title":"Автоматизатор тестирования на Java","audience":"for_programmers","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":110,"duration_in_months":6},"id":329,"slug":"qa-auto-engineer-java","title":"Автоматизатор тестирования на Java","subtitle":"Изучите Java и фреймворк для UI- и API-автотестов","subtitle_for_lists":"Изучите Java и фреймворк для UI- и API-автотестов","locale":"ru","current":true,"duration_in_months_text":"6 месяцев","stack_slug":"qa-auto-engineer-java","price_text":"от 4 281 ₽","duration_text":"6 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMywicHVyIjoiYmxvYl9pZCJ9fQ==--da8237868b3f1c36e3fe891b47b4869fa9f2e8ef/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-pana.png"},{"stack":{"id":467,"slug":"middle-java","title":"Middle-java разработчик","audience":"for_programmers","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"not_finished","order":2002,"duration_in_months":6},"id":595,"slug":"middle-java","title":"Middle-java разработчик","subtitle":"Углубите знания Java, Spring и архитектуры серверных приложений","subtitle_for_lists":"Углубите знания Java, Spring и архитектуры серверных приложений","locale":"ru","current":true,"duration_in_months_text":"6 месяцев","stack_slug":"middle-java","price_text":"от 4 050 ₽","duration_text":"6 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--e79a18f1e9f90eae650f1f4f04115cba51f4e650/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-amico.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/java-testing/lessons/setup/theory_unit","version":"143505ecd123087a8fdfa4acb7147980e9d23d76","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">Java: Автоматическое тестирование</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":"Java: Автоматическое тестирование"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><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">Stack<Integer> stack = new Stack<>();</code>. Это ещё не дублирование, но уже шаг в эту сторону. Как правило, реальные тесты сложнее и включают в себя большую подготовительную работу.</p>
<p>Допустим, мы разрабатываем собственную коллекцию, такую же как <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html" rel="noopener noreferrer" target="_blank">ArrayList</a> и хотим протестировать методы для обработки коллекций:</p>
<ul>
<li>contains</li>
<li>get</li>
<li>remove</li>
<li>и другие (всего их более 20 штук)</li>
</ul>
<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">import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
public class ArrayListTest {
@Test
public void testGet() {
ArrayList<Integer> list = new ArrayList<>();
list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));
assertEquals(1, list.get(0));
}
@Test
public void testContains() {
ArrayList<Integer> list = new ArrayList<>();
list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));
assertTrue(list.contains(5));
}
}</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">public class ArrayListTest {
ArrayList<Integer> list = new ArrayList<>();
public void init () {
// в реальной практике этот метод может содержать много строк кода
list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));
}
}</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">init()</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">public class ArrayListTest {
@Test
public void testSomeMethod() {
init(); // Запускаем инициализацию
// Сам тест
}
}</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>Тесты в JUnit обладают одной очень важной особенностью. Посмотрите на код ниже и подумайте одинаковые или разные значения выведут в консоль первый и второй тесты? Вот сам код:</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">public class TestClass {
private long startTime = System.currentTimeMillis();
@Test
public void testFirst() {
System.out.println("first = " + startTime);
}
@Test
public void testSecond() {
System.out.println("second = " + startTime);
}
}</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">startTime</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">JUnit</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">@Test</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">startTime</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">testFirst()</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">TestClass</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">testSecond()</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">startTime</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">testSecond()</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">testFirst()</code>. Попробуйте запустить подобный пример на своём компьютере и посмотреть на результаты.</p>
<p>Почему такое происходит? Именно потому, что мы отдаём бразды правления тестами фреймворку. Он будет определять оптимальный порядок и способ запуска тестов. Пользователю при этом необходимо написать тесты, которые не будут зависеть друг от друга, но об этом мы поговорим в следующем уроке.</p>
<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">public class ArrayListTest {
ArrayList<Integer> list = new ArrayList<>();
@BeforeEach
public void beforeEach() {
list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));
}
@Test
public void testGet() {
assertEquals(1, list.get(0));
}
@Test
public void testContains() {
assertTrue(list.contains(5));
}
}</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">@BeforeEach</code> помечаются методы, которые будут выполняться перед стартом <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">@BeforeAll</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">@BeforeAll</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">@BeforeAll
public static void beforeAll() {
// Делаем тут какую-то подготовку
}</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">@AfterEach</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">@AfterAll</code>, которые позволяют выполнить определённые действия после каждого или после всех тестов. Например, вы можете написать метод, который удалит созданный в начале файл.</p>
<p>Почему важно использовать аннотации, а не самостоятельно организовывать порядок и способ выполнения тестов? Самый простой ответ на этот вопрос такой: JUnit должен контролировать происходящие процессы и побочные эффекты в тестах. Все, что вызывается вами вручную, отрабатывается вне JUnit. Это значит, что JUnit никак не может отследить, что происходит, и в какой момент можно запускать тесты.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/java?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Java-разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Java и фреймворк Spring Boot и REST API</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczNSwicHVyIjoiYmxvYl9pZCJ9fQ==--883f3fd4e1b571538035b5680c8d4a9eb504b1f6/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Source%20code-amico.png" alt="Java-разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 6 792 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/java-junit-testing?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">1 месяц</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Junit</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите code coverage, фикстуры, моки, работа с файлами</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyNiwicHVyIjoiYmxvYl9pZCJ9fQ==--a6cffb3f9d8b79cd8231a2320e70f02b1975e66d/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Mobile%20testing-amico.png" alt="Junit" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/qa-auto-engineer-java?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">6 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Автоматизатор тестирования на Java</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Java и фреймворк для UI- и API-автотестов</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMywicHVyIjoiYmxvYl9pZCJ9fQ==--da8237868b3f1c36e3fe891b47b4869fa9f2e8ef/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-pana.png" alt="Автоматизатор тестирования на Java" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 4 281 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/middle-java?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">6 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Middle-java разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Углубите знания Java, Spring и архитектуры серверных приложений</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxOSwicHVyIjoiYmxvYl9pZCJ9fQ==--e79a18f1e9f90eae650f1f4f04115cba51f4e650/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-amico.png" alt="Middle-java разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 4 050 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/java-testing/lessons/setup/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 / 12</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/java-testing/lessons/setup/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-D8AK0-_C.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-DOv3_-Z_.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>