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 
Simple Nothing is f*cked06.11.18 16:36
Simple
NEW 06.11.18 16:36 
в ответ Программист 06.11.18 07:26
Роя Ошеровава

Кстати, эту книгу автору тоже неплохо было бы прочитать :)

#21 
Simple Nothing is f*cked06.11.18 16:38
Simple
NEW 06.11.18 16:38 
в ответ dymanoid 05.11.18 21:11
рефакторить огромную кодовую базу "чтобы юнит-тесты ввести".

Речь идет о самых примитивных рефакторингах типа Extract method. У Фезерса подробнее.

#22 
dymanoid знакомое лицо06.11.18 20:57
dymanoid
NEW 06.11.18 20:57 
в ответ Simple 06.11.18 16:38
Речь идет о самых примитивных рефакторингах типа Extract method.

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

#23 
  moose старожил06.11.18 21:54
NEW 06.11.18 21:54 
в ответ Simple 06.11.18 16:36

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


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


def get_suggestions (name):
    try:
        retval = []
        page = requests.get(u'https://blablabla?query={0}'.format(name))
        tree = html.fromstring(page.content)
        group = tree.xpath("//div[@class='some-group']")
        for item in group:
            name_nodes = item.xpath('.//div[starts-with(@class, "some-node")]')
            if name_nodes and len(name_nodes) == 3:
                retval.append(name_nodes[0].text)
                retval.append(name_nodes[2].text)
    except Exception, e:
        print e
    return retval
вроде ясно, что код делает, и вызывающий проверит, вернули ли хоть какие-нибудь имена. если нет - будет отправлено сообщение об этом. но у нас пока примитивная задача: написать юнит-тест для этой ф-ии (читать: функции). главный вопрос остается: что мы хотим протестировать? полагаю, что цель - проверить, выполняет ли ф-я (читать: функция) то, чего мы от нее ожидаем.
#24 
dymanoid знакомое лицо06.11.18 22:49
dymanoid
NEW 06.11.18 22:49 
в ответ moose 06.11.18 21:54, Последний раз изменено 06.11.18 22:49 (dymanoid)

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

Её надо раздраконить на 2-3 отдельные, руководствуясь S и I из SOLID. Применяя D из SOLID. Не забывая про DRY при этом.

Тогда всё будет замечательно тестироваться.

#25 
Simple Nothing is f*cked06.11.18 23:06
Simple
NEW 06.11.18 23:06 
в ответ dymanoid 06.11.18 20:57

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

Синглтоны мокаются без проблем.

#26 
Simple Nothing is f*cked06.11.18 23:09
Simple
NEW 06.11.18 23:09 
в ответ moose 06.11.18 21:54

Выделить page.content в качестве параметра, а дальше уже все просто.

#27 
dymanoid знакомое лицо06.11.18 23:21
dymanoid
NEW 06.11.18 23:21 
в ответ Simple 06.11.18 23:06

А ну-ка примерчик про синглтоны. На C# и без хот-патчинга и прочих танцев с бубном IL-кодом, плиз.

Типа:

bool MyMethod(string key)
{
    var data = MyService.Instance.GetData(key);
    return MyService.Instance.CheckData(data);    
}

class MyService
{
    public static MyService Instance { get; }
    private MyService()
    {
        MyRepository.Instance.Init();
        MyXrenZnaetChto.Instance.Init();
        ZachemStolkoSingletonov.Instance.Init();
    }
}

#28 
Simple Nothing is f*cked06.11.18 23:23
Simple
NEW 06.11.18 23:23 
в ответ dymanoid 06.11.18 23:21

Не, я по жабе. Но на сишарпе тоже должно быть что-то похожее.

#29 
dymanoid знакомое лицо06.11.18 23:25
dymanoid
NEW 06.11.18 23:25 
в ответ Simple 06.11.18 23:23

Нету. Только специальной переделкой (доработкой напильником) синглтонов.

#30 
  moose старожил06.11.18 23:30
NEW 06.11.18 23:30 
в ответ dymanoid 06.11.18 22:49

т.е. вы предлагаете, чтобы каждая ф-нкция содержала не более одного оператора? я могу это догнать только если ф-я становится слишком необъятной, и поэтому труднопонимаемой, или часть кода можно выделить в отдельную ф-ю, которая может использоваться другими. но вы бы лучше пояснили вашу идею , переиначив этот примитивный примерчик, "раздраконив его на 2-3 отдельніх, руководствуясь этими странными словами (S,I, ...)

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

#31 
  beatus Teddybär06.11.18 23:33
beatus
NEW 06.11.18 23:33 
в ответ dymanoid 06.11.18 22:49, Последний раз изменено 06.11.18 23:37 (beatus)
Нормальная функция. Из объектов увидел только X-Path и page. Всё оставить как есть, только добавить if для логов (см. моё предыдущее сообщение). Тесты писать не для функции, а для операций внутри функции.
P.S. программирую на Java, JavaScript и PHP
#32 
  moose старожил06.11.18 23:38
NEW 06.11.18 23:38 
в ответ Simple 06.11.18 23:09, Последний раз изменено 06.11.18 23:51 (moose)

зачем? если дело в том, чтобы проверить, умеет ли ф-я разыскивать в прочитанном с сайта полезное, то можно запэтчить requests mock'ом, который будет возвращать "правильный вариант", а потом - "неправильный". и мы увидим, работает ли наша примитивная ф-я как мы задумали. я вот почему-то вижу, что она может перестать работать только в единственном реальном случае: формат сайта изменится. и мне важно об этом как можно раньше узнать. а опасность, что кто-то пришлет "неправильный" параметр name - второстепенна. я просто не вижу НИКАКОЙ необходимости здесь что-то рефакторить, создавать юнит тэст вообще, кроме как "соблюдение ритуала". разубедите, если вы иначе считаете.


припомнилась одна история, когда потребовалось по протоколу передавать даты следующих событий. события могли проходить только по определенным дням в определенное время. и вот задается количество событий, скажем, 5, и в параметрах стоит, что события происходят по понедельниками, средам и субботам в 17:00. событие входит в рассмотрение, если до его наступления - не менее 15 минут. вот здесь я понаюниттэстовался вволю. наверное, какой-нибудь простенький тулчик помог бы, но тогда все было сделано "на колене": задаешь дату-время и количество событий, смотришь, возвращается ли тот список, что ожидаешь. кстати, не так сложно было этот наколенковый юниттест провести, как придумать сами тесткэйсиз.

#33 
dymanoid знакомое лицо06.11.18 23:41
dymanoid
NEW 06.11.18 23:41 
в ответ moose 06.11.18 23:30

Нужно почитать про SOLID - это альфа и омега современного ООП. Это не какие-то там вещи типа TDD, про которые все спорят (хорошо это или нет), а общепризнанные принципы разработки, которые значительно увеличивают качество кода и тем самым значительно уменьшают стоимость его поддержки.

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

#34 
  moose старожил06.11.18 23:53
NEW 06.11.18 23:53 
в ответ dymanoid 06.11.18 23:41

т.е. приведенный пример слишком сложен, чтобы пороиллюстрировать вашу идею? может, найдете таки пять минут? : )

#35 
Программист коренной житель07.11.18 07:02
NEW 07.11.18 07:02 
в ответ moose 06.11.18 21:54
главный вопрос остается: что мы хотим протестировать? полагаю, что цель - проверить, выполняет ли ф-я (читать: функция) то, чего мы от нее ожидаем.

:) Это так мой шеф говорит "мне надо, чтобы все работало" :)

А чего ты ожидаешь от этой функции? :D


Предположу, что requests - это какой-то объект, который возвращает какой-то другой объект.

Для того, чтобы сделать юнит-тест для этой функции нужна самая малость - подставить фейковый requests, который будет возвращать заранее известный объект page.

Далее делаешь 3 теста:

1) на странице нет div'ов class='some-group' -> результат пустое множество

2) на странице 1 div c class='some-group', но внутри группы нет some-node -> результат пустое множество

3) на странице 1 div c class='some-group', внутри группы 2 some-node, имя одной some-node'ы длиной 2 символа, а другой - 3 -> результат получаешь массив из одного элемента с заранее известным именем.


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


#36 
MrSanders старожил07.11.18 07:21
NEW 07.11.18 07:21 
в ответ Simple 06.11.18 23:23
Не, я по жабе. Но на сишарпе тоже должно быть что-то похожее.

Ну, в яве private static final тоже только через рефлекцию менять можно. Чем мы и занимались лет 5 для тестов, пока весь код в порядок приводили :)

Но. Самое страшное не в этом. Самое страшное это отловить все ссылки на такой синглтон, который нам для теста надо моком подменить. Потому что как рассуждает программизд? "Это же синглтон, не меняется, чем три раза A.getInstance() вызывать, я вызову его один раз и сохраню в своем статическом поле". И всё, мы приплыли. Хотя, надо признать они такое же и с "вставленными" DI полями делать норовят. Пока ручки не отобьешь.

#37 
Программист коренной житель07.11.18 07:23
NEW 07.11.18 07:23 
в ответ dymanoid 06.11.18 23:21

Во-первых, начнем с того, что синглтоны - это зло :) И нужно иметь очень важную причину, чтобы использовать синглтон.

Во-вторых, предусмотреть бэк дор для тестов - как нефиг делать (не знаю, что ты понимаешь под хот-патчингом, но никаких танцев с бубном :D) (кстати, твой вариант синглтона нерабочий, т.к. Instance всегда null) Вот пример синглтона с бэк дором:

class MyService
{
    public static MyService Instance { get; internal set; }
    private MyService()
    {
        MyRepository.Instance.Init();
        MyXrenZnaetChto.Instance.Init();
        ZachemStolkoSingletonov.Instance.Init();
    }
}

Далее остается добавить только [assembly: InternalsVisibleTo("MyTests")] и все.

#38 
MrSanders старожил07.11.18 07:42
NEW 07.11.18 07:42 
в ответ moose 06.11.18 23:53
т.е. приведенный пример слишком сложен, чтобы пороиллюстрировать вашу идею? может, найдете таки пять минут? : )

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

А как вы будете проверять, например, что метод вернёт ошибку, если в ответе ваших div-ов не будет? Ведь URL сервера у вас жестко внутри метода вбит.

#39 
MrSanders старожил07.11.18 07:44
NEW 07.11.18 07:44 
в ответ Программист 07.11.18 07:23

ну всё же изменение кода только для нужд тестов не есть хорошо. Но иногда приходится.

#40 
Simple Nothing is f*cked07.11.18 07:57
Simple
NEW 07.11.18 07:57 
в ответ dymanoid 06.11.18 23:25
По-моему у Ошерова что-то такое упоминалось. Но тут лучше Программиста спросить.
#41 
Simple Nothing is f*cked07.11.18 07:59
Simple
NEW 07.11.18 07:59 
в ответ moose 06.11.18 23:30
Почитайте про SOLID. Можно сразу Clean Code Мартина.
#42 
Simple Nothing is f*cked07.11.18 08:01
Simple
NEW 07.11.18 08:01 
в ответ beatus 06.11.18 23:33
Нормальная, но в чистом виде нетестируемая из-за зависимости от request.
#43 
Simple Nothing is f*cked07.11.18 08:31
Simple
NEW 07.11.18 08:31 
в ответ MrSanders 07.11.18 07:44
В жабе этого не нужно.
#44 
MrSanders старожил07.11.18 11:44
NEW 07.11.18 11:44 
в ответ Simple 07.11.18 08:31

Технически - нет, не нужно. До всего можно добраться. В крайнем случае модифицируя байт-код агентом (см. PowerMock).

А вот практически приходится. У нас были тесты, проверявшие права доступа. И чтобы переинициализировать (очистить после теста) классы, которые тестами затрагивались, приходилось что-то около 10-15 статических полей то в null то в 0 то в false сбрасывать. И каждый раз, когда классы менялись (то просто поле переименуют, то его в другой класс перенесут, то вообще удалят), сыпались тесты => выкатывать изменения нельзя.

Договорились что в двух классах будут protected методы junitReset(), а наши тесты будут их вызывать. После этого, при таких же изменениях как и раньше, сразу вылетает ошибка компиляции в junitReset(), его правят и тесты работают как и раньше.

Теперь от этих полей избавились, осталось 2, но метод убирать не хотят. Говорят нам проще с ним, чем время от времени в ошибках тестов разбираться.

#45 
Simple Nothing is f*cked07.11.18 12:03
Simple
NEW 07.11.18 12:03 
в ответ MrSanders 07.11.18 11:44

Мне больше понравился jmockit.

У тебя опыта побольше. В моих случаях статических переменных не было вроде.

#46 
MrSanders старожил07.11.18 14:23
NEW 07.11.18 14:23 
в ответ Simple 07.11.18 12:03

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

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

#47 
Simple Nothing is f*cked07.11.18 14:25
Simple
NEW 07.11.18 14:25 
в ответ MrSanders 07.11.18 14:23

а мне наоборот больше понравился, чем powermock :)

Конечно, возможность факапа есть, но тут уж никуда не денешься. Начать как-то надо.

#48 
dymanoid знакомое лицо07.11.18 20:34
dymanoid
NEW 07.11.18 20:34 
в ответ Программист 07.11.18 07:23, Последний раз изменено 07.11.18 20:55 (dymanoid)

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

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

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

Замена модификатора сеттера на internal никаким образом не поможет синглтон сделать mockable.


ЗЫ. Хот-патчинг - это замена кода приложения в процессе выполнения приложения. Есть на всех платформах, в том числе .NET. Например, можно любые методы менять на что угодно и вообще творить всякое непотребство. К сожалению, приходилось применять: ручная генерация IL и потом хардкорный asm, чтобы заставить jitter всё это дело схавать и не подавиться.

#49 
dymanoid знакомое лицо07.11.18 20:53
dymanoid
NEW 07.11.18 20:53 
в ответ moose 06.11.18 23:53

Идея в том, что по S (single responsibility) каждый класс, метод, функция должны выполнять только одну задачу. Это позволяет минимизировать изменения в программе, если изменились требования к этой задаче. Любое изменение - это потенциально источник ошибок. Меньше изменений - меньше ошибок.

В примере функция и лезет в веб, и получает контент, и парсит XML. По SOLID, функция, которая парсит XML, должна на вход его получать. Откуда он пришёл, ей должно быть по барабану. Другая функция лезет в веб и качает данные. Точка. Ей по барабану, кто эти данные использовать будет и как. И так далее.

Разбивая логику на связанные, но независимые блоки, автоматом получаем тестируемые блоки, поскольку их зависимости минимальны и есть чёткое понимание, чего давать на вход и чего ждать на выходе.

#50 
  moose старожил07.11.18 21:15
NEW 07.11.18 21:15 
в ответ dymanoid 07.11.18 20:53, Последний раз изменено 07.11.18 21:35 (moose)

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

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

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

вообще, здравому смыслу есть место где-нибудь во всех этих S, unit test, TDD и прочих религиях?


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

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

#51 
dymanoid знакомое лицо07.11.18 21:34
dymanoid
NEW 07.11.18 21:34 
в ответ moose 07.11.18 21:15

Короче, ты или тролль, или реально не хочешь понимать. Литературы хорошей тут насоветовали уже прилично. Засим откланиваюсь.

#52 
  moose старожил07.11.18 21:37
NEW 07.11.18 21:37 
в ответ dymanoid 07.11.18 21:34

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

#53 
Программист коренной житель08.11.18 07:16
NEW 08.11.18 07:16 
в ответ dymanoid 07.11.18 20:34
Не надо мне про синглтоны рассказывать, я сам кому хочешь расскажу.

Я не знаю, что ты можешь рассказать про синглтоны, но привести рабочий код синглтона ты не смог.


Замена модификатора сеттера на internal никаким образом не поможет синглтон сделать mockable.

Ошибаешься. Почитай про InternalsVisibleToAttribute. В отличае от тебя, я не только видел синглтоны в легаси коде, но и инжектил в эти синглтоны свои мок-объекты.


ЗЫ. Хот-патчинг - это замена кода приложения в процессе выполнения приложения.

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

#54 
AlexNek патриот08.11.18 12:20
AlexNek
NEW 08.11.18 12:20 
в ответ moose 07.11.18 21:15
что касается идей о задачах, выполняемых ф-ми, но приведенная мной одну только и решает. и одновременно - несколько, как и любая другая практически.

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

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

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

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

#55 
Simple Nothing is f*cked08.11.18 18:03
Simple
NEW 08.11.18 18:03 
в ответ moose 07.11.18 21:37

Вообще-то это азы. Если вы азов не знаете, то это не его вина.

#56 
  moose старожил08.11.18 19:21
NEW 08.11.18 19:21 
в ответ AlexNek 08.11.18 12:20, Последний раз изменено 08.11.18 19:22 (moose)

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

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

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

#57 
vovancpp постоялец16.11.18 15:43
NEW 16.11.18 15:43 
в ответ dymanoid 06.11.18 23:41
Нужно почитать про SOLID - это альфа и омега современного ООП

c SOLID нужно тоже аккуратно. Некоторые с ним не согласны: egor256: SOLID Is OOP for Dummies

#58 
MrSanders старожил16.11.18 17:54
NEW 16.11.18 17:54 
в ответ vovancpp 16.11.18 15:43, Последний раз изменено 16.11.18 18:05 (MrSanders)

Нда-с. "Дураков у нас на сто лет вперёд припасено." (c) Егорка чушь прогнал. Причём он кроме Open-Close и не спорит ни с чем. И аргументов никаких не приводит кроме "а чо, это надо?". А к остальному у него претензия "а раньше оно по другому звалось." Не читайте его больше.

Кстати, что-то мне кажется что похожую статью мы тут года два назад обсуждали...

P.S. Нашёл. Полтора года назад именно про эту статью то же самое писал :). https://foren.germany.ru/arch/showmessage.pl?Number=323101...


#59 
  moose старожил06.12.18 21:03
NEW 06.12.18 21:03 
в ответ moose 08.11.18 19:21

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

#60 
dymanoid знакомое лицо06.12.18 23:12
dymanoid
NEW 06.12.18 23:12 
в ответ moose 06.12.18 21:03

Такие вещи не тестятся юнит-тестами. Это задача интеграционных тестов.

#61 
MrSanders старожил07.12.18 09:36
NEW 07.12.18 09:36 
в ответ dymanoid 06.12.18 23:12

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

Просто прохождение теста не гарантирует что код действительно thread-safe.

#62 
Simple Nothing is f*cked07.12.18 15:32
Simple
NEW 07.12.18 15:32 
в ответ MrSanders 07.12.18 09:36

нп

В насквозь-легаси пхп-проекте начал писать юнит тесты!

#63 
dymanoid знакомое лицо07.12.18 19:14
dymanoid
NEW 07.12.18 19:14 
в ответ MrSanders 07.12.18 09:36, Последний раз изменено 07.12.18 19:15 (dymanoid)

Потому что это будет тест ради клизмы, а не ради теста. В реальной среде всё будет по-другому - здрасьте, рейскондишн. И на кой писать такой юнит-тест тогда?

Юнит-тесты пишут, не для того чтобы ошибки искать, кстати.

#64 
  moose старожил07.12.18 20:23
NEW 07.12.18 20:23 
в ответ dymanoid 07.12.18 19:14
Юнит-тесты пишут, не для того чтобы ошибки искать, кстати.

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

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

#65 
MrSanders старожил07.12.18 23:40
NEW 07.12.18 23:40 
в ответ dymanoid 07.12.18 19:14
Потому что это будет тест ради клизмы, а не ради теста. В реальной среде всё будет по-другому - здрасьте, рейскондишн. И на кой писать такой юнит-тест тогда?

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

Юнит-тесты пишут, не для того чтобы ошибки искать, кстати.

И для этого тоже. Не далее как вчера. Сообщили об ошибке - "что-то annotation processor не обрабатывает класс MyPowerfullClassWithThousandMethodsAndFields, мы класс проверили, вроде всё по документации."

Юнит тестов у процессора нет. Взял документацию и четко по ней написал блэкбокс тесты. Прогнал, нашёл ошибку. Если поле было аннотировано хоть какой-то аннотацией другие проверки не делались.

Что я сделал не так, и почему я не мог написать юнит-тест чтобы искать ошибку?

#66 
  moose старожил18.12.18 12:13
NEW 18.12.18 12:13 
в ответ Simple 05.11.18 16:17, Последний раз изменено 18.12.18 12:14 (moose)

попалась в каком-то контексте книга Working effectively with legacy code, вспомнил, что здесь тоже советовали, и купил (сорок с чем-то выкинул). что могу сказать? давно не делал такой бесполезной траты денег (на книгу) и времени (на ознакомление). естественно, поначалу читал все подряд, даже трогательные рассказы о том, как мама (или папа?) в детстве купили компьютер, и как он ... в общем, как автор "рос в теме". затем, когда стало ясно, что количество слов явно превышает количество смысла, стал чинать только код до волшебного улучшалова и после, а когда скука совсем одолела, перешел сразу к стр. 323 (в книге 423), где все изложенное еще раз изложено более сжато (но все равно размазано).

еще раз повторю, напрасно потратил время/деньги. не попадитесь. не извлек из текста для себя НИЧЕГО нового (а уж полезного тем более). вот так бывает.

#67 
MolMed гость18.12.18 19:43
MolMed
NEW 18.12.18 19:43 
в ответ moose 18.12.18 12:13

Чтобы не покупать, можно, разумеется исключительно для ознакомления (!) скачать с Libgen или зеркал. Я всегда так делаю.

#68 
1 2 3 4 все