TDD лише теоретично


29

Трохи більше року тому мені пощастило взяти 9-місячну перерву в роботі. Я вирішив, що в цей час я відточу свої навички C #. Я почав працювати над купою проектів і змусив наслідувати TDD.

Це був досить просвітницький процес.

Спочатку було важко, але з часом я навчився писати більш тестовий код (який, як виявляється, має тенденцію бути більше ТВОРНОГО коду), і в ході цього процесу я також посилив свої навички дизайну OO.

Зараз я знову в складі робочої сили і помічаю щось дивне.

Я вважаю за краще не слідкувати за TDD.

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

Натомість я застосував трохи (масово) інший підхід:

  1. Виберіть вертикальний шматочок роботи
  2. Розробити функціонуючий прототип
  3. Рефактор, поки все добре і охайно
  4. Нехай оцінять прекрасний ТВЕРДИЙ і тестуваний код, який я написав.

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

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

Я також хотів би дати зрозуміти, що я також не передую всім автоматизованим тестуванням. Тут мій процес відрізняється. І саме тому я задаю це питання.

TDD в теорії. Не на практиці.

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

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

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

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

Отож, я збираюся в пекло [BT] DD, чи це звичайна форма прагматичного кодування та тестування?

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

Примітка:

Я є єдиним розробником своїх проектів і відповідаю за все: збір вимог, дизайн, архітектура, тестування, розгортання тощо. Я підозрюю, що саме тому мій процес працює.


2
Виглядає як шип і стабілізується, завжди роблячи стабілізацію, якщо If that slice doesn't need modification. lizkeogh.com/2012/06/24/beyond-test-driven-development
RubberChickenLeader

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

5
Так, і написання тестів спочатку суттєво подвоює вашу роботу по прототипуванню.
Роберт Харві

3
це означає, що я брехав про те, що це "трохи".
MetaFight

1
"І коли я займаюся написанням тестів, я пишу їх набагато менше, але покриваю майже стільки ж ґрунту (вища рентабельність інвестицій)" Коли ви кажете, що пишете набагато менше їх, ви маєте на увазі, оскільки ви лише тестуєте код, який ви " Ви змінюєтесь, або ви хочете сказати, що ви якось покривають той самий (перевірений) фрагмент коду меншою кількістю тестів, ніж якщо б ви використовували TDD?
Бен Ааронсон

Відповіді:


6

Щоб процес працював у довгостроковій перспективі, я писав би тести, коли пишеться код.

Що може здатися суперечним вашому підходу. Однак ви поставили це запитання, тож я вам піду:

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

Залишаючи це пізніше, знаки (природно) зменшаться з часом.

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

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

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


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

Проблема в тому, що завтра ніколи не настане, завжди є наступна особливість. І що ти вирішив зробити; написати наступну функцію або написати тести на те, що ви тільки що «закінчили»?
Енді

9

Хоча TDD важко реалізувати на 100%, є недолік у вашому підході

  1. Виконайте робочий вертикальний зріз роботи

    1.1 Проходить 1 рік ....

    1.2 Нова робота починає роботу над проектом

  2. Якщо цей фрагмент потребує модифікації

    2.3 Розбираємо назви методів "Очищення кодування" імена та параметри стилю "GetUnicorn (colourOfUnicorn)"

    2.4 Прочитайте коментарі xml "Отримує золотий єдиноріг (для їзди) (obvs)"

    2.5 Вишук оригінального розробника

    2.6 Сподіваємось, вони пам’ятають, що повинен робити код

    2.7 Запропонуйте їм все пояснити

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

  4. Змініть код

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


2
Гей, я пишу код самодокументування! Мої заняття несуть одну відповідальність, і тому їх легко зрозуміти. Ніхто не потребуватиме полювання на мене :)
MetaFight

7
@MetaFight, і якщо вони будуть, ви легко помітите зверху твердого золотого єдинорога!
jonrsharpe

3
Я його називаю Золотиком.
MetaFight

Щодо більш серйозної ноти, так, у вас є пункт. Я розглядав журнал "тестування заборгованості", щоб я міг погасити гроші, коли моє навантаження легше.
MetaFight

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

4

Я погоджуюся і з Даніелем Холлінраком, і з Еваном, що першим ключовим моментом, чому ваш тест-лише-якщо-модифікація працює добре, є:

I am the sole developer on my projects and I am responsible for everything

і що ймовірним другим ключовим моментом є:

you're producing nice clean code

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

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

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


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

3

Для мене ключовим моментом є таке:

Я є єдиним розробником своїх проектів і відповідаю за все: збір вимог, дизайн, архітектура, тестування, розгортання тощо. Я підозрюю, що саме тому мій процес працює.

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

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

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


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

Це справедливо, але, на жаль, за моїм досвідом, майже кожен проект, над яким я працював над документацією, коли він існує, є або застарілим, або неповним.
Даніель Холлінрак

1
Те саме тут, саме тому розробники повинні усвідомити важливість документування речей і не писати більше коду у вигляді тестів! Можливо, нам знадобляться інструменти, щоб забезпечити кращу підготовку документації (тобто це не просто гарне форматування підписів методів) з коментарів до коду та квитків на вимоги.
gbjbaanb

Я трохи відредагував свою відповідь у відповідь на ваші коментарі. Дякую.
Даніель Холлінрак

1
@gbjbaanb Якщо я можу допомогти, мені подобається уникати вимог щодо написання документів, архітектурних діаграм та дизайну документів. Це тому, що вони, як правило, дуже швидко залягають. У моєму випадку мені дуже пощастило, оскільки я керую багатьма невеликими програмами з дуже маленькими обов'язками. Це робить вимоги та архітектурну документацію трохи надмірними. А проектів досить мало, щоб загальний дизайн був зрозумілим. Що я маю в документуванні, однак, як системи взаємодіють між собою , як розгорнути їх, і як стежити за своїм здоров'ям.
MetaFight

3

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

Проблеми, які я можу побачити з практикою написання тестів безпосередньо перед тим, як змінити:

Мені часто потрібно вносити зміни поспіхом

Хоча ви можете заощадити час загалом, лише написавши тести, коли вони вам потрібні, але не весь час є рівним. Витратити 2 години на написання тестів, щоб заощадити 1 годину, коли я перебуваю в режимі кризи - абсолютно того варто.

Простіше писати тести одночасно, коли я пишу код

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


Визначення Майкла Пір'я у спадщині - код без тестів. Незалежно від того, чи згодні ви з його визначенням, зрозуміло, що значна частина витрат на зміну існуючого коду гарантує, що він все ще працює так, як очікувалося, часто навіть не зрозуміло, що таке очікувана поведінка.

Запис тестів компенсує вартість, яка кодує розуміння того, що таке правильна поведінка, а також надає простий спосіб "майбутнім нам" перевірити, що поведінка все ще правильна.


2

Це хороше питання, і FWIW я кину два мої центи.

Близько року тому я кодував у Salesforce, платформі, яка мала вбудований механізм, який змушував вас не обов'язково писати тести перед тим, як кодувати , а навпаки, змушував вас писати тести взагалі.

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

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

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

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

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

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

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


2
Я відчуваю, що ми знаходимося на одному поколінні мови, щоб отримати TDD правильно. Я думаю, що зараз TDD начебто "прикручений" до більшості мов із рамками xUnit. У якийсь момент це буде просто вбудовано в те, як робиться кодування - не окремо. Як би ви визначили клас, і відразу будуть створені заглушки для всіх тестувань, а також деякий підмножина самих тестів (ті, які можна було б легко визначити самими класами / методами).
Calphool

3
@Calphool Ми були на самому справі інтегровані деякі легко перевіряються речі на мову! Ми називаємо це статичним набором тексту . Іржа переносить це додатково, перевіряючи позики, щоб перевірити ще більше помилок. Але більшість тестів притаманні саме цьому класу ("якщо я натискаю кнопку, віджет стане червоним") - як би компілятор / IDE, можливо, знав, що ви збираєтеся це перевірити?
користувач253751

1
@immibis: Можливо, продовживши перевірку типу далі. Можливо, поняття "віджет стає червоним" стає концепцією першого класу, яку можна певним чином вивести з коду. Я не претендую на відповіді, я просто вважаю, що TDD все ще досить нова, що не була повністю інтегрована в еволюцію мови.
Calphool

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

1

Я не міг порекомендувати ваш підхід.

Якщо я використовую ваш підхід, це буде, наприклад, наступним чином (будинок - це додаток):

  1. Я починаю будувати будинок для своєї сім’ї, як каменяр з певними знаннями або як початківець.
  2. Я знаю такі вимоги, як дитячі кімнати, гостьова кімната і починаю будувати свій "прототипний" будинок.
  3. Через пару раз ваш будинок-прототип зроблений.
  4. Я починаю дивитись, чи структура є досить стійкою вручну. Тому я набираю багато ваг і заношу його в різні кімнати на першому поверсі. Щоб переконатися, що коли я сиджу в кімнаті зі своєю родиною, стеля не зламається. Але це ламається, і я починаю рефакторинг. Спочатку очистіть всю масу. Тоді будуйте його новим і тестуйте його вручну, поки він не буде достатньо стабільним.
  5. Чим я переїжджаю з родиною. Все добре.
  6. Через місяць мої двоюрідні брати та батьки приходять до нас у гості. Але перш, ніж вони зможуть увійти до нашого будинку, їм потрібно заплатити архітектору та інженеру-будівельнику, щоб переконатися, що стеля не зламається, коли ми сидимо в одній із кімнат на першому поверсі.
  7. У архітектора та інженера-будівельника багато роботи, тому що їм нема з чого почати. Тому їм потрібно зайти в мій будинок і подивитися, як я його будую.
  8. І знову це недостатньо стабільно. Тож їм доведеться реконструювати землю першого поверху.
  9. Але після цього все нормально, і всі сміливо можуть увійти в мій будинок.

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

Тож існує кращий підхід без програмування "прототипу" і ніж почати рефакторинг. Замість програмування прототипу "зробіть Design з UML вашої програми наступним чином.

  1. Створіть діаграму UseCase. Ви можете використовувати draw.io для початку.
  2. Тоді створіть діаграму EPK на основі ваших UseCases для визначення поведінки. (ПОПЕРЕДЖЕННЯ вашої заявки) Швидше до рефактора, ніж рефактор кодованого прототипу. Особливо, коли ти новачок.
  3. Створіть діаграму класу. (СТРУКТУРА вашої заявки)
  4. Визначте, де у вас можуть виникнути проблеми при здійсненні поведінки.
  5. Напишіть для цього простий прототип з можливо 10 або 20 рядками коду, щоб визначити, як можна реалізувати цю поведінку. Добре для початківців. Або подивіться підручник, подивіться вихідний код інших прикладних програм там. Як вони це вирішили.
  6. Чим почати кодування. Кулак успішних тестів вашого UseCase. Це можна зробити різними способами. Спершу створіть всю Структуру, яка потрібна для тесту та цю UseCase. При використанні Enterprise Architekt структура може бути створена для вас. На основі ваших діаграм. Або створити структуру під час підключення тесту. Тому помилок компіляції не з’являється. Тут слід згадати, що ТИЛЬКО потрібно тестувати ПОВЕДІНКУ своєї заявки. У вас є UseCases.
  7. Тоді реалізуйте поведінку свого UseCase.
  8. Після успіху UseCases починають писати Тести на винятки. І це завжди добре, коли ви бачите зелені кольори, коли ваші тести дійсні;)
  9. І ви закінчили.

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

Тому я просто проголосую за підходи, тому що вони дуже трудомісткі для початківців і не є гарними. Щоб мати чіткі межі між вашою структурою та поведінкою, ви можете використовувати Дизайн, керований доменом, і під "Впорядкувати дуже Домен" з двома пакетами (одним пакетом, названим структурою, а іншим іменем поведінки). Також для ваших тестів. простий приклад перевірити цей приклад, написаний на Java.


1

Код, який я пишу, працює. Це працює, тому що я тестую його вручну.

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

Автоматизовані тести (неважливо тест-перший чи ні) змушують вас швидко працювати - забезпечуючи швидший цикл зворотного зв’язку щодо вашого коду.

Ви впевнені, що через півроку ви пам’ятаєте тестувати певний стан вручну - будь ласка, не кажіть, що ви задокументуєте всі важливі умови для тестування - адже написання документації / коментаря дорівнює тесту написання (виконувана документація)

  • Виберіть вертикальний шматочок роботи

  • Розробити функціонуючий прототип

  • Рефактор, поки все добре і охайно

І знову: під час рефакторингу ви вручну перевіряли всю логіку, на яку впливає рефакторинг? Скільки часу потрібно на тест на зміну рефакторингу? Якщо рефакторинг порушує якийсь код, скільки часу ви знайдете причину перерв?

  • Нехай оцінять прекрасний ТВЕРДИЙ і тестуваний код, який я написав.

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

Ви знайшли свій шлях дуже продуктивним лише тому, що ви працюєте лише з кодом, і, я думаю, тому, що ви тільки починаєте працювати над цим проектом (Скільки років над цим проектом ви працюєте? 6 - 8 місяців?).
Ви все ще пам’ятаєте все, що написали, і можете розпізнати причину можливих проблем. Я впевнений, що ви почнете писати тести з початку через 3 - 3 роки вашого проекту - адже ви хочете бути впевнені, що нічого не забудете.


0

Якщо ви ніколи не помиляєтесь, вам не дуже потрібні тести. Більшість розробників роблять помилки, але якщо ви ніколи не робите, і ви впевнені, що ніколи не помилитесь у майбутньому (а ви єдиний у проекті), насправді немає причин витрачати час на написання тестів.

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

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


Звичайно, хороша архітектура, принципи SOLID тощо повинні запобігати цьому - ні. Складні системи мають деталі, які впливають на інші частини, ось так воно і є. наприклад, зміна граматики Antlr в Rubberduck може легко змусити ідеально модифіковану частину працювати ідеально, порушуючи 45 інших функцій. Без ретельних тестів, це неможливо знати, і вам доведеться бути божевільним, щоб кожен раз вручну перевіряти всі випадки. Якщо я щось зміню в роздільній здатності і перерва 987 тестів, я знаю, що я щось зробив не так і на що це вплинуло.
Матьє Гіндон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.