Пустой виртуальный метод вместо интерфейса
Я не очень понимаю, зачем мок-фреймфоркам использовать рефлекшены :)
С примерами не получится, я почти всё что о шарпе знал уже благополучно забыл. Затем, чтобы можно было работать с неидеальным кодом.
В яве самое, пожалуй, частое применение: подменить значение приватного поля.
Сеттера нет, поле инициализируется или конструктором, или каким-нибудь DI фреймворком. А геттер сделали финальным (не виртуальным, в подклассе не перепишешь).
Впрочем, есть и такие фреймворки, которые умеют подменять буквально все (в том числе и статики)... но вот только их мало кто использует :D
Ну. насчёт мало. Пытаются постоянно. У меня уже рука устала линейкой по пальцам бить. Но иногда прям действительно приходится. Как подсунут тебе какую-то библиотеку, так чтобы замочить в ней доступ к какому-нибудь сервису или к БД приходится или конструктор или какой-то приватный метод подменять.
Но в яве это уже байткод менять на лету надо. Столько весёлых побочек приносит, особенно если при тестах оптимизацию не отключать.
Затем, чтобы можно было работать с неидеальным кодом.
Все самые популярные мок-фремворки не работают с кодом. На вход ты даешь интерфейс или какой-нибудь класс и преймворк "пишет" свои классы наследники. Идеальный там код или нет вообще никого не интересует.
В яве самое, пожалуй, частое применение: подменить значение приватного поля.
Наиболее популярные мок-фремворки этим не занимаются.
Щас Котлин у всех на слуху.
Ну какой ещё котлин, ява или тем более сишарп, это всё для древних стариков, молодёжь выбирает лучшее, Я сказал учить всем Solidity!
вот тут пример простейшего вендора, и интерфейс токена, заливаю холокост токен, но дергал смартконтракт не через функцию, а фаллбак.
в результате ханурик, вклинился, захватил мой смартконтракт, вывел бабосики! 999.999 холохосттокенов ещё остались висеть на балансе:

Все самые популярные мок-фремворки не работают с кодом.
Да ниможитбыть! Кого-то засосала трясина ИИ. Представил себя большой языковой моделью.
Ладно, раскрою мысль: "чтобы программист, мог с помощью этих фреймворков писать тесты для неидеального кода".
Наиболее популярные мок-фремворки этим не занимаются.
Ну, или у нас разные популярные мок фреймворки, или кто-то в мире ява как я в шарпе. Мокито популярный? Что делает аннотация @InjectMocks в его расширении для JUnit-а?
А уж что делает EasyMock с рефлексией, чтобы работать с bridge - методами...
Ладно, раскрою мысль: "чтобы программист, мог с помощью этих фреймворков писать тесты для неидеального кода".
Всегда можно нашлепать нетестируемый код и потом говорить, что фреймворки плохи :)
Неидеальный код можно приближать к идеалу рефакторингом.
Ну, или у нас разные популярные мок фреймворки, или кто-то в мире ява как я в шарпе.
Я про мир явы вообще ничего не знаю. Знаю только, что Java существует
Мокито популярный?
Судя по всему, это что-то для специальное для явы. Ну и судя по тому, что они умеют подменять статики, рефлекшенами они таки пользуются.
А уж что делает EasyMock с рефлексией, чтобы работать с bridge - методами...
EasyMock тоже что-то исключительно из мира явы. Кто такие bridge
- методы я не в курсе.
Всегда можно нашлепать нетестируемый код и потом говорить, что фреймворки плохи :)
Подмена понятий. Мы не о "хороший-плохой" а о "зачем мок-фреймворку использовать reflection".
Неидеальный код можно приближать к идеалу рефакторингом.
Не всегда. Ну и пока не приблизили, что, не тестируем?
Мы не о "хороший-плохой" а о "зачем мок-фреймворку использовать reflection".
Задача мок-фремворка - подменить внешние зависимости у unit under test.
Т.е. для того, чтобы код был тестируемым, нужно фактически 2 вещи: 1) unit under test должен зависить от абстракций и 2) нужно иметь возможность инжектить эти абстракции (самое простое - через конструктор).
Если эти 2 условия соблюдаются, то никакие рефлекшены мок-фреймвокру не нужны :)
Ну и пока не приблизили, что, не тестируем?
Пока не приблизили, это все легаси, которое и так работает. Покрывать тестами легаси можно, но вот насколько это эффективно - большой вопрос.
Если же у вас новый код нетестируемый, то либо вы сами себе злобные Буратины, либо тесты вам нахрен не нужны.
Если эти 2 условия соблюдаются, то никакие рефлекшены мок-фреймвокру не нужны :)
Угу. А рядом порхают единороги и попукивают радугой феи. Вот только есть ещё одна загвоздка - юнит у нас не класс. Юнит у нас - метод. И для тестирования метода приходится мочить другие методы того же самого класса. Или суперкласса. А вот они могут быть из другого фреймворка. И с ними и случаются засады.
// Customer - наш класс. А Entity - класс фреймворка, которому 5 лет, от другой фирмы, от которого у нас нет исходников. // Да даже если б и были, модифицировать его мы не можем. public class Customer extends Entity { private final String firstName; .... private String plz; public boolean isSame(Person other) { // Тут логика, которую мы хотим протестировать if(other == null) return false; // а вот тут порылась собака. Метод getPrimaryKey() определён в Entity. Из чужого фреймворка. И он финальный. // т.е. его переписать нельзя. Он просто возвращает primary key с которым этот объект сохранён в БД. Или -1 если // ещё не сохранён. А сеттера к нему нет. Или скрыт. Бывает, да? Ели это тот же объект из БД то он "тот же самый". if(getPrimaryKey() == other.getPrimaryKey()) return true; // и дальше код, который надо тестировать. boolean result = getName().equals(other.getName() && ....; result &= //ещё что-нибудь. return result; } }
Ну и как же мне сделать этот код тестируемым по феншую? Написать свою public long getPkFromEntity() { return getPrimaryKey();} ?
Ну, т.е. расширить продуктивный код исключительно в интересах юнит теста, сделает его менее читаемым и добавить возможность подклассу поломать ПК, переписав мой метод.
А может лучше воспользоваться рефлексией и подставить в приватное поле other.primaryKey (унаследованное от суперкласса Entity) значение 123?
Пока не приблизили, это все легаси, которое и так работает.
Эту реплику из зала мы, товарищи, сейчас отметём с негодованием. Как неорганизованную. (ц)
Не верно. Это не наше легаси. И не его мы тестируем. Мы его должны использовать. Иначе ORM работать не будет. А тестировать мы должны свой код. Который от легаси зависит.
И таких примеров не один и не 10. Иногда бывают сильносвязанные классы. Ну вот надо чтобы А знало Б, а Б знало А. Цикл. И уже никто не может передать А или Б в конструктор. Делаем завод, который пару А и Б сгенерирует, конструкторы и сеттеры прячем чтобы только завод их вызвать мог, геттеры финальные (не переписываемые) опять же всё по феншую. А вот красота с "инжектить абстракции" в тесте поломалась.
А бывают DI фреймворки, которые ТРЕБУЮТ чтобы конструктор был пустым. А все зависимости засовывают напрямую в аннотированные поля. Приватные. И наш красивый код ДОЛЖЕН использовать этот фреймворк. Потому что экосистема. И именно потому что экосистема к коду архитектурное требование - сеттеров быть не должно (мнэ, на вашем языке - проперть ридонли :)). Потому что значения установит DI. А геттеры или финальные (не переписываемые) или их нет, чтобы не давать доступа к полю даже наследникам.
И сидишь ты такой и чешешь тыковку - поднимать DI для тестов со своей конфигурацией (а это тоже иногда не сложно, а невозможно из-за конфликтов реализаций) или тупо воспользоваться фишкой мок-фреймворка и подменить значения приватных полей. И угадай с трёх раз, что же обычно выбирают?
Когда у тебя ну просто всё с чистого листа, и пишешь ты свой новенький REST сервис, например, то счастье возможно. Но авторы мок-фреймворков к счастью понимают, что не только лишь все могут таким наслаждаться, мало кто может это делать.
P.S. Кстати, примерно такой и была первая версия EasyMock-а. Только интерфейсы мочить умела. Но автор быстро понял что этого для реальной жизни не хватает.
А бывают DI фреймворки, которые ТРЕБУЮТ чтобы конструктор был пустым. А все зависимости засовывают напрямую в аннотированные поля. Приватные. И наш красивый код ДОЛЖЕН использовать этот фреймворк. Потому что экосистема. И именно потому что экосистема к коду архитектурное требование - сеттеров быть не должно (мнэ, на вашем языке - проперть ридонли :)). Потому что значения установит DI. А геттеры или финальные (не переписываемые) или их нет, чтобы не давать доступа к полю даже наследникам.
Сначала призывают к соревнованиям по бегу, затем заранее простреливают обе ноги, затем дают пару модных костылей, которые немного облегчат участь.
Вот только есть ещё одна загвоздка - юнит у нас не класс. Юнит у нас - метод.
Да хоть кого выбери юнитом. Юнит в любом случае должен работать с абстракциями.
Ну и как же мне сделать этот код тестируемым по феншую?
Очень просто - использовать абстракции. Если их нет, то ввести :) Использовать "тонкие" DTO,
Можно также использовать прокси-объекты.
Было бы желание сделать по феншую :)
Ну, т.е. расширить продуктивный код исключительно в интересах юнит теста, сделает его менее читаемым и добавить возможность подклассу поломать ПК, переписав мой метод.
Ну это зависит от того, как расширять :) Совсем необязательно, что будет менее читаемым.
А может лучше воспользоваться рефлексией и подставить в приватное поле other.primaryKey (унаследованное от суперкласса Entity) значение 123?
Угу, а еще тебе понадобится реальная БД, т.к. где-нибудь в этом Entity погребено подключение к БД. Надо будет держать эту БД в валидном для теста состоянии. А еще не дай бог кому-то придет в голову поменять логику Entity и приватное в лучшем случае удалят, а в худшем - будут использовать как-то иначе. Или натрафят обфускатор :)
А бывают DI фреймворки, которые ТРЕБУЮТ чтобы конструктор был пустым.
Ну во времена, когда один класс может имень много конструкторов это вообще самая маленькая проблема в мире :)
И именно потому что экосистема к коду архитектурное требование - сеттеров быть не должно (мнэ, на вашем языке - проперть ридонли :)) Потому что значения установит DI. А геттеры или финальные (не переписываемые) или их нет, чтобы не давать доступа к полю даже наследникам.
Да пофиг :) Переходи на уровень абстракций, т.е. интерфейсов. На этом уровне совершенно наплевать на всякие там финальные или приватные методы/проперти... их просто нет, т.к. интерфейс по определению публичный :)
И угадай с трёх раз, что же обычно выбирают?
Стреляющие себе в ногу выбирают рефлекшены :) Это ж очевидно :D :D :D