Пустой виртуальный метод вместо интерфейса
Если эти 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-а. Только интерфейсы мочить умела. Но автор быстро понял что этого для реальной жизни не хватает.