Итак, я должен извиниться перед Джимом. Он написал рабочий тест на mockito и JUnit, и я сказал ему в обзоре, что не думаю, что он сделал то, что ожидал. Хотя я был неправ, этот сценарий выглядит для меня как ошибка . Назовите это желательными неожиданными побочными эффектами.
Представьте, что у вас есть следующие два класса:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class Service { private String name; private Widget widget; public Service(String name, Widget widget) { this .name = name; this .widget = widget; } public void execute() { widget.handle(name); } } public interface Widget { void handle(String thing); } |
Ничего захватывающего там нет …
Теперь давайте попробуем протестировать сервис с помощью теста Mockito (JUnit 5 здесь):
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@ExtendWith (MockitoExtension. class ) class ServiceTest { @Mock private Widget widget; @InjectMocks private Service service = new Service( "Joe" , widget); @Test void foo() { service.execute(); verify(widget).handle( "Joe" ); } } |
Тест проходит. Но должно ли это?
Для чего InjectMocks ?
Для меня аннотация @InjectMocks
предназначена для того, чтобы быть фабричным методом для создания чего-то, что зависит от фиктивных значений, выраженных с помощью @Mock
в тесте. Вот как я обычно это использую, и я также ожидаю, что все объекты в моей экосистеме создаются с использованием инжектора конструктора.
Это хороший принцип проектирования, но это не определение того, что делает инструмент!
Что делает InjectMocks …
Процесс применения этой аннотации просматривает поле, аннотированное @InjectMocks
и @InjectMocks
другой путь, если он null
чем если он уже инициализирован. Будучи настолько чистым в том, что null
путь является декларативным подходом к инжектированию конструктора, я совершенно не думал, что вставлять макеты может означать сделать это для существующего объекта. Документация также не совсем подтверждает это.
- Если объекта нет, то
@InjectMocks
должен создать его- Он использует самый большой конструктор, который он может предоставить
- Если есть объект, он пытается заполнить макеты через сеттеры
- Если нет сеттеров, он пытается взломать макеты, непосредственно устанавливая поля, заставляя их быть доступными по пути
В довершение всего, @InjectMocks
молча терпит неудачу, так что вы можете иметь неудачные тесты, не зная об этом.
В довершение всего, некоторые люди, использующие MockitoAnnotations.initMocks()
в своих тестах, помимо Mockito Runner, вызывают всевозможные странности !!! Серьезно, ребята, никогда не называйте это.
Уроки выучены
Э-э … прости Джим!
Аннотация @InjectMocks
пытается сделать все возможное, но чем сложнее сценарий, тем сложнее его предсказать.
Использование двух сквозных техник для инициализации объекта кажется мне опасным и сложным для понимания подходом, но если он работает, то он может быть лучше альтернатив, если он задокументирован. Добавить комментарий!
Возможно, должен существовать какой-то вид @InjectWithFactory
котором вы можете объявить метод, который получает @Mock
и который @Mock
при создании с объектами @Mock
, чтобы вы могли заполнить любые другие параметры из остального контекста теста.
Или, может быть, мы просто привыкли к этой работе и забываем о том, легко ли это понять.
Последняя мысль
Я узнал, что Mockito делает в вышеописанном, создав тест и отладив библиотеку Mockito, чтобы выяснить, как она достигает результата. Я настоятельно рекомендую исследовать ваши наиболее часто используемые библиотеки таким образом. Вы узнаете что-то полезное!
Смотрите оригинальную статью здесь: Удивительная инъекция Мнения, высказанные участниками Java Code Geeks, являются их собственными. |