Вы можете немного приблизиться к желаемым глубинным заглушкам, используя doAnswer(RETURNS_DEEP_STUBS)
, но вы не можете переопределить произвольно глубокие вызовы методов, не позаботившись о том, чтобы заглушить их родительские вызовы. Я бы придерживался ручных одноуровневых насмешек, как вы делаете в своем ответе, или использовал еще меньше насмешек, если это возможно.
Поведение шпиона по умолчанию заключается в делегировании вызова его реального метода, который обычно возвращает реальный объект (например, ваше имя), а не шпиона Mockito. Это означает, что вы обычно не сможете изменить поведение этих объектов с помощью Mockito: шпион на самом деле не тот же класс, что и объект, за которым ведется слежка, а скорее является сгенерированным подклассом, в котором каждое значение поля копируется из шпионского -по стоимости. (Копирование — важная функция, потому что делегирующий шпион будет вести себя очень неинтуитивно в отношении this
, в том числе для вызовов методов и значений полей.)
Foo foo = new Foo();
foo.intValue = 42;
foo.someObject= new SomeObject();
Foo fooSpy = Mockito.spy(foo);
// Now fooSpy.intValue is 42, fooSpy.someObject refers to the exact same
// SomeObject instance, and all of fooSpy's non-final methods are overridden to
// delegate to Mockito's behavior. Importantly, SomeObject is not a spy, and
// Mockito cannot override its behavior!
Так что это не сработает:
doReturn("Neil").when(person).getName().getFirstName();
// Mockito thinks this call ^^^^^^^^^ should return "Neil".
И это тоже не будет:
doReturn("Neil").when(person.getName()).getFirstName();
// The object here ^^^^^^^^^^^^^^^^ won't be a mock, and even if Mockito
// could automatically make it a mock, it's not clear whether that
// should be the same spy instance every time or a new one every time.
В вашей ситуации я бы выбрал следующее, в порядке от наиболее предпочтительного к наименее:
Создайте объект Real Name и установите его с помощью doReturn
. В конце концов, похоже, что Name — это объект данных (также известный как объект-значение), что, вероятно, означает, что у него нет зависимостей, стабильное поведение и сложные для имитации переходы состояний. Вы можете ничего не получить, насмехаясь над ним.
Создайте фиктивное имя и установите его как в своем ответе. Это особенно полезно, если Name сложнее, чем кажется, или если оно вообще не существует.
Замените getName, чтобы вернуть глубокую заглушку...
doAnswer(RETURNS_DEEP_STUBS).when(person).getName();
... который вы затем можете переопределить...
doReturn("Neil").when(person.getName()).getFirstName();
...даже для произвольно глубоких значений.
doReturn("Gaelic").when(person.getName()
.getEtymology()
.getFirstNameEtymology())
.getOrigin();
В качестве заключительной редакционной статьи скажу, что одна из опасностей частичной имитации заключается в том, что из-за нее очень сложно определить, какое поведение является реальным, а какое подделкой; из-за этого вам может быть сложно гарантировать, что поведение, которое вы тестируете, является продукцией, а не фиктивным поведением. Еще одна опасность глубокой заглушки заключается в том, что вы можете нарушить Закон Деметры по определению. Если вы обнаружите, что часто используете этот метод в тестах, возможно, пришло время подумать о перестройке тестируемой системы.
person
Jeff Bowman
schedule
18.12.2017