Комментарии 12
>И приходилось выбирать: или красивый дизайн, или качественное покрытие тестами
Я думаю вам стоит выкинуть PowerMock и почитать про dependency injection… Моки на статик методы это зло
Я думаю вам стоит выкинуть PowerMock и почитать про dependency injection… Моки на статик методы это зло
А при чем тут dependency injection (который я, к слову, неплохо знаю) и статик методы? Существует множество сторонних библиотек с набором utility-классов со статическими функциями. Зачастую, в таких ситуациях можно обойтись и без использования моков. А иногда их очень не хватает. Здесь я не призывал использовать статики или делать моки на все подряд. Ко всему же надо подходить с головой.
Подход «с головой» к пробелеме написания стабов для статик методов заключается в замене static method на instance method и DI.
Можете привести пример где вам надо сделать mock на utility-классы со статическими функциями?
Можете привести пример где вам надо сделать mock на utility-классы со статическими функциями?
Научите заменять static метод в сторонней библиотеке? Пример… ByteBuffer.allocate()
Можете привести пример где вам надо сделать mock на utility-классы со статическими функциями?
То есть не пример статической функции, это я и сам найду.
А пример где для тестирование надо сделать мок статической функции
Пример придуманный и совершенно сферический:
Опять же, всегда можно придраться, почему ByteBuffer не приходит извне функции. Но еще раз напоминаю, что это лишь пример, такой же сферический, как те, которые приведены в статье. Однако, похожие ситуации в жизни не раз бывали и в них не к чему было придраться. Просто на вскидку сложно вспомнить что то конкретное.
Да, в этом примере очень бы хотелось проверить, выполняется ли flip() для буфера. Предположим, что если его кто то удалит, все будет плохо.
public ByteBuffer serialize(Object obj) {
ByteBuffer buffer = ByteBuffer.allocate(new byte[100]);
serializer.serialize(object, buffer);
buffer.flip();
return buffer;
}
Опять же, всегда можно придраться, почему ByteBuffer не приходит извне функции. Но еще раз напоминаю, что это лишь пример, такой же сферический, как те, которые приведены в статье. Однако, похожие ситуации в жизни не раз бывали и в них не к чему было придраться. Просто на вскидку сложно вспомнить что то конкретное.
Да, в этом примере очень бы хотелось проверить, выполняется ли flip() для буфера. Предположим, что если его кто то удалит, все будет плохо.
Эмм, пример и правда слишком сферический, тут нечего даже сказать.
Скорее всего тут не надо проверять что «вызван метод flip», а просто смотреть на возвращенный буфер и проверять его свойства.
Все таки хотелось бы посмотреть какой был реальный кейс
Скорее всего тут не надо проверять что «вызван метод flip», а просто смотреть на возвращенный буфер и проверять его свойства.
Все таки хотелось бы посмотреть какой был реальный кейс
Есть два подхода к тестированию — BlackBox & WhiteBox. Если проверять только результат выполнения метода — это BlackBox тестирование. Если тестировать с проверкой всех «потрохов» — это WhiteBox подход. Мне позарез надо знать, что был вызван метод flip(), а не ручками выставлены значения. А то ведь всякое бывает.
А реальный кейс… Может Вы и сами скоро с подходящим столкнетесь. А если нет — тем лучше. Значит Вам конкретно эта функциональность PowerMock'а не пригодится. Но ведь важно просто знать, что возможность есть, что бы быть готовым.
А реальный кейс… Может Вы и сами скоро с подходящим столкнетесь. А если нет — тем лучше. Значит Вам конкретно эта функциональность PowerMock'а не пригодится. Но ведь важно просто знать, что возможность есть, что бы быть готовым.
Вот это вот «позарез надо знать» попахивает тем, что вы пытаетесь протестировать слишком много. Это приведёт к тому, что тестов будет слишком много, они будут слишком завязаны на коде, и как следствие, их придётся слишком часто менять при внутренних изменениях в коде, не влияющих на функциональность.
Тестировать надо функциональность, то есть «внешние проявления». Если вам позарез нужно знать, что был вызван метод flip(), значит, подумайте о том, что свалится, если этот метод не был вызван. И на эту ситуацию и напишите тест.
Тестировать надо функциональность, то есть «внешние проявления». Если вам позарез нужно знать, что был вызван метод flip(), значит, подумайте о том, что свалится, если этот метод не был вызван. И на эту ситуацию и напишите тест.
Вопрос о подходах unit-тестирования, а так же о том как и где их применять — это тема отдельной статьи и отдельного обуждения… В этой статье был обзор возможностей PowerMock для создания детализированных тестов.
И позвольте Вам возразить. Конечно, Вы частично правы. При таком подходе тесты будут завязаны на коде. И в случае изменений кода потребуется время на изменение тестов. Но это не всегда плохо. Упавший тест — повод задуматься: «А все ли я сделал правильно? Может кто то неспроста до меня сделал именно так?» Плюс ко всему, есть ряд ситуаций, когда тестирование по «внешним проявлениям» не подойдет. Простой пример: есть некий метод, предположим, public void open(), который как мы видим не принимает и не возвращает параметров. Внутри него происходит последовательный вызов нескольких функций из внутренних объектов (возможно так же и статические). Скорее всего, состояние тестируемого объекта изменится. Но, во-первых, это состояние может быть чисто внутренним и не торчать наружу. А следовательно, что бы его проверить необходимо производить танцы с бубном (или использовать PowerMock для удобного доступа к private полям). Во-вторых, в тестируемом методе наверняка важен порядок выполнения внутренних вызовов. Как Вы будете это проверять?
Думать о том, что свалится, если в этом методе что то будет не вызвано — это совсем другая задача, другого теста. Этот же метод вы оставите не протестированным.
Повторюсь, я утверждаю, что все тесты должны быть только такими. Все всегда зависит от ситуации. Просто всегда понимайте что вы делаете и что это может повлечь. Вы тестируете систему детально — будьте готовы к переделке тестов при рефакторинге архитектуры. Вы тестируете систему интеграционно в виде BlackBox — готовьтесь к тому, что внутри система может вести себя не корректно, а ваши тест кейсы просто не способны отловить эту особенность. И никогда не стоит говорить: «я крут и со мной такого не случится». Практика показывает, что случится. Причем в самый не подходящий момент.
И позвольте Вам возразить. Конечно, Вы частично правы. При таком подходе тесты будут завязаны на коде. И в случае изменений кода потребуется время на изменение тестов. Но это не всегда плохо. Упавший тест — повод задуматься: «А все ли я сделал правильно? Может кто то неспроста до меня сделал именно так?» Плюс ко всему, есть ряд ситуаций, когда тестирование по «внешним проявлениям» не подойдет. Простой пример: есть некий метод, предположим, public void open(), который как мы видим не принимает и не возвращает параметров. Внутри него происходит последовательный вызов нескольких функций из внутренних объектов (возможно так же и статические). Скорее всего, состояние тестируемого объекта изменится. Но, во-первых, это состояние может быть чисто внутренним и не торчать наружу. А следовательно, что бы его проверить необходимо производить танцы с бубном (или использовать PowerMock для удобного доступа к private полям). Во-вторых, в тестируемом методе наверняка важен порядок выполнения внутренних вызовов. Как Вы будете это проверять?
Думать о том, что свалится, если в этом методе что то будет не вызвано — это совсем другая задача, другого теста. Этот же метод вы оставите не протестированным.
Повторюсь, я утверждаю, что все тесты должны быть только такими. Все всегда зависит от ситуации. Просто всегда понимайте что вы делаете и что это может повлечь. Вы тестируете систему детально — будьте готовы к переделке тестов при рефакторинге архитектуры. Вы тестируете систему интеграционно в виде BlackBox — готовьтесь к тому, что внутри система может вести себя не корректно, а ваши тест кейсы просто не способны отловить эту особенность. И никогда не стоит говорить: «я крут и со мной такого не случится». Практика показывает, что случится. Причем в самый не подходящий момент.
Я кстати согласен с автором. Например, у нас есть код, много где завязанный на Hazelcast, у которого полно статических методов. По уму, надо рефакторить и выносить вызовы Hazelcast в класс-обертку, которую уже легко будет заменить моком. Но мы планируем от него избавиться в будущем, поэтому рефакторить ради тестов я смысла не вижу, если все равно этот код будет убран. Так что в данном конкретном случае проще заюзать PowerMock. Спасибо автору, буду теперь его использовать :)
автору спасибо, не знал про такую тулзу. Обычно тестировал методы, где есть статические вызовы путем выноса этих статических вызовов в отдельный метод и затем переопределяя его. Эта штука однозначно облегчит жизнь.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
PowerMock (+Mockito): новый взгляд на unit-тестирование