тестирование
#программирование,  #разоблачения

Все-таки вам нужны юнит-тесты

В прошлом посте я говорил, что вам не нужны юнит-тесты. Но то были #вредныесоветы, а сейчас настало время разоблачений. Сегодня мы поговорим о всей важности тестирования и о том, чем грозит его отсутствие (либо неправильное применение). Я постараюсь максимально подробно описать плюсы тестирования и разобрать вредные советы из прошлого поста.

В качестве вступления

Каждое утверждение относится к конкретной области знаний. Например, я никогда не занимался строительством и не знаю, как нужно (и нужно ли) тестировать надежность конструкции. Я не занимался разработкой аппаратного обеспечения и не знаю, каким образом в этой области применяются модульные тесты, и применяются ли они в принципе. Я не занимался низкоуровневой разработкой, не писал на ассемблере и не знаю, как модульно тестировать микроконтроллеры.

Но я занимаюсь enterprise разработкой. Я знаю, как писать бэкенд для крупных сервисов и, что самое главное – я знаю, как применять модульное и интеграционное тестирование в больших проектах на языках высокого уровня.

Спойлер
А может и не знаю 🙂 В любом случае, мне есть, чем поделиться.

Я занимаюсь проектами, в которых минимум пользовательского интерфейса, а многие (микро)сервисы и в принципе его не имеют. Проще говоря, существует единая точка входа в процесс, но до визуального отображения результатов работы будет вызвано множество функций в разных частях системы. Все, что они делают – обрабатывают, подготавливают, сохраняют и пересылают данные из одних мест в другие. И я хочу рассказать, насколько важно модульное и интеграционное тестирование в таких системах.

Простейший пример приложения

Чтобы лучше понять, как это – писать систему без интерфейса, давайте рассмотрим простейший пример. Допустим, у вас есть telegram-бот, который складывает числа. Он просто суммирует их и не делает ничего больше.

Вы ему присылаете несколько чисел, разделенных пробелом, например: «2 2 5». Он эти числа парсит, складывает между собой и возвращает вам результат: 9.

Выглядит очень просто, но это – приложение без какого-либо интерфейса. Да, здесь интерфейсом выступает сам telegram, но точкой входа в вашу систему будет являться вебхук-контроллер, который принимает события от telegram. Иными словами, вы сами никакого пользовательского интерфейса не предоставляете.

На бумаге это будет выглядеть как-то так:

  1. Пользователь отправляет вам сообщение, которое уходит на сервер telegram. 
  2. Telegram оповещает ваше приложение о новом сообщении с помощью вебхуков.
  3. Ваше приложение выполняет необходимые действия и отправляет на сервер telegram ответ.
  4. Telegram оповещает пользователя о новом сообщении.

Диаграмма довольно условная, и telegram может работать по-другому, но в качестве примера сгодится вполне.

Ручного тестирования недостаточно

Вы можете запустить ваше приложение локально, настроить вебхуки и начать тестировать, используя клиент telegram. Иными словами, заняться ручным тестированием. И вы не будете испытывать каких-либо неудобств.

А теперь представьте, что система разрастается. По какой-то неведомой нам причине миллионы пользователей заинтересовались вашим приложением и бесконечно шлют свои сообщения. Вы решаете разделить ваше приложение на consumer, processor и producer:

  1. Consumer принимает вебхуки и складывает сообщения в очередь входящих сообщений.
  2. Processor забирает сообщения из очереди входящих сообщений, складывает числа и кладет результат в другую очередь.
  3. Producer берет сообщения из очереди результатов и отправляет их пользователям.

В таком контексте вам нужно будет поднять три локальных сервиса, две очереди сообщений и настроить связь между ними. Вы станете это делать лишь для того, чтобы проверить, что «2+2» вернет 4, а «3+привет» вернет «Извините, но вы должны ввести числа» (а не «3привет», как сами знаете где)?

Личное мнение – мануальное тестирование умрет
Лично я считаю, что понятие «ручное тестирование» очень сильно изменится, а мануальные тестировщики попросту перестанут существовать. Тестирование веб-приложений отлично автоматизируется тем же Selenium (либо чем-то еще), тестирование десктопных и мобильных приложений тоже будет автоматизироваться. Нет, ручное тестирование само по себе никуда не денется, просто тестировщики будут проводить целый комплекс работ – от создания и поддержки автотестов до эпизодического тестирования руками там, где это проще. Чисто ручное тестирование без использования скриптов там, где это возможно – это рудимент.
Здесь начинается тестирование

Да, разумеется, когда сервис отправится в тестирование (в QA отдел), самим тестировщикам не придется множество раз и подолгу его настраивать. По-хорошему, за них это должны делать скрипты, те самые пайплайны, над которыми мы смеялись в прошлый раз.

Да даже если тестировщику и придется все это поднимать, то это же часть его работы, верно? Ну поднимет и начнет тестировать, вроде бы никаких проблем.

За исключением одной: хороший разработчик отдаст в тестирование только тот код, который он уже сам проверил, чтобы исключить множество циклов доработки по возврату.

И, чтобы разработчику не пришлось каждый раз поднимать весь контекст у себя на машине, и были придуманы юнит- и интеграционные тесты. Вы просто берете и тестируете отдельно модуль сложения чисел, модуль получения входных данных из очереди входящих сообщений и модуль отправки результатов в выходную очередь. Когда вы делаете какое-то изменение, вы запускаете эти тесты и смотрите, что у вас все в порядке, и затем уже отдаете результат в отдел QA для проведения более объемлющего тестирования.

Далее под словом «тестирование» я буду подразумевать не работу отдела QA, а написание разработчиком юнит- и интеграционных тестов на свой код.

Далее, пробежимся по вредным советам из прошлого поста.

Хороший программист уверен в себе

Помните шутку про стадии роста программиста?

  1. Джуниор: я ничего не знаю
  2. Миддл: я знаю все
  3. Сеньор: я ничего не знаю

Чем больше мы знаем, тем отчетливее осознаем, что гораздо больше областей нам неподвластны. К чему это я? Да к тому, что сколько бы хорошим и уверенным в себе ни был программист, он по-прежнему может совершить ошибку. Даже лучшие от этого не застрахованы. И это нормально.

На самом деле, всем плевать, насколько вы опытны и уверены в себе. Важно только то, что результат вашей работы можно эффективно использовать, а о применимости результата вам расскажут ваши тесты.

На мой взгляд, самое главное правило хорошего программиста звучит следующим образом:

Закрывая тикет и отправляя задачу в тестирование, вы гарантируете, что:

  1. Тестирование не выявит багов в вашем коде.
  2. Тестирование не выявит проблемы регрессии, вызванные вашими изменениями.
  3. Закрытый вами тикет никогда не окажется в статусе Reopened.

Прогоняя юнит- и интеграционные тесты, вы становитесь в состоянии дать эту гарантию.

Тесты отнимают время

Да, это правда. Тесты действительно отнимают время, но это – цена за гарантию работоспособности. Все, что вам нужно сделать (оно же и самое сложное) – найти грамотный баланс где-то между «писать тесты на все подряд» и «не писать тесты вообще». Вы можете потратить больше времени на написание тестов и быть уверенным, что ничего не упадет. Также вы можете написать меньше тестов и, соответственно, потратить меньше времени, но ошибки регрессии будут более вероятны.

Требуют ли тесты поддержки?

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

Лично я ничего более глупого не слышал. Тесты не требуют абсолютно никакой поддержки – вы их написали и радуетесь. Если во время рефакторинга или добавления новой функциональности вы случайно что-то сломаете, тесты вам об этом расскажут (при условии, что вы нашли тот самый баланс). Как правило, бизнес-требования по уже написанной функциональности не меняются каждую неделю, поэтому и тесты нет нужды обновлять.

Да, если вам все-таки приходится обновить логику приложения в каком-то месте, то придется обновить и соответствующие тесты. Но это один случай на сто, в остальных 99 случаях тесты просто делают свою работу и не требуют внимания.

Код и тесты – единое целое

Выше я уже сформулировал главное, на мой взгляд, правило хорошего разработчика. 

Вспомнить правило

Закрывая тикет и отправляя задачу в тестирование, вы гарантируете, что:

  1. Тестирование не выявит багов в вашем коде.
  2. Тестирование не выявит проблемы регрессии, вызванные вашими изменениями.
  3. Закрытый вами тикет никогда не окажется в статусе Reopened.

Исходя из этого правила можно сделать вывод, что код и тесты на этот код – единое целое. Не стоит рассматривать тесты как какую-то необязательную надстройку над единственно важной вещью – кодом.

Как разработчик, вы должны предоставить результат, который затем может быть эффективно использован заказчиком. Тесты помогут вам такой результат гарантировать с высокой вероятностью.

Конечно, есть и QA-отдел. Но следует помнить, что только хреновый программист отдает в QA сырой код, который он даже не удосужился проверить и запустить. Цель хорошего программиста – отдать в QA только тот код, в котором он уверен.

Отдавая сырой код в QA и приступая к следующей задаче, вы вовсе не экономите время и не становитесь более эффективным. Наоборот, вы рискуете потратить гораздо больше времени, чем это необходимо, если тестировщики вернут вам задачу на доработку. Тогда вам придется снова погружаться в контекст, искать причину ошибки и отправлять задачу на вторую итерацию. А это, как я уже говорил выше, недопустимо для хорошего разработчика.

Проще просто не допускать, чтобы задача возвращалась вам на доработку, верно? Вы потратите больше времени сегодня, но вам не придется копаться в логах завтра. К тому же, вы застрахуетесь от ошибок регресси в будущем.

Время – цена тестирования. Но помните, что время может быть как потраченным, так и сэкономленным.

Тесты на библиотеки

Стоит ли тестировать тривиальные функции и методы библиотек, которые вы используете?

Допустим, есть такая функция:

public MyObject getInstance()
    return new MyObject();
}

Лично я не стал бы покрывать ее тестами. Тут нет бизнес-логики, и сама функция слишком тривиальна. Суть тестирования не в том, чтобы покрыть 100% строк и веток, а в нахождении нужного баланса – что стоит тестировать, а что нет.

Библиотеки, которые вы используете в своих проектах, уже оттестированы их разработчиком и, на первый взгляд, не требуют дополнительного тестирования с вашей стороны. Но всегда может настать момент, когда вы обновляете версию библиотеки, а там старый метод начинает вести себя по-другому. С этой позиции неплохо бы иметь чисто поверхностные тесты на те функции библиотеки, которые вы используете.

Либо же, как рекомендовал Роберт Мартин, вы можете писать адаптеры к этим библиотекам, и покрывать тестами эти самые адаптеры. Это поможет вам как отловить какие-либо изменения в поведении библиотек, так и заменять либо удалять их в принципе.

Happy Path тестирование

Happy Path, когда вы тестируете только положительные сценарии, может подойти только к поверхностному тестированию библиотек. Когда вы тестируете свой код, вы должны хотеть его сломать.

Представьте, как собирается тестовый автомобиль на конвейере. При happy path после сборки тестировщик просто покрутил бы руль, поддал бы немножко газку и удостоверился бы, что машина тормозит. Но достаточно ли этого? А как же жесткое тестирование подвески, резкий старт и разгон, устойчивость в поворотах и, конечно же, крэш-тест?

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

Проблема вовсе не в тестах

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

Проблема в человеке по ту сторону монитора, безусловно, есть. Люди – вообще источники всех проблем. И, повышая качество исполнителя, можно достичь очень даже впечатляющих результатов.

Но тут снова есть подводный камень. Повышая свой уровень, программист становится в состоянии решать гораздо более сложные задачи, но он по-прежнему не застрахован от ошибок. Ошибки допускают абсолютно все, вне зависимости от опыта и квалификации. Со временем ошибки просто перестают быть тривиальными и становятся более скрытыми и комплексными, но они по-прежнему остаются. И только тестирование своего кода поможет вам их вовремя заметить и исправить.

Простейший пример

Буквально вчера я написал юнит-тест на кусок своего кода в полной уверенности, что увижу зеленую галочку. Однако, тест упал. И не просто упал, а свалился с позорным NullPointerException. Для более-менее опытного программиста словить NPE – это вообще стыд и срам. Если вы получаете NPE, вам следует встать коленками на горох и самолично выпороть себя на глазах у всех коллег.

А вот тот самый кусок кода (упрощенный), на котором упал тест:

public void doSomething(Boolean condition) {
    if (condition) {
        // handle condition
    }
    // handle
}

Можете сразу заметить проблемное место?

Суть в том, что в метод приходит булев флаг, в зависимости от которого нужно выполнить какое-то дополнительное действие. Флаг этот не является обязательным, то есть его может не быть вовсе, и в таком случае мы должны предполагать, что он эквивалентен значению false. И тест как раз проверял вариант, когда этот флаг не передается:

doSomething(null);

Разумеется, NPE упал на этой строке:

if (condition)

Когда я поправил код, все заработало:

if (Boolean.TRUE.equals(condition))

Глаза замыливаются, и вы можете начать работать с big-B Boolean как с обычным boolean. Но у тестов есть еще одно крутое преимущество – у них ничего не замыливается, и они не ошибаются.

Вывод: тесты – ваши друзья.

Главное правило хорошего программиста

Давайте еще раз вспомним главное правило хорошего программиста.

Закрывая тикет и отправляя задачу в тестирование, вы гарантируете, что:

  1. Тестирование не выявит багов в вашем коде.
  2. Тестирование не выявит проблемы регрессии, вызванные вашими изменениями.
  3. Закрытый вами тикет никогда не окажется в статусе Reopened.

А соблюдать это правило вам поможет одна простая аксиома:

Тесты в разумном количестве с высокой вероятности дадут гарантию того, что задача не будет возвращена на доработку.

Заключение

Тесты – не панацея. Сами по себе они не несут никакого смысла. И только грамотное сочетания программного кода и разумного количества тестов поможет достичь нужного результата. В каждой ситуации вам стоит решать, где и как вы будете (или не будете) писать тесты на свой код, чтобы достичь цели.

В общем и целом, правило простое – чем больше тестов, тем надежнее. Однако, крайности – это всегда плохо, так что вам всего лишь стоит найти баланс 🙂

Понравилось? Подписывайтесь на меня в соцсетях!

 
Twitter
VK

guest
38 Комментариев
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Алексей
Алексей
4 лет назад

Как-то начав работать на большом продукте пришел к такому же выводу, QA покрывают мало случаев и при хорошем покрытии автотестами можно обойтись небольшой командой проверяющей сложные случаи. Разработчику нужно уметь и писать код и автотесты — без них редко принимают таску, делать все быстро и качественно. Но перейдя в другую область программирования увидел очень забавную вещь. Можно быть не сеньор/лид, а простым мануал QA и получать те же деньги в Самаре — до 600-1000$ (как 2-3 летний разработчик особо не смотрящий по сторонам). Особенно если сидеть долго на одном месте и можно не париться над изучением языков программирования, автоматизации тестов и прочего. Команды по 30-40 человек на одном проекте. Пришел, внимательно прошел 3-5 E2E и пошел домой. Даже английский Elementary пойдет. В таких проектах мануал QA еще долго будут счастливо жить, сидеть на митингах и лутать зп.

trackback
1 месяц назад

vardenafil ssri

vardenafil ssri

trackback
1 месяц назад

sildenafil 100 mg para que sirve

sildenafil 100 mg para que sirve

trackback

do you need a prescription for sildenafil

do you need a prescription for sildenafil

trackback
1 месяц назад

how much does sildenafil cost

how much does sildenafil cost

trackback
1 месяц назад

levitra dosage 40 mg

levitra dosage 40 mg

trackback
1 месяц назад

chloramphenicol pharmacy

chloramphenicol pharmacy

trackback

can you take sildenafil and tadalafil together

can you take sildenafil and tadalafil together

trackback
1 месяц назад

enalapril online pharmacy

enalapril online pharmacy

trackback
1 месяц назад

mexican pharmacy viagra online

mexican pharmacy viagra online

trackback
1 месяц назад

vipps certified pharmacy viagra

vipps certified pharmacy viagra

trackback
1 месяц назад

diovan pharmacy coupons

diovan pharmacy coupons

trackback
1 месяц назад

what happens if a woman takes tadalafil

what happens if a woman takes tadalafil

trackback
1 месяц назад

cheap vardenafil tablets

cheap vardenafil tablets

trackback
1 месяц назад

codeine phosphate online pharmacy

codeine phosphate online pharmacy

trackback
1 месяц назад

can you take tadalafil daily

can you take tadalafil daily

trackback
1 месяц назад

does tadalafil increase testosterone

does tadalafil increase testosterone

trackback

acetaminophen vs. ibuprofen for fever in adults

acetaminophen vs. ibuprofen for fever in adults

trackback
1 месяц назад

sulfasalazine discussion board

sulfasalazine discussion board

trackback
1 месяц назад

gabapentin controlled substance

gabapentin controlled substance

trackback
1 месяц назад

gabapentin gabitril

gabapentin gabitril

trackback
1 месяц назад

etodolac vs. etoricoxib

etodolac vs. etoricoxib

trackback
1 месяц назад

celecoxib solubility pka

celecoxib solubility pka

trackback

can you buy cheap pyridostigmine without a prescription

can you buy cheap pyridostigmine without a prescription

trackback
1 месяц назад

amitriptyline and weight gain

amitriptyline and weight gain

trackback
1 месяц назад

mebeverine brand names

mebeverine brand names

trackback
1 месяц назад

sumatriptan vs rizatriptan

sumatriptan vs rizatriptan

trackback
1 месяц назад

imuran a opalanie

imuran a opalanie

trackback
1 месяц назад

mobic and endep

mobic and endep

trackback
1 месяц назад

stelax 10 baclofen tablets

stelax 10 baclofen tablets

trackback
1 месяц назад

nitrate-free period imdur

nitrate-free period imdur

trackback

can i purchase toradol without a prescription

can i purchase toradol without a prescription

trackback
1 месяц назад

can i purchase cheap ketorolac prices

can i purchase cheap ketorolac prices

GenIPTV Provider
13 дней назад

The Best Premium IPTV Service WorldWide!

Muriel Henderson
6 дней назад

I genuinely enjoy examining on this website , it holds excellent articles . «The great secret of power is never to will to do more than you can accomplish.» by Henrik Ibsen.

우리카지노도메인
3 дней назад

Thank you for the sensible critique. Me & my neighbor were just preparing to do some research on this. We got a grab a book from our area library but I think I learned more from this post. I’m very glad to see such excellent information being shared freely out there.

우리카지노
2 дней назад

Dead indited articles, regards for entropy. «The earth was made round so we would not see too far down the road.» by Karen Blixen.

Social media & sharing icons powered by UltimatelySocial
38
0
Нравится? Оставьте комментарий!x
()
x