Deutsch
Germany.ruФорумы → Архив Досок→ Программирование

unit tests

1806  1 2 3 4 все
  moose старожил04.11.18 16:37
04.11.18 16:37 

я относительный новичок в теме, т.к. классических юниттестов написал несколько штук, и большинство из них - "hello, unittest!" из различных учебников.

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

для некоторых особенно простых просто смысла не вижу, т.к. они написаны, чтобы сэкономить место, и их правильность очевидна (а если бы нет - всплыло бы сразу). например, я не хочу иметь по всему коду длиннющие строчки получения форматированной даты последней субботы, это вынесено в ф-ю, и я ее просто вызываю. зачем сюда юниттэст?

другой тип ф-й - web-scrapping. мы идем на какой-то сайт определенный, и выискиваем там определенные вещи по определенным критериям. каждый раз там будет в общем случае находиться что-то другое. или разработчики сайта могут что-то изменить, и я ничего не найду, и это бы неплохо проверить, но это будет обнаружено в самой ф-ии по ходу дела. а юниттэст, по определению, должен проверить, правильно ли работает моя ф-я, т.е. я ей должен подсунуть "правильный сайт", и "неправильный сайт", и говорить окей, если на правильном найдено то, что ожидалось, а на неправильном - не найдено, или найдено не то, что ожидалось. но что даст такой тест, если хозяин сайта что-то изменит? тест и дальше будет проходить, но ф-я будет возвращать некорректный результат. в общем, что-то у меня работа над юниттестами превращается в кошмар, если сравнить ее с собственно "нормальной разработкой". растолкуйте, где я глючу, что почитать.

#1 
AlexNek патриот04.11.18 17:57
AlexNek
NEW 04.11.18 17:57 
в ответ moose 04.11.18 16:37

Я тоже не профи в юнит тестах, так как этим нужно заниматься постоянно.

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


А так глянь тута, тоже самое можно сделать и для внешнего сайта. Ключевое слов Mocking

https://www.danylkoweb.com/Blog/the-fastest-way-to-mock-a-...


https://www.c-sharpcorner.com/UploadFile/dacca2/fundamenta...

#2 
dymanoid знакомое лицо04.11.18 18:13
dymanoid
NEW 04.11.18 18:13 
в ответ moose 04.11.18 16:37

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

Посему имеет смысл писать тесты на всё, что тестируется. Даже на "а + b", потому что в будущем кто-то может поменять тип аргументов метода, например, и a+b неожиданно станет возвращать мусор.

#3 
  moose старожил04.11.18 20:08
NEW 04.11.18 20:08 
в ответ AlexNek 04.11.18 17:57

я знаю и про "не совсем верно", и про "мокинг", но все эти волшебные средства (любяе!)

- стоят дополнительных затрат

- работают чудесно на уровне "хэлло, уорлд!", но отказывают, когда задача усложняется

#4 
  moose старожил04.11.18 20:25
NEW 04.11.18 20:25 
в ответ dymanoid 04.11.18 18:13, Последний раз изменено 04.11.18 20:32 (moose)

т.е. это на случай, если мы начнем что-то менять без оглядки на уже отлаженное?

а я читаю другое. например, такой вот "рекламный набор":

https://dzone.com/articles/top-8-benefits-of-unit-testing

, который, похоже, сто раз цитируется."


в качестве хэллоуорлд для юнит тест любят приводить тест чего-то "калькулятор", и тестируют его "эдд". и юниттэст выглядит очень красиво: проверь, что вернет на 2+2, если четыре - ок, нет - бей тревогу!

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

- читает содержимое сайта (что тут проверять?)

- ищет там определенные тэги (списки тэгов), предполагая, что там находится то, что мы ищем (снова что проверять?)

- если находит - возвращает что нашла в удобном формате

- если не находит (это может выясниться на разных этапах: что-то с интернетом, что-то с сайтом, ...?) - возвращает нечто, что дает понять, что не нашли.


можно, конечно, "промОчить" сайт, заставив читать не сайт, а какой-то мок, причем их должно быть несколько, и переделать всю кухню: пусть ф-я не делает все это, а разделим ее пополам: одна часть читает сайт. для нее юниттест - просто вернула она нечто длиннющее или нет. вторая часть получает это прочитанное (или мок) на входе, и должна из этого что-то извлечь. и тут мы можем понаделать моков на любой случай: прочитанное содержит нечто, из чего извлекается "ура!", прочитанное содержит то же "ура!", но в другом (третьем) допустимом месте, сайт изменил структуру, и "ура!" уже находится в другом месте, мы его не находим...

можно, конечно, это проделать. но это:

- потребует дополнительных затрат

- если структура сайта изменится, тесты будут проходить, но результаты мы получать не будем (хотя они будут присутствовать на сайте, просто искать их нужно теперь иначе). т.е ваш случай "Даже на "а + b", потому что в будущем кто-то может поменять тип аргументов метода, например, и a+b неожиданно станет возвращать мусор." будет иметь место быть, но наш юниттест не поможет нам это поймать: тесты, повторяю, будут и дальше благополучно проходить на красиво подобранных моках, а мы будем получать или мусор, или ничего. и самое плохое - может случиться, что выводы станем делать, основываясь на наших юниттестах. берусь утверждать, что такие вещи можно протестировать только интекрационными тестами, а выдумывать для подобных вещей юниттесты - дань моде, которая только мешает жить.


человек под фонарем что-то ищет. второй подходит:

- что-нибудь потеряли?

- да, вот ключи выронил...

- здесь?

- да нет, вон там, в кустах.

- а почему здесь ищете?

- здесь светлее...


#5 
AlexNek патриот04.11.18 20:53
AlexNek
NEW 04.11.18 20:53 
в ответ moose 04.11.18 20:08

Подожди какая цель? Поболтать о UT или найти решение. Если первое то я пас пока, время перемены работы

#6 
AlexNek патриот04.11.18 20:56
AlexNek
NEW 04.11.18 20:56 
в ответ moose 04.11.18 20:25
- читает содержимое сайта (что тут проверять?)
- ищет там определенные тэги (списки тэгов), предполагая, что там находится то, что мы ищем (снова что проверять?)
- если находит - возвращает что нашла в удобном формате
- если не находит (это может выясниться на разных этапах: что-то с интернетом, что-то с сайтом, ...?) - возвращает нечто, что дает понять, что не нашли.

уже как минимум 3 различных задачи (Читаем,Ищем, анализируем результат). А говоришь простая. Задача должна быть одна и только одна


#7 
dymanoid знакомое лицо04.11.18 21:22
dymanoid
NEW 04.11.18 21:22 
в ответ moose 04.11.18 20:25

Мне пофигу, что пишут в рекламных наборах. Я говорю, опираясь на многолетний опыт и текущую сферу занятости (пилим софт для разработки и тестов софта для авто- и авиапрома).

Если твоя функция (что за "ф-ии" - руки отсохнут слово полностью написать, и это при таких-то романах в постах?) делает половину логики программы, то она нетестируемая. Я написал - тестить всё, что тестируется. Твой софт не тестируется, судя по всему. Поэтому делать для него юнит-тесты бысмысленно, так начальству и скажи. Времени и денег будет потрачено много, а результатов, кроме галочки "у нас юзают юнит-тесты" никаких. Смысл?

#8 
Simple Nothing is f*cked04.11.18 21:33
Simple
NEW 04.11.18 21:33 
в ответ dymanoid 04.11.18 21:22

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

#9 
  beatus Teddybär05.11.18 11:43
beatus
NEW 05.11.18 11:43 
в ответ moose 04.11.18 20:25, Последний раз изменено 05.11.18 12:08 (beatus)
Тесты имеет смысл писать если несколько разрабов кодят одну и ту же фичу или связанные фичи или проект бурно развивается (regressionstest). Даже в таком маленьком проекте, как мой редактор сообщений для германки ( < 2000 code lines) имеет смысл писать регрессион-тесты. Например, после изменения class на id для input type "file" загрузка изображений перестала работать, что я обнаружил чисто случайно. А если бы был написан тест, то ошибка обнаружилась бы ещё перед публикацией модификаций.
она просто
- читает содержимое сайта (что тут проверять?)
Доступен сайт или нет (http-response code)
- ищет там определенные тэги (списки тэгов), предполагая, что там находится то, что мы ищем (снова что проверять?)
Здесь как раз нужен мок, симулирующий тэги. Или просто мок-параметр, соответствующий тэгу (одному или всем это вам решать). Проверяется возвращаемое значение, см. следующий пункт
- если находит - возвращает что нашла в удобном формате
Проверка полей/формата. Естественно, исходные значения должны браться из мок'a.
- если не находит (это может выясниться на разных этапах: что-то с интернетом, что-то с сайтом, ...?) - возвращает нечто, что дает понять, что не нашли.
Проверка корректного возврата ошибки
она просто
Как уже здесь вам сказали, полезно все вышеописанные операции выделить в отдельные функции.
если структура сайта изменится, тесты будут проходить, но результаты мы получать не будем
Будет выдаваться ошибка (а лучше 2): тэги не найдены и / или некорректный формат данных. В этом случае необходимо будет изменить вашу программу и моки.
#10 
Simple Nothing is f*cked05.11.18 16:17
Simple
NEW 05.11.18 16:17 
в ответ moose 04.11.18 16:37

Почитайте "Working effectively with legacy code", многое станет понятным.

Если у вас legacy code, возможно, имеет смысл начать с интеграционных тестов. Для веб-проектов, например, можно попробовать связку BDD + Selenium.

#11 
dymanoid знакомое лицо05.11.18 21:11
dymanoid
NEW 05.11.18 21:11 
в ответ Simple 04.11.18 21:33

Я в веб-специфике не силён, может, там и можно "х*як-х*як, и в продакшн". А в серьёзном энтерпрайзе или там где безопасность/надёжность важна, никто не даст рефакторить огромную кодовую базу "чтобы юнит-тесты ввести". Это ж надо целый отдел посадить и параллельно с "нормальной" разработкой вести этот самый рефакторинг, плюс проблемы с интеграцией и т.п. На моей памяти ни одна контора не обладала такими железными яйцами и свободными средствами, чтобы себе это позволить.

Именно поэтому во всех "рекламных материалах" и говорят - пишите юнит-тесты с самого начала. Потом пришпандурить не получится. Ну, только если там "хелло ворлд" на 10 тыщ строк.

#12 
MrSanders старожил05.11.18 21:32
NEW 05.11.18 21:32 
в ответ dymanoid 05.11.18 21:11
Я в веб-специфике не силён, может, там и можно "х*як-х*як, и в продакшн".

Там это не можно. Там это нужно. И юнит тесты - ругательство :)

А в серьёзном энтерпрайзе или там где безопасность/надёжность важна, никто не даст рефакторить огромную кодовую базу "чтобы юнит-тесты ввести".

Ну... Мы справились. Просто аргументы другие. Рефакторинг не для того, чтобы юнит-тесты ввести, а для повышения удобства сопровождения/модификации и уменьшения времени поиска ошибок. А юнит-тесты помогают находить критические места: где тест писать сложно, там и код сложный и непонятный - надо рефакторить. Мы перевели (почти) весь код с использования статических переменных на DI, синглтоны все стали наконец-то немодифицируемыми и стало их где-то в 10 раз меньше.

Стоило нефигово. Точно не скажу но порядка 20,000-50.000 часов.

#13 
dymanoid знакомое лицо05.11.18 22:16
dymanoid
NEW 05.11.18 22:16 
в ответ MrSanders 05.11.18 21:32

Памятники таким ставить надо. Не каждая контора пойдёт на это.

#14 
  beatus Teddybär05.11.18 23:40
beatus
NEW 05.11.18 23:40 
в ответ dymanoid 05.11.18 22:16, Последний раз изменено 05.11.18 23:42 (beatus)
Памятники? 😲 Какой-то перебор. Тривиальная задача по рефакторингу кода и написание тестов за разумно отведённый срок была выполнена... задача для уровня верк-штудентов, может быть юниор-разработчиков. Менеджеру проекта уважение за выделенный бюджет и понимание важности чистоты кода, это да.
#15 
Программист коренной житель06.11.18 07:26
NEW 06.11.18 07:26 
в ответ moose 04.11.18 16:37

Опять про юнит-тесты? :)


например, я не хочу иметь по всему коду длиннющие строчки получения форматированной даты последней субботы, это вынесено в ф-ю, и я ее просто вызываю. зачем сюда юниттэст?

Вопрос в том, что ты хочешь протестировать.

Если ты хочешь протестировать правильность результата этой функции, то надо делать тест результата.

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


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


Я использую разбиение каждого теста на 3 части (подход Роя Ошеровава мне показался очень удобным и наглядным):

void TestName ()
{
  // Arrange
  ....
  // Act
  ....
  // Assert
  ....
}


Таким образом всегда видно, что именно тестируется и как работает unit under test.



но что даст такой тест, если хозяин сайта что-то изменит? тест и дальше будет проходить, но ф-я будет возвращать некорректный результат.

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

#16 
MrSanders старожил06.11.18 10:57
NEW 06.11.18 10:57 
в ответ Программист 06.11.18 07:26
Более того, юнит-тесты и не должны взаимодействовать ни с какими сторонними ресурсами.

+1

Как только для теста нам нужна работающая БД, веб-сервис или какой-то другой сервер, это уже не юнит тест. А как минимум - "интеграционный".


Если есть опасения что структура/содержимое чужого сайта может измениться - надо делать метод, который парсит ответ, отказоустойчивым, выдавать, например, последнее считанное значение (главное чтобы с ошибкой не вываливался) и делать программку/скрипт (назовите это "health check") - раз в день/час проверять можем ли мы пропарсить ответ сайта и если нет, то громко кричим (как можем, е-мейл, смс, в твиттер разработчикам :)).

#17 
MrSanders старожил06.11.18 11:07
NEW 06.11.18 11:07 
в ответ dymanoid 05.11.18 22:16
Памятники таким ставить надо. Не каждая контора пойдёт на это.

Тут памятник надо ставить убедившему руководство архитектору. Потому что инвестиции уже (почти или совсем) окупились. Одних только критических апдейтов теперь выкатываем в 2 раза меньше. На правку ошибок тоже на 40% меньше времени тратится (и количество уменьшилось, и на каждую в среднем). Правда теперь менеджеры воют что реализация нового функционала занимает больше времени, потому как тесты писать и править ранее написанные приходится. Вот только брешут-с :)

#18 
  beatus Teddybär06.11.18 12:28
beatus
NEW 06.11.18 12:28 
в ответ MrSanders 06.11.18 10:57, Последний раз изменено 06.11.18 12:49 (beatus)
раз в день/час проверять можем ли мы пропарсить ответ сайта
Для задачи ТС ваш "health check" избыточен: его программа и так шерстит сайт(ы) круглосуточно. Достаточно обычного протоколирования ошибок парсинга.
надо делать метод, который парсит ответ, отказоустойчивым, выдавать, например, последнее считанное значение (главное чтобы с ошибкой не вываливался)
Если будет выдавать информативную/ые ошибку/и, то никакого "health check" и не потребуется.
#19 
MrSanders старожил06.11.18 14:00
NEW 06.11.18 14:00 
в ответ beatus 06.11.18 12:28, Последний раз изменено 06.11.18 14:03 (MrSanders)
его программа и так шерстит сайт(ы) круглосуточно

А где это было написано, а то я пропустил?

#20 
1 2 3 4 все