Що таке одиничне тестування? [зачинено]


211

Я бачив багато запитань із запитанням "як" для тестування певної мови, але жодного питання не було "що", "чому" та "коли".

  • Що це?
  • Що це робить для мене?
  • Чому я повинен його використовувати?
  • Коли я повинен його використовувати (також коли ні)?
  • Назвіть деякі загальні підводні камені та помилки

Убер, доступно багато ресурсів, і стаття у Вікіпедії , безумовно, чудове місце для початку. Отримавши основну ідею, можливо, деякі конкретніші запитання допоможуть вам вирішити, чи потрібно використовувати тестування.
блідий кінь

Чисті переговори код: youtube.com/watch?v=wEhu57pih5w
Кіран

1
Це не одне конкретне питання. Це 5 широких питань.
Raedwald

9
Добре, що ви задали це запитання, читаючи відповідь працюючого програміста набагато краще і до речі, порівняно з читанням онлайн-ресурсів.
Пратюш Данука

Відповіді:


197

Тестування блоку - це, грубо кажучи, тестування бітів вашого коду в ізоляції з тестовим кодом. Безпосередніми перевагами, які спадають на думку, є:

  • Запуск тестів стає автоматизованим та повторюваним
  • Ви можете протестувати на набагато більш деталізованому рівні, ніж тестування точкою та клацанням через GUI

Зауважте, що якщо ваш тестовий код записує у файл, відкриває підключення до бази даних або робить щось по мережі, це більш доцільно класифікувати як тест на інтеграцію. Інтеграційні тести - це добре, але їх не слід плутати з одиничними тестами. Тестовий код блоку повинен бути коротким, солодким та швидким у виконанні.

Ще один спосіб подивитися на тестування одиниць - це те, що ви спочатку пишете тести. Це відоме як Тестово-керована розробка (коротше TDD). TDD приносить додаткові переваги:

  • Ви не пишете умоглядного коду "Мені це може знадобитися в майбутньому" - достатньо, щоб тести пройшли
  • Код, який ви написали, завжди охоплюється тестами
  • Спочатку написавши тест, ви змушені думати про те, як ви хочете викликати код, що зазвичай покращує дизайн коду в довгостроковій перспективі.

Якщо ви зараз не робите тестування, рекомендую розпочати його. Отримайте хорошу книгу, практично будь-яка книга xUnit зробить це, оскільки поняття між ними дуже зручні.

Іноді написання одиничних тестів може бути болісним. Коли це станеться так, спробуйте знайти когось, який допоможе вам, і протистоять спокусі "просто написати проклятий код". Тестування приладів дуже схоже на миття посуду. Це не завжди приємно, але він зберігає вашу метафоричну кухню в чистоті, і ви дуже хочете, щоб вона була чистою. :)


Редагувати: Одне помилкове уявлення приходить на думку, хоча я не впевнений, чи це так часто. Я чув, як керівник проекту говорив, що тестові підрозділи змусили команду написати весь код двічі. Якщо це виглядає і відчувається таким чином, ну, ти робиш це неправильно. Записування тестів не тільки прискорює розробку, але й дає вам зручний показник "зараз я закінчую", що у вас не було б інакше.


2
Чи хотіли б ви сказати, як TDD прискорює розвиток?
Мартін

70

Я не погоджуюся з Деном (хоча кращим вибором може бути просто не відповісти) ... але ...

Тестування блоку - це процес написання коду для перевірки поведінки та функціональності вашої системи.

Очевидно, тести покращують якість вашого коду, але це лише поверхня переваги тестування одиниць. Реальними вигодами є:

  1. Спростіть змінити технічну реалізацію, переконавшись, що ви не змінюєте поведінку (рефакторинг). Правильно перевірений пристрій код може бути агресивно відремонтований / очищений з невеликими шансами зламати щось, не помічаючи цього.
  2. Надайте розробникам впевненості при додаванні поведінки чи виправленнях.
  3. Документуйте свій код
  4. Вкажіть області вашого коду, які щільно з'єднані. Важко одиничний код тесту, який щільно поєднаний
  5. Надайте засоби для використання свого API та шукайте труднощі на початку
  6. Позначає методи та класи, які не дуже згуртовані

Ви повинні пройти тестування, оскільки це у ваших інтересах доставити рентабельний та якісний продукт вашому клієнту.

Я б запропонував використовувати його для будь-якої системи або частини системи, яка моделює поведінку в реальному світі. Іншими словами, він особливо добре підходить для розвитку підприємства. Я б не використовував його для викидання / корисних програм. Я б не використовував його для частин системи, які проблематично перевірити (користувальницький інтерфейс - поширений приклад, але це не завжди так)

Найбільша помилка полягає в тому, що розробники випробовують занадто великий блок, або вони вважають метод одиницею. Це особливо вірно, якщо ви не розумієте Інверсію контролю - у цьому випадку ваші тестові пристрої завжди перетворяться на тестування інтеграції. Блок тесту повинен перевірити індивідуальну поведінку - і більшість методів мають багато поведінки.

Найбільша помилкова думка полягає в тому, що програмісти не повинні проводити тести. В це вірять лише погані або ледачі програмісти. Чи повинен хлопець, що будує ваш дах, не перевіряв його? Чи повинен лікар, який замінює серцевий клапан, не перевіряв новий клапан? Тільки програміст може перевірити, що його код виконує те, що він мав намір робити (QA може перевірити кращі випадки - як поводиться код, коли йому сказано робити те, що програміст не мав наміру, і клієнт може зробити тест прийняття - чи робить код що клієнт заплатив за це)


44

Основна відмінність одиничного тестування, на відміну від "просто відкриття нового проекту та перевірки цього конкретного коду", полягає в тому, що він автоматизований , таким чином повторюваний .

Якщо ви перевірите код вручну, це може переконати вас, що код працює ідеально - у його поточному стані . Але як бути через тиждень, коли ви зробили невелику модифікацію в ньому? Чи готові ви повторно перевірити його вручну, коли що- небудь зміниться у вашому коді? Найімовірніше, ні :-(

Але якщо ви можете запустити тести в будь-який час, за допомогою одного кліка, точно таким же чином, в протягом декількох секунд , то вони будуть негайно показати вам щоразу , коли що - щось зламалося. І якщо ви також інтегруєте одиничні тести у свій автоматизований процес збирання, вони попередить вас про помилки навіть у тих випадках, коли, здавалося б, цілком непов’язана зміна зламала щось у віддаленій частині кодової бази - коли вам навіть не спало на думку, що існує необхідність повторної перевірки саме цієї функціональності.

Це головна перевага одиничних тестів перед ручним тестуванням. Але зачекайте, є ще:

  • тестові одиниці різко скорочують цикл зворотного зв’язку щодо розробки : з окремим відділом тестування вам може знадобитися тиждень, коли ви дізнаєтесь, що в вашому коді є помилка, до цього часу ви вже забули велику частину контексту, таким чином, це може зайняти години знайти та виправити помилку; OTOH з одиничними тестами, цикл зворотного зв'язку вимірюється в секундах, і процес виправлення помилок, як правило, уздовж рядків "о ш * т, я забув перевірити, чи є ця умова тут" :-)
  • одиничні тести ефективно документують (ваше розуміння) поведінку вашого коду
  • Тестування одиниць змушує вас переоцінити вибір дизайну, що призводить до більш простого і більш чіткого дизайну

Рамки тестування блоків, у свою чергу, полегшують вам написання та запуск тестів.


+1 Крім того, моя улюблена частина про тестовий код (особливо, коли йому надано нову кодову базу): Це демонструє очікуване використання тестового коду.
Стівен Еверс

34

Мене ніколи не навчали одиничному тестуванню в університеті, і мені знадобилося певний час, щоб "отримати" його. Я прочитав про це, поїхав "ах, правильно, автоматичне тестування, що може бути крутим, я здогадуюсь", а потім забув про це.

Минуло зовсім трохи більше часу, перш ніж я дійсно з'ясував суть: Скажімо, ви працюєте у великій системі, і ви пишете невеликий модуль. Вона компілюється, ви ставите її кроками, це чудово працює, ви переходите до наступного завдання. Через дев'ять місяців після лінійки і дві версії пізніше хтось інший вносить зміни в якусь, здавалося б, незв'язану частину програми, і вона порушує модуль. Гірше, вони перевіряють свої зміни, і код працює, але вони не перевіряють ваш модуль; пекло, вони можуть навіть не знати, що ваш модуль існує .

А тепер у вас є проблема: зламаний код знаходиться в багажнику і ніхто навіть не знає. Найкращий випадок - внутрішній тестер знаходить його перед тим, як відправити, але виправити код, який запізнюється в грі, коштує дорого. І якщо жоден внутрішній тестер не знайде ... ну, це може дійсно дуже дорого вийти.

Рішення - одиничні тести. Вони вирішать проблеми, коли ви пишете код - що добре - але ви могли це зробити вручну. Справжня виплата полягає в тому, що вони вирішать проблеми дев'ять місяців вниз, коли ви зараз працюєте над зовсім іншим проектом, але літній стажер вважає, що це буде виглядати акуратніше, якщо ці параметри були в алфавітному порядку - і тоді тест одиниці Ви писали, що зворотний шлях не вдається, і хтось кидає речі у стажера, поки він не змінить параметр назад. Ось "чому" одиничні тести. :-)


13

Опинившись на філософських плюсах тестування одиниць та TDD, ось декілька з них є ключовими спостереженнями «лампочки», які вразили мене на моїх попередніх перших кроках на шляху до просвітлення TDD (жодної оригінальної чи необов’язково новини) ...

  1. TDD НЕ означає вдвічі більше кількості коду. Тестовий код, як правило, досить швидко і безболісно писати і є ключовою частиною вашого дизайнерського процесу і критично.

  2. TDD допомагає зрозуміти, коли припинити кодування! Ваші тести дають вам впевненість, що ви зробили достатньо на даний момент і можете перестати налаштовуватися і переходити до наступного.

  3. Тести та код працюють разом, щоб досягти кращого коду. Ваш код може бути поганим / невдалим. ТЕСТ може бути поганим / баггі. У TDD ви бавитесь на шансах БОТУ бути поганим / баггі бути досить низьким. Часто це тест, який потребує виправлення, але це все-таки хороший результат.

  4. TDD допомагає при кодуванні запорів. Ви знаєте таке відчуття, що вам так багато робити, ви ледве знаєте, з чого почати? У п'ятницю вдень, якщо ви просто зволікаєте ще на пару годин ... TDD дозволяє дуже швидко викласти те, що, на вашу думку, потрібно зробити, і ваше кодування швидко рухається. Також, як лабораторні щури, я думаю, що ми всі реагуємо на це велике зелене світло і працюємо більше, щоб побачити це знову!

  5. Аналогічно, ці типи дизайнерів можуть бачити, над чим вони працюють. Вони можуть побродити на перерву з соком / сигаретою / iphone та повернутися до монітора, який негайно дає їм наочний підказ про те, куди вони дісталися. TDD дає нам щось подібне. Легше зрозуміти, куди ми потрапили, коли життя втручається ...

  6. Я думаю, що Фоулер сказав: "Недосконалі тести, запускаються часто, набагато краще, ніж досконалі тести, які ніколи не пишуться". Я трактую це як надання мені дозволу писати тести, де я думаю, що вони будуть найбільш корисні, навіть якщо решта покриття мого коду жахливо неповна.

  7. TDD допомагає у всіх видах дивовижних способів. Хороші одиничні тести можуть допомогти задокументувати, що щось потрібно робити, вони можуть допомогти вам перенести код з одного проекту на інший і подарують вам необгрунтоване відчуття переваги над вашими колегами, які не тестують :)

Ця презентація є чудовим вступом до всіх випробувань на смачність.


7

Я хотів би порекомендувати книгу тестування шаблонів xUnit Джерарда Месзароса. Він великий, але є чудовим ресурсом для тестування блоків. Ось посилання на його веб-сайт, де він обговорює основи одиничного тестування. http://xunitpatterns.com/XUnitBasics.html


5

Я використовую одиничні тести, щоб заощадити час.

Під час побудови бізнес-логіки (або доступу до даних) функціональність тестування часто може включати введення матеріалів на багато екранів, які можуть бути, а може і не закінчені. Автоматизація цих тестів економить час.

Для мене одиничні випробування - це різновид модульованого тестового джгута. Зазвичай існує принаймні один тест на публічну функцію. Я пишу додаткові тести для висвітлення різних форм поведінки.

Усі особливі випадки, про які ви думали при розробці коду, можуть бути записані в коді в одиничних тестах. Одиничні тести також стають джерелом прикладів використання коду.

Мені набагато швидше виявити, що мій новий код щось порушує в моїх тестових одиницях, а потім перевірити код і попросити якийсь розробник передового пошуку знайти проблему.

Для тестування доступу до даних я намагаюся написати тести, які або не мають змін, або очищають після себе.

Блок тестів не зможе вирішити всі вимоги до тестування. Вони зможуть заощадити час розробки та перевірити основні частини програми.


4

Це мій погляд на це. Я б сказав, що тестування приладів - це практика написання тестів на програмне забезпечення, щоб переконатися, що ваше справжнє програмне забезпечення робить те, що йому призначено. Це почалося з jUnit у світі Java і стало найкращою практикою в PHP, а також у SimpleTest та phpUnit . Це основна практика екстремального програмування і допомагає вам бути впевненим, що ваше програмне забезпечення все ще працює за призначенням після редагування. Якщо у вас достатньо тестового покриття, ви можете робити капітальний рефакторинг, виправляти помилки або швидко додавати функції, набагато менший страх ввести інші проблеми.

Це найефективніше, коли всі одиничні тести можна запустити автоматично.

Тестування блоків, як правило, пов'язане з розробкою ОО. Основна ідея - створити скрипт, який створює середовище для вашого коду, а потім виконує його; ви пишете твердження, вказуєте призначений результат, який ви повинні отримати, а потім виконайте свій тестовий скрипт, використовуючи таку рамку, як згадана вище.

Рамка запустить усі тести на ваш код, а потім повідомить про успіх або провал кожного тесту. phpUnit запускається з командного рядка Linux за замовчуванням, хоча для нього доступні інтерфейси HTTP. SimpleTest від природи базується на веб-сторінках і набагато простіше вставати та працювати, IMO. У поєднанні з xDebug, phpUnit може надати автоматизовану статистику для покриття коду, яку деякі люди вважають дуже корисною.

Деякі команди записують гачки з свого сховища підривної системи, щоб одиничні тести запускалися автоматично, коли ви здійснюєте зміни.

Добре зберігати свої тести на одиницю в тому ж сховищі, що і ваша програма.


4

Бібліотеки на зразок NUnit , xUnit або JUnit просто обов'язкові, якщо ви хочете розвивати свої проекти, використовуючи підхід TDD, популяризований Кентом Беком:

Ви можете прочитати « Вступ до тестової керованої розробки» (TDD) або книгу Кента Бека « Тестова розробка: за прикладом» .

Тоді, якщо ви хочете бути впевнені, ваші тести охоплюють "хорошу" частину вашого коду, ви можете використовувати програмне забезпечення типу NCover , JCover , PartCover або будь-яке інше. Вони підкажуть відсоток покриття вашого коду. Залежно від того, наскільки ви вміли в TDD, ви знатимете, чи досить добре ви практикували :)


3

Тестування одиниць - це тестування одиниці коду (наприклад, однієї функції) без необхідності інфраструктури, на яку покладається ця одиниця коду. тобто перевірити його ізольовано.

Якщо, наприклад, функція, яку ви тестуєте, підключається до бази даних і виконує оновлення, в одиничному тесті ви, можливо, не захочете робити це оновлення. Ви б хотіли тест на інтеграцію, але в цьому випадку це не так.

Таким чином, тестовий пристрій здійснював би функціональність, включену у функцію, яку ви протестуєте, без побічних ефектів оновлення бази даних.

Скажімо, ваша функція отримала деякі номери з бази даних, а потім виконала обчислення стандартного відхилення. Що ви тут намагаєтеся випробувати? Що стандартне відхилення обчислено правильно або дані повертаються з бази даних?

У одиничному тесті ви просто хочете перевірити, чи стандартне відхилення обчислено правильно. Під час тесту на інтеграцію ви хочете перевірити розрахунок стандартного відхилення та пошук бази даних.


3

Тестування блоку - це написання коду, який тестує ваш код програми.

Unit частина імені про намір перевірити невеликі блоки коду (один метод, наприклад) за один раз.

xUnit є для того, щоб допомогти у цьому тестуванні - вони є рамками, які допомагають у цьому. Частина цього - автоматизовані тестові бігуни, які повідомляють, який тест не виходить, а який - пройти.

Вони також мають можливість встановити загальний код, який вам потрібен у кожному тесті, перед тим, як руйнувати його, і знищити його після закінчення всіх тестів.

Ви можете провести тест, щоб перевірити, чи очікуваний виняток було викинуто, не потрібно самостійно писати весь блок пробування.


3

Думаю, що ви не розумієте, що рамкові тестування рамок типу NUnit (тощо) допоможуть вам автоматизувати невеликі та середні тести. Зазвичай ви можете запустити тести в графічному інтерфейсі (наприклад, NUnit ), просто натиснувши кнопку, а потім - сподіваємось - побачити, що смужка прогресу залишається зеленою. Якщо воно стає червоним, рамка показує, який тест не вдався і що саме пішло не так. У звичайному тесті одиниці ви часто використовуєте твердження, наприклад Assert.AreEqual(expectedValue, actualValue, "some description")- тому, якщо два значення неоднакові, ви побачите помилку, що говорить "деякий опис: очікувана <очікувана валя>, але була <actualValue>".

Тож як висновок одиничне тестування зробить тестування більш швидким та набагато зручнішим для розробників. Ви можете запустити всі одиничні тести, перш ніж ввести новий код, щоб не порушити процес збирання інших розробників для того ж проекту.



3

Тестування блоків - це практика переконатися, що функція або модуль, який ви збираєтесь реалізувати, буде вести себе так, як очікувалося (вимоги), а також переконатися, як він поводиться у сценаріях, таких як граничні умови та недійсне введення.

xUnit , NUnit , mbUnit тощо - це інструменти, які допоможуть вам у написанні тестів.


2

Розробка тесту, керована тестом, набула свого роду термін Unit Test. Як старий таймер я згадаю більш загальне його визначення.

Тест блоку також означає тестування одного компонента у більшій системі. Цей єдиний компонент може бути dll, exe, бібліотекою класів тощо. Це може бути навіть єдина система в багатосистемному застосуванні. Отже, в кінцевому підсумку модульне тестування закінчується тестуванням того, що ви хочете назвати єдиним фрагментом більшої системи.

Потім ви перейдете до інтегрованого або системного тестування, перевіривши, як всі компоненти працюють разом.


2

Перш за все, якщо говорити про тестування модулів чи будь-які інші види автоматизованого тестування (інтеграція, навантаження, тестування інтерфейсу тощо), ключова відмінність від запропонованого вами полягає в тому, що воно автоматизоване, повторюване і не вимагає ніяких людських ресурсів Для споживання (= ніхто не повинен виконувати тести, вони зазвичай працюють при натисканні кнопки).


1

Я пішов на презентацію про тестування одиниць на FoxForward 2007, і мені сказали, що ніколи не перевіряти те, що працює з даними. Зрештою, якщо ви перевіряєте живі дані, результати непередбачувані, і якщо ви не тестуєте живі дані, ви фактично не тестуєте написаний вами код. На жаль, це велика частина кодування, яке я роблю в ці дні. :-)

Я нещодавно знімав TDD, коли писав розпорядок збереження та відновлення налаштувань. По-перше, я перевірив, що можу створити об’єкт зберігання. Потім, що це був метод, який мені потрібно було викликати. Потім, щоб я міг це назвати. Потім, щоб я міг передати його параметри. Потім, щоб я міг передати йому конкретні параметри. І так далі, поки я остаточно не переконався, що він збереже вказане налаштування, дозвольте мені змінити його, а потім відновити його для кількох різних синтаксисів.

Я так і не дійшов до кінця, тому що мені потрібно було - рутина - тепер - чорт, але це була гарна вправа.


1

Що ви робите, якщо вам купіть лайно і здається, що ви застрягли у вічному стані очищення, що ви знаєте, що за допомогою додавання будь-якої нової функції або коду можна зламати поточний набір, оскільки поточне програмне забезпечення - це як будинок картки?

Як ми можемо робити тестування одиниць?

Ви починаєте з малого. Проект, в який я щойно потрапив, не мав одиничного тестування до декількох місяців тому. Коли покриття було таким низьким, ми просто виберемо файл, який не мав покриття, і натисніть «Додати тести».

Зараз ми налічуємо понад 40%, і нам вдалося зібрати більшість плодів, що звисають.

(Найкраще, що навіть при такому низькому рівні охоплення ми вже стикаємося з багатьма прикладами коду, який робить неправильну справу, і тестування це сприйняло. Це величезний мотиватор, який підштовхує людей до додавання більше тестування.)


1

Це відповідає на те, чому вам слід робити тестування одиниць.


Три відео нижче тестують блок тестування в JavaScript, але загальні принципи застосовуються для більшості мов.

Тестування одиниць: Хвилини зараз врятують години пізніше - Ерік Манн - https://www.youtube.com/watch?v=_UmmaPe8Bzc

Тестування модулів JS (дуже добре) - https://www.youtube.com/watch?v=-IYqgx8JxlU

Написання Testable JavaScript - https://www.youtube.com/watch?v=OzjogCFO4Zo


Тепер я просто вивчаю цю тему, тому я не можу бути на 100% правильним, і тут є більше, ніж те, що я тут описую, але моє основне розуміння одиничного тестування полягає в тому, що ви пишете якийсь тестовий код (який тримається окремо від вашого основний код), який викликає функцію у вашому головному коді з введенням (аргументами), необхідного для функції, а потім код перевіряє, чи повертає він дійсне значення повернення. Якщо вона поверне дійсне значення, рамка тестування одиниць, яку ви використовуєте для запуску тестів, показує зелене світло (все добре), якщо значення недійсне, ви отримуєте червоне світло, а потім зможете вирішити проблему безпосередньо перед вами випустити новий код у виробництво, без тестування ви, можливо, насправді не виявили помилку.

Отже, ви пишете тести для свого поточного коду і створюєте код, щоб він пройшов тест. Через кілька місяців вам або комусь іншому потрібно змінити функцію у вашому головному коді, тому що раніше ви вже писали тестовий код для цієї функції, ви тепер запускаєтеся знову, і тест може бути невдалим, оскільки кодер ввів логічну помилку у функцію або повертає щось повністю відрізняється від того, що повинна повернути ця функція. Знову ж без тестування цю помилку може бути важко відстежити, оскільки це може вплинути і на інший код, і він залишиться непоміченим.


Також той факт, що у вас є комп'ютерна програма, яка працює через ваш код і тестує його замість того, щоб ви вручну це робили на сторінці браузера за сторінкою, економить час (тестування одиниць на JavaScript). Скажімо, ви змінюєте функцію, яка використовується деяким сценарієм на веб-сторінці, і вона працює добре і добре для своєї нової мети. Але, скажімо також, для аргументів, що у вашому коді є ще одна функція, яка залежить від того, щоб нова модифікована функція працювала належним чином. Ця залежна функція може перестати працювати через зміни, які ви внесли до першої функції, однак без тестів, які автоматично виконуються на вашому комп'ютері, ви не помітите, що є проблема з цією функцією, поки вона фактично не буде виконана і ти'

Знову повторюю, що тести, які виконуються під час розробки вашої програми, вирішать подібні проблеми під час кодування. Не маючи тестів, вам доведеться вручну пройти всю програму, і навіть тоді помилку можна буде важко помітити, наївно ви надсилаєте її у виробництво і через деякий час добрий користувач надсилає вам звіт про помилку (який буде не так добре, як ваші повідомлення про помилки в тестовій рамці).


Це досить заплутано, коли ти вперше почуєш тему і думаєш собі, чи я вже не тестую свій код? І написаний вами код працює так, як це, як і передбачається, "навіщо мені потрібен інший фреймворк?" ... Так, ви вже тестуєте свій код, але комп'ютер краще це робити. Вам просто потрібно написати один раз досить хороші тести для функції / одиниці коду, а про інше подбає вас потужний процесор, замість того, щоб вам вручну перевірити, чи працює весь ваш код, коли ви вносите зміни в свій код.

Крім того, вам не доведеться перевіряти свій код, якщо ви цього не хочете, але він окупається, коли ваша база проекту / коду починає зростати, оскільки шанси на введення помилок зростають.


0

Тестування приладів та TDD взагалі дозволяють мати коротші цикли зворотного зв’язку щодо програмного забезпечення, яке ви пишете. Замість того щоб мати великий тестовий етап у самому кінці впровадження, ви поступово перевіряєте все, що пишете. Це дуже підвищує якість коду, як ви відразу бачите, де у вас можуть бути помилки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.