unit tests
я относительный новичок в теме, т.к. классических юниттестов написал несколько штук, и большинство из них - "hello, unittest!" из различных учебников.
решил оюниттестить один из недавних проектов, и столкнулся с тем, что не могу придумать, как это сделать для большинства ф-й.
для некоторых особенно простых просто смысла не вижу, т.к. они написаны, чтобы сэкономить место, и их правильность очевидна (а если бы нет - всплыло бы сразу). например, я не хочу иметь по всему коду длиннющие строчки получения форматированной даты последней субботы, это вынесено в ф-ю, и я ее просто вызываю. зачем сюда юниттэст?
другой тип ф-й - web-scrapping. мы идем на какой-то сайт определенный, и выискиваем там определенные вещи по определенным критериям. каждый раз там будет в общем случае находиться что-то другое.
или разработчики сайта могут что-то изменить, и я ничего не найду, и это бы неплохо проверить, но это будет обнаружено в самой ф-ии по ходу дела. а юниттэст, по определению, должен проверить, правильно ли работает моя ф-я, т.е. я ей должен подсунуть "правильный сайт", и "неправильный сайт", и говорить окей, если на правильном найдено то, что ожидалось, а на неправильном - не найдено, или найдено не то, что ожидалось. но что даст такой тест, если хозяин сайта что-то изменит? тест и дальше будет проходить, но ф-я будет возвращать некорректный результат. в общем, что-то у меня работа над юниттестами превращается в кошмар, если сравнить ее с собственно "нормальной разработкой". растолкуйте, где я глючу, что почитать.
Я тоже не профи в юнит тестах, так как этим нужно заниматься постоянно.
Но если функцию нельзя оттестировать, то она написана не совсем верно - выполняет слишком много действий.
А так глянь тута, тоже самое можно сделать и для внешнего сайта. Ключевое слов Mocking
https://www.danylkoweb.com/Blog/the-fastest-way-to-mock-a-...
https://www.c-sharpcorner.com/UploadFile/dacca2/fundamenta...
Юнит-тесты нужны не для того чтобы проверить правильность программы (хотя это часто как дополнительный бонус получается, хотя бы частично), а чтобы минимизировать ошибки, неизбежно возникающие во время изменения кода (дальнейшая разработка, багфиксы).
Посему имеет смысл писать тесты на всё, что тестируется. Даже на "а + b", потому что в будущем кто-то может поменять тип аргументов метода, например, и a+b неожиданно станет возвращать мусор.
т.е. это на случай, если мы начнем что-то менять без оглядки на уже отлаженное?
а я читаю другое. например, такой вот "рекламный набор":
https://dzone.com/articles/top-8-benefits-of-unit-testing
, который, похоже, сто раз цитируется."
в качестве хэллоуорлд для юнит тест любят приводить тест чего-то "калькулятор", и тестируют его "эдд". и юниттэст выглядит очень красиво: проверь, что вернет на 2+2, если четыре - ок, нет - бей тревогу!
у меня задача, как я уже описал, проверить работу ф-ии, которая не "делает очень много", как алекс описал. она просто
- читает содержимое сайта (что тут проверять?)
- ищет там определенные тэги (списки тэгов), предполагая, что там находится то, что мы ищем (снова что проверять?)
- если находит - возвращает что нашла в удобном формате
- если не находит (это может выясниться на разных этапах: что-то с интернетом, что-то с сайтом, ...?) - возвращает нечто, что дает понять, что не нашли.
можно, конечно, "промОчить" сайт, заставив читать не сайт, а какой-то мок, причем их должно быть несколько, и переделать всю кухню: пусть ф-я не делает все это, а разделим ее пополам: одна часть читает сайт. для нее юниттест - просто вернула она нечто длиннющее или нет. вторая часть получает это прочитанное (или мок) на входе, и должна из этого что-то извлечь. и тут мы можем понаделать моков на любой случай: прочитанное содержит нечто, из чего извлекается "ура!", прочитанное содержит то же "ура!", но в другом (третьем) допустимом месте, сайт изменил структуру, и "ура!" уже находится в другом месте, мы его не находим...
можно, конечно, это проделать. но это:
- потребует дополнительных затрат
- если структура сайта изменится, тесты будут проходить, но результаты мы получать не будем (хотя они будут присутствовать на сайте, просто искать их нужно теперь иначе). т.е ваш случай "Даже на "а + b", потому что в будущем кто-то может поменять тип аргументов метода, например, и a+b неожиданно станет возвращать мусор." будет иметь место быть, но наш юниттест не поможет нам это поймать: тесты, повторяю, будут и дальше благополучно проходить на красиво подобранных моках, а мы будем получать или мусор, или ничего. и самое плохое - может случиться, что выводы станем делать, основываясь на наших юниттестах. берусь утверждать, что такие вещи можно протестировать только интекрационными тестами, а выдумывать для подобных вещей юниттесты - дань моде, которая только мешает жить.
человек под фонарем что-то ищет. второй подходит:
- что-нибудь потеряли?
- да, вот ключи выронил...
- здесь?
- да нет, вон там, в кустах.
- а почему здесь ищете?
- здесь светлее...
- читает содержимое сайта (что тут проверять?)
- ищет там определенные тэги (списки тэгов), предполагая, что там находится то, что мы ищем (снова что проверять?)
- если находит - возвращает что нашла в удобном формате
- если не находит (это может выясниться на разных этапах: что-то с интернетом, что-то с сайтом, ...?) - возвращает нечто, что дает понять, что не нашли.
уже как минимум 3 различных задачи (Читаем,Ищем, анализируем результат). А говоришь простая. Задача должна быть одна и только одна
Мне пофигу, что пишут в рекламных наборах. Я говорю, опираясь на многолетний опыт и текущую сферу занятости (пилим софт для разработки и тестов софта для авто- и авиапрома).
Если твоя функция (что за "ф-ии" - руки отсохнут слово полностью написать, и это при таких-то романах в постах?) делает половину логики программы, то она нетестируемая. Я написал - тестить всё, что тестируется. Твой софт не тестируется, судя по всему. Поэтому делать для него юнит-тесты бысмысленно, так начальству и скажи. Времени и денег будет потрачено много, а результатов, кроме галочки "у нас юзают юнит-тесты" никаких. Смысл?
Доступен сайт или нет (http-response code)она просто
- читает содержимое сайта (что тут проверять?)
Здесь как раз нужен мок, симулирующий тэги. Или просто мок-параметр, соответствующий тэгу (одному или всем это вам решать). Проверяется возвращаемое значение, см. следующий пункт- ищет там определенные тэги (списки тэгов), предполагая, что там находится то, что мы ищем (снова что проверять?)
Проверка полей/формата. Естественно, исходные значения должны браться из мок'a.- если находит - возвращает что нашла в удобном формате
Проверка корректного возврата ошибки- если не находит (это может выясниться на разных этапах: что-то с интернетом, что-то с сайтом, ...?) - возвращает нечто, что дает понять, что не нашли.
Как уже здесь вам сказали, полезно все вышеописанные операции выделить в отдельные функции.она просто
Будет выдаваться ошибка (а лучше 2): тэги не найдены и / или некорректный формат данных. В этом случае необходимо будет изменить вашу программу и моки.если структура сайта изменится, тесты будут проходить, но результаты мы получать не будем
Я в веб-специфике не силён, может, там и можно "х*як-х*як, и в продакшн". А в серьёзном энтерпрайзе или там где безопасность/надёжность важна, никто не даст рефакторить огромную кодовую базу "чтобы юнит-тесты ввести". Это ж надо целый отдел посадить и параллельно с "нормальной" разработкой вести этот самый рефакторинг, плюс проблемы с интеграцией и т.п. На моей памяти ни одна контора не обладала такими железными яйцами и свободными средствами, чтобы себе это позволить.
Именно поэтому во всех "рекламных материалах" и говорят - пишите юнит-тесты с самого начала. Потом пришпандурить не получится. Ну, только если там "хелло ворлд" на 10 тыщ строк.
Я в веб-специфике не силён, может, там и можно "х*як-х*як, и в продакшн".
Там это не можно. Там это нужно. И юнит тесты - ругательство :)
А в серьёзном энтерпрайзе или там где безопасность/надёжность важна, никто не даст рефакторить огромную кодовую базу "чтобы юнит-тесты ввести".
Ну... Мы справились. Просто аргументы другие. Рефакторинг не для того, чтобы юнит-тесты ввести, а для повышения удобства сопровождения/модификации и уменьшения времени поиска ошибок. А юнит-тесты помогают находить критические места: где тест писать сложно, там и код сложный и непонятный - надо рефакторить. Мы перевели (почти) весь код с использования статических переменных на DI, синглтоны все стали наконец-то немодифицируемыми и стало их где-то в 10 раз меньше.
Стоило нефигово. Точно не скажу но порядка 20,000-50.000 часов.
Опять про юнит-тесты? :)
например, я не хочу иметь по всему коду длиннющие строчки получения форматированной даты последней субботы, это вынесено в ф-ю, и я ее просто вызываю. зачем сюда юниттэст?
Вопрос в том, что ты хочешь протестировать.
Если ты хочешь протестировать правильность результата этой функции, то надо делать тест результата.
Если ты хочешь протестировать то, что эта функция была вызвана (например при форматировании текста эта функция должна быть вызнана родно один раз), тогда делаешь другой тест.
Когда коллеги обращаются ко мне с просьбой помочь написать юнит-тест, то мой первый вопрос: что ты хочешь протестировать? Правкически всегда этот вопрос сдавит в тупик :D И это, пожалуй, один из главных плюсов юнит-тестирования - разработчик должен понимать, что он тестирует. А потом уже можно будет решать вопросы о том как тестировать.
Я использую разбиение каждого теста на 3 части (подход Роя Ошеровава мне показался очень удобным и наглядным):
void TestName () { // Arrange .... // Act .... // Assert .... }
Таким образом всегда видно, что именно тестируется и как работает unit under test.
но что даст такой тест, если хозяин сайта что-то изменит? тест и дальше будет проходить, но ф-я будет возвращать некорректный результат.
Тесты не имеют хрустального шара и не могут предвидеть изменений каких-то сторонних сайтов или библиотек. Более того, юнит-тесты и не должны взаимодействовать ни с какими сторонними ресурсами. Тестами можно проверить правильность твоего кода и твоей логики, а также убедиться, что эта логика не поломалась при изменении твоего кода.
Более того, юнит-тесты и не должны взаимодействовать ни с какими сторонними ресурсами.
+1
Как только для теста нам нужна работающая БД, веб-сервис или какой-то другой сервер, это уже не юнит тест. А как минимум - "интеграционный".
Если есть опасения что структура/содержимое чужого сайта может измениться - надо делать метод, который парсит ответ, отказоустойчивым, выдавать, например, последнее считанное значение (главное чтобы с ошибкой не вываливался) и делать программку/скрипт (назовите это "health check") - раз в день/час проверять можем ли мы пропарсить ответ сайта и если нет, то громко кричим (как можем, е-мейл, смс, в твиттер разработчикам :)).
Памятники таким ставить надо. Не каждая контора пойдёт на это.
Тут памятник надо ставить убедившему руководство архитектору. Потому что инвестиции уже (почти или совсем) окупились. Одних только критических апдейтов теперь выкатываем в 2 раза меньше. На правку ошибок тоже на 40% меньше времени тратится (и количество уменьшилось, и на каждую в среднем). Правда теперь менеджеры воют что реализация нового функционала занимает больше времени, потому как тесты писать и править ранее написанные приходится. Вот только брешут-с :)
Для задачи ТС ваш "health check" избыточен: его программа и так шерстит сайт(ы) круглосуточно. Достаточно обычного протоколирования ошибок парсинга.раз в день/час проверять можем ли мы пропарсить ответ сайта
Если будет выдавать информативную/ые ошибку/и, то никакого "health check" и не потребуется.надо делать метод, который парсит ответ, отказоустойчивым, выдавать, например, последнее считанное значение (главное чтобы с ошибкой не вываливался)
с этой книги я начинал знакомство с предметом. там а начале, кстати, речь о том, что мы так или иначе постоянно создаем юниттесты, просто "пора это все свести в систему".
вот, предложу для примера желающим простой пример. не знаю, как его "в систему привести". что ф-я (читать: функция) делает, ясно из кода:
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вроде ясно, что код делает, и вызывающий проверит, вернули ли хоть какие-нибудь имена. если нет - будет отправлено сообщение об этом. но у нас пока примитивная задача: написать юнит-тест для этой ф-ии (читать: функции). главный вопрос остается: что мы хотим протестировать? полагаю, что цель - проверить, выполняет ли ф-я (читать: функция) то, чего мы от нее ожидаем.
Вот эта х-ня (читать: функция) - типичный пример божественного объекта. И швец, и жнец, и на дуде игрец. И в веб лезет, и контент получает, и XML парсит.
Её надо раздраконить на 2-3 отдельные, руководствуясь S и I из SOLID. Применяя D из SOLID. Не забывая про DRY при этом.
Тогда всё будет замечательно тестироваться.
А ну-ка примерчик про синглтоны. На 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(); } }
т.е. вы предлагаете, чтобы каждая ф-нкция содержала не более одного оператора? я могу это догнать только если ф-я становится слишком необъятной, и поэтому труднопонимаемой, или часть кода можно выделить в отдельную ф-ю, которая может использоваться другими. но вы бы лучше пояснили вашу идею , переиначив этот примитивный примерчик, "раздраконив его на 2-3 отдельніх, руководствуясь этими странными словами (S,I, ...)
заодно пояснив, чем ваш вариант будет лучше практически. пока все идет так, что без человека не обходится. просто вызыватель пришлет имэйл с этими найденными "suggestions". и если там будет ничего, то кто-то должен будет полезть туда и разобраться. предложите вариант, который облегчит жизнь.
P.S. программирую на Java, JavaScript и PHP
зачем? если дело в том, чтобы проверить, умеет ли ф-я разыскивать в прочитанном с сайта полезное, то можно запэтчить requests mock'ом, который будет возвращать "правильный вариант", а потом - "неправильный". и мы увидим, работает ли наша примитивная ф-я как мы задумали. я вот почему-то вижу, что она может перестать работать только в единственном реальном случае: формат сайта изменится. и мне важно об этом как можно раньше узнать. а опасность, что кто-то пришлет "неправильный" параметр name - второстепенна. я просто не вижу НИКАКОЙ необходимости здесь что-то рефакторить, создавать юнит тэст вообще, кроме как "соблюдение ритуала". разубедите, если вы иначе считаете.
припомнилась одна история, когда потребовалось по протоколу передавать даты следующих событий. события могли проходить только по определенным дням в определенное время.
и вот задается количество событий, скажем, 5, и в параметрах стоит, что события происходят по понедельниками, средам и субботам в 17:00. событие входит в рассмотрение, если до его наступления - не менее 15 минут. вот здесь я понаюниттэстовался вволю. наверное, какой-нибудь простенький тулчик помог бы, но тогда все было сделано "на колене": задаешь дату-время и количество событий, смотришь, возвращается ли тот список, что ожидаешь. кстати, не так сложно было этот наколенковый юниттест провести, как придумать сами тесткэйсиз.
Нужно почитать про SOLID - это альфа и омега современного ООП. Это не какие-то там вещи типа TDD, про которые все спорят (хорошо это или нет), а общепризнанные принципы разработки, которые значительно увеличивают качество кода и тем самым значительно уменьшают стоимость его поддержки.
Я не предлагаю каждую запятую в функции превращать - это к джаваскриптерам. Я предлагаю писать качественный код без мегамотов и прочих смурфиков.
главный вопрос остается: что мы хотим протестировать? полагаю, что цель - проверить, выполняет ли ф-я (читать: функция) то, чего мы от нее ожидаем.
:) Это так мой шеф говорит "мне надо, чтобы все работало" :)
А чего ты ожидаешь от этой функции? :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 -> результат получаешь массив из одного элемента с заранее известным именем.
Понятно, что в реальности выделение групп и нод - более сложные процедуры, поэтому стоит подумать над тем, чтобы выделить эти процедуры в отдельные функции (и тестировать их по отдельности).
Не, я по жабе. Но на сишарпе тоже должно быть что-то похожее.
Ну, в яве private static final тоже только через рефлекцию менять можно. Чем мы и занимались лет 5 для тестов, пока весь код в порядок приводили :)
Но. Самое страшное не в этом. Самое страшное это отловить все ссылки на такой синглтон, который нам для теста надо моком подменить. Потому что как рассуждает программизд? "Это же синглтон, не меняется, чем три раза A.getInstance() вызывать, я вызову его один раз и сохраню в своем статическом поле". И всё, мы приплыли. Хотя, надо признать они такое же и с "вставленными" DI полями делать норовят. Пока ручки не отобьешь.
Во-первых, начнем с того, что синглтоны - это зло :) И нужно иметь очень важную причину, чтобы использовать синглтон.
Во-вторых, предусмотреть бэк дор для тестов - как нефиг делать (не знаю, что ты понимаешь под хот-патчингом, но никаких танцев с бубном :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")]
и все.
т.е. приведенный пример слишком сложен, чтобы пороиллюстрировать вашу идею? может, найдете таки пять минут? : )
Я со своей стороны влезу. Допустим, вы решили что юнит-тест для этого метода важен и нужен.
А как вы будете проверять, например, что метод вернёт ошибку, если в ответе ваших div-ов не будет? Ведь URL сервера у вас жестко внутри метода вбит.
Технически - нет, не нужно. До всего можно добраться. В крайнем случае модифицируя байт-код агентом (см. PowerMock).
А вот практически приходится. У нас были тесты, проверявшие права доступа. И чтобы переинициализировать (очистить после теста) классы, которые тестами затрагивались, приходилось что-то около 10-15 статических полей то в null то в 0 то в false сбрасывать. И каждый раз, когда классы менялись (то просто поле переименуют, то его в другой класс перенесут, то вообще удалят), сыпались тесты => выкатывать изменения нельзя.
Договорились что в двух классах будут protected методы junitReset(), а наши тесты будут их вызывать. После этого, при таких же изменениях как и раньше, сразу вылетает ошибка компиляции в junitReset(), его правят и тесты работают как и раньше.
Теперь от этих полей избавились, осталось 2, но метод убирать не хотят. Говорят нам проще с ним, чем время от времени в ошибках тестов разбираться.
Не, меня его синтакс пугает. Ну и делать моки конструкторов, статических и приватных методов должно быть больно :)
А jmockit слишком упрощает. Про то что ты его запускаешь с агентом упоминается, но не поясняют а что же это такое. А это, товарищи - модификация всего загружаемого байт кода. И что ты в результате тестишь не знают даже разработчики jmockit-а.
Не учите папу кашлять. Не надо мне про синглтоны рассказывать, я сам кому хочешь расскажу.
Я же писал, что если есть 10-летний лигаси-код в огромном энтерпрайз-продукте, который напичкан синглтонами под завязку, то никто в здравом уме не пойдёт всё это "рефакторить". За редким исключением, когда менеджмент понимающий и архитектор грозится уволиться, см выше.
А мои каракули в том посте - это был вопрос, как вот такие конструкции можно заменить моками. Вопрос оказался в пустоту, потому что товарищ на другой платформе работает. А в джабе я не силён, чтобы пример на джабе слабать.
Замена модификатора сеттера на internal никаким образом не поможет синглтон сделать mockable.
ЗЫ. Хот-патчинг - это замена кода приложения в процессе выполнения приложения. Есть на всех платформах, в том числе .NET. Например, можно любые методы менять на что угодно и вообще творить всякое непотребство. К сожалению, приходилось применять: ручная генерация IL и потом хардкорный asm, чтобы заставить jitter всё это дело схавать и не подавиться.
Идея в том, что по S (single responsibility) каждый класс, метод, функция должны выполнять только одну задачу. Это позволяет минимизировать изменения в программе, если изменились требования к этой задаче. Любое изменение - это потенциально источник ошибок. Меньше изменений - меньше ошибок.
В примере функция и лезет в веб, и получает контент, и парсит XML. По SOLID, функция, которая парсит XML, должна на вход его получать. Откуда он пришёл, ей должно быть по барабану. Другая функция лезет в веб и качает данные. Точка. Ей по барабану, кто эти данные использовать будет и как. И так далее.
Разбивая логику на связанные, но независимые блоки, автоматом получаем тестируемые блоки, поскольку их зависимости минимальны и есть чёткое понимание, чего давать на вход и чего ждать на выходе.
что касается идей о задачах, выполняемых ф-ми, но приведенная мной одну только и решает. и одновременно - несколько, как и любая другая практически.
ф-я в примере не "лезет в вэб". туда лезет метод get модула requests. достанет он мне что-то из интернета снаружи от ф-ии, и я это передам аргументом, будет только лишний аргумент в заголовке ф-ии и непонятно в контексте вызывающем, зачем мы лезем куда-то в интернет, когда нам абсолютно безразлично, как ф-я будет добывать эти suggestions: у нас своя логика, построенная ни том, получили мы их или нет. а она пусть хоть из пальца, хоть из файла, хоть генерирует случайно, ...
в общем, у этой ф-ии задача - сформировать и выдать список подсказок, и ее решением она и занимается.
вообще, здравому смыслу есть место где-нибудь во всех этих S, unit test, TDD и прочих религиях?
ps. кроме того, если "лезть в интернет" снаружи ф-ии, то представим себе, что ту, наружную, вызывающую, мы тоже должны оюниттестить... т.е. проблему просто перенесли этажом выше.
не проблема за пэтчить это "лазание в вэб", и пусть мок возвращает нам "правильный контент", который мы можем где-то прочесть из файла (ведь не будем же мы тащить в тест-код контент длиной несколько метров текста?), но что мы таким образом протестировали? то, что и без теста очевидно? я остаюсь при мысли, что не на каждый пук следует писать юнит-тест. приведенный пример попадает в категорию тестируемых, но смысла в такам юниттестировании - ноль, одни затраты.
Не надо мне про синглтоны рассказывать, я сам кому хочешь расскажу.
Я не знаю, что ты можешь рассказать про синглтоны, но привести рабочий код синглтона ты не смог.
Замена модификатора сеттера на internal никаким образом не поможет синглтон сделать mockable.
Ошибаешься. Почитай про InternalsVisibleToAttribute. В отличае от тебя, я не только видел синглтоны в легаси коде, но и инжектил в эти синглтоны свои мок-объекты.
ЗЫ. Хот-патчинг - это замена кода приложения в процессе выполнения приложения.
Ну совершенно очевидно, что это тоже не нужно, т.к. на крайний случай можно воспользоваться рефлекшенами и сделать инжект.
что касается идей о задачах, выполняемых ф-ми, но приведенная мной одну только и решает. и одновременно - несколько, как и любая другая практически.
В полемику сейчас вступать нет много времени, но с подобной точкой зрения мне трудно согласится, хотя можно понять ход Ваших мыслей.
Если рассматривать функцию как черный ящик, то она выдает то что вам требуется. Но функция имеет еще и "чёрный ход" она запрашивает откуда-то данные и их еще и перерабатывает.
Иначе говоря функция не "сквозная" она не занимается переработкой данных, что нарушает принцип тестирования: мы задаем данные с известным результатом и сравниваем полученный результат с ожидаемым.
Так что минимальные изменения, которые требуются - это дать функции "вход". Закиньте ей просто интерфейс для забора данных.
я не вижу принципиальной разницы, как ф-я получит данные (в силу незнания азов, видимо : ). можно просто запэтчить в тесте вход, не заморачиваясь с интерфейсами. иначе вы добавляете сложности и вообще "лишних забот" вызывающей ф-ии (возможно, не одной, а десятку), которые не входят в их S. и если когда-нибдуь ф-я get_suggestions() будет решать свою задачу иначе, не "лазя в вэб", то вам придется переписывать все вызывающие куски, и все их тесты.
я остаюсь при мнении, что юнит тесты на подобные ф-ии - религиозный ритуал. если кому-то необходимо (кошерно по его вере) - то свобода совести ему открывает для этого дорогу. но мне она же оставляет выбор не делать никаких юниттестов там, где это неуместно.
кстати, таки сделал через @mock.patch(...), чудесно рабатыват,
но польза - единственная: познакомился, как оно устроено. тест в дальнейшем использовать для тестирования этой ф-ии не буду, т.к. бессмысленно. только как пример, откуда можно будет списать, когда это посчитаю разумным, т.е. юнит тест будет облегчать жизнь, а не усложнять и без того непростое
Нужно почитать про SOLID - это альфа и омега современного ООП
c SOLID нужно тоже аккуратно. Некоторые с ним не согласны: egor256: SOLID Is OOP for Dummies
Нда-с. "Дураков у нас на сто лет вперёд припасено." (c) Егорка чушь прогнал. Причём он кроме Open-Close и не спорит ни с чем. И аргументов никаких не приводит кроме "а чо, это надо?". А к остальному у него претензия "а раньше оно по другому звалось." Не читайте его больше.
Кстати, что-то мне кажется что похожую статью мы тут года два назад обсуждали...
P.S. Нашёл. Полтора года назад именно про эту статью то же самое писал :). https://foren.germany.ru/arch/showmessage.pl?Number=323101...
Почему? Я вполне могу один и тот же метод в 10 потоках прогнать, чтобы попытаться ошибки синхронизации найти. Для этого мне ничего "снаружи" тестируемого класса не надо. Чем не юнит-тест?
Просто прохождение теста не гарантирует что код действительно thread-safe.
Потому что это будет тест ради клизмы, а не ради теста. В реальной среде всё будет по-другому - здрасьте, рейскондишн. И на кой писать такой юнит-тест тогда?
Юнит-тесты пишут, не для того чтобы ошибки искать, кстати.
Юнит-тесты пишут, не для того чтобы ошибки искать, кстати.
у меня тоже такое мнение сложилось (не берем во вниманиее ТДД). просто наличие юниттестов и возможности их автопрогона позволяет убедиться (грубо), что, наваяв что-то в одном месте, мы не нарушили ничего в другом через какой-нибудь побочный эффект. хотя почему для "ошибки искать" юниттэсты не годятся? ведь мы все равно так или иначе наш код тестируем. если выработана привычка делать это юниттэстами, то неплохо, наверное.
я против юниттэстов ради юниттэстов, или когда ресурсы на их создание превосходит затраты на основной код. ну и не на каждый пук следует писать юниттэст. бывает "очевидный код". хотя ошибки в таком обходятся как правило дороже всего : )
Потому что это будет тест ради клизмы, а не ради теста. В реальной среде всё будет по-другому - здрасьте, рейскондишн. И на кой писать такой юнит-тест тогда?
Не совсем. Понятно что гарантии нет, но многие ошибки синхронизации или доступа к мониторам всё же отлавливаются. Так что лучше такой ничего не гарантирующий тест, чем никакого. Из личного: один такойтест помог сразу найти ошибку в исправленном коде - тупо не то имя монитора написано было, соответственно сразу дэдлок.
Юнит-тесты пишут, не для того чтобы ошибки искать, кстати.
И для этого тоже. Не далее как вчера. Сообщили об ошибке - "что-то annotation processor не обрабатывает класс MyPowerfullClassWithThousandMethodsAndFields, мы класс проверили, вроде всё по документации."
Юнит тестов у процессора нет. Взял документацию и четко по ней написал блэкбокс тесты. Прогнал, нашёл ошибку. Если поле было аннотировано хоть какой-то аннотацией другие проверки не делались.
Что я сделал не так, и почему я не мог написать юнит-тест чтобы искать ошибку?
попалась в каком-то контексте книга Working effectively with legacy code, вспомнил, что здесь тоже советовали, и купил (сорок с чем-то выкинул). что могу сказать? давно не делал такой бесполезной траты денег (на книгу) и времени (на ознакомление). естественно, поначалу читал все подряд, даже трогательные рассказы о том, как мама (или папа?) в детстве купили компьютер, и как он ... в общем, как автор "рос в теме". затем, когда стало ясно, что количество слов явно превышает количество смысла, стал чинать только код до волшебного улучшалова и после, а когда скука совсем одолела, перешел сразу к стр. 323 (в книге 423), где все изложенное еще раз изложено более сжато (но все равно размазано).
еще раз повторю, напрасно потратил время/деньги. не попадитесь. не извлек
из текста для себя НИЧЕГО нового (а уж полезного тем более). вот так бывает.