Аннотация @InjectMocks
дает нам возможность заглушить / закрепить частного участника и повторно использовать тестовый пример.Вот концептуальный код, в котором проблема возникла, когда мы подделали Фальшивого Участника.
public class TestBuilder{
@Spy
private StubComponent componentA = new StubComponent();
@Mock
private FakeComponent componentB;
@InjectMocks
private class TestTarget targetInstance = mock(TestTarget.class);
public static Class TestTarget{
private StubComponent componentA;
private FakeComponent componentB;
public ShimmedResultB testInvokation(String para){
componentA.doCallRealMethod();
ShimmedResultA shimmedResultA = componentA.someUnableToStubbedMethod(para);
ShimmedResultB shouldNotBeNull = componentB.someShimmedMethod(shimmedResultA);
return shouldNotBeNull;
}
}
private TestBuilder(){
MockitoAnnotations.initMocks(this);
//Shim the real component A with partial stubbed
doReturn(shimmedResultA).when(componentA).someUnableToStubbedMethod(any());
//Shim the fake component B
//************The issue is here****************
componentB = mock(FakeComponent.class);
//*********************************************
when(componentB.someShimmedMethod(any())).thenReturn(shimmedResultB);
}
public TestTarget getTargetInstance(){
return this.targetInstance;
}
public static TestTarget build(){
return (new TestBuilder()).getTargetInstance();
}
public static main(String[] args){
TestTarget testInstance = TestBuilder.build();
ShimmedResultB result = testInstance.testInvokation("");
assertThat(result, not(equalTo(null)));
}
}
Проблема в том, что мы высмеиваем Фальшивого componentB
.Тогда someShimmedMethod
вернет ноль.Похоже, что InjectMocks
не может отнести mock()
к приватному члену.
Вот несколько определений терминологии:
StubComponent: тест проникнет в этот компоненткак частный участник.Тем не менее, есть какой-то метод, который может не пройти.Мы могли бы воспользоваться его публичным методом.Этот компонент может иметь меньшую область зависимостей, которые легко могут быть инициированы локальным ресурсом.
FakeComponent: Этот компонент будет тестироваться где-то еще.Здесь мы можем построить только макетированный экземпляр и использовать все методы для цели теста.
Заглушка: @Spy может помочь нам перехватить член-заглушку.Частный член не на 100% реален.Но некоторая часть с заглушкой может позволить тесту проникнуть в этот закрытый элемент.
Shim: @Mock выдаст нам нулевой указатель до initMocks.Таким образом, мы можем начать проектировать возврат компонента Fake после initMocks.Это магия @InjectMocks.Тем не менее, это самая сложная часть, так как разработчик хотел бы инициировать каждую вещь и макет (FakeComponent.class) для componentB интуитивно.Это очистит весь отмытый дизайн и сделает ваше утверждение неудачным.
========================================================================
Спасибо Maciej'sОтветьте и извините за опечатку, когда я переведу структуру моего теста.И позвольте мне поднять проблему с более ясным описанием к ответу Мачей.
public class TestBuilder{
@Spy
private StubComponent componentA = new StubComponent();
@Mock
private FakeComponent componentB;
@InjectMocks
private TestTarget targetInstance = mock(TestTarget.class);
public static Class TestTarget{
private StubComponent componentA;
private FakeComponent componentB;
public ShimmedResultB testInvokation(String para){
componentA.doCallRealMethod();
ShimmedResultA shimmedResultA = componentA.someUnableToStubbedMethod(para);
ShimmedResultB shouldNotBeNull = componentB.someShimmedMethod(shimmedResultA);
return shouldNotBeNull;
}
public TestTarget(){
//The FakeComponent has some specific remote resource
//And could not be initialized here
componentB = new FakeComponent();
//We will use mock server to test this FakeComponent else where
}
}
private TestBuilder(){
//Hook the testing Function for trigger the step in
doCallRealMethod().when(this.targetInstance).testInvokation(anyString());
//Inject Stubbed and Faked Private Member for testing
MockitoAnnotations.initMocks(this);
//Shim the real component A with partial stubbed
doReturn(shimmedResultA).when(componentA).someUnableToStubbedMethod(any());
//************The issue is here****************
componentB = mock(FakeComponent.class);
//*********************************************
//Shim the leveraged method of fake componentB
when(componentB.someShimmedMethod(any())).thenReturn(shimmedResultB);
}
public TestTarget getTargetInstance(){
return this.targetInstance;
}
public static TestTarget build(){
return (new TestBuilder()).getTargetInstance();
}
public static main(String[] args){
TestTarget testInstance = TestBuilder.build();
//The doRealCall hook will trigger the testing
ShimmedResultB result = testInstance.testInvokation("");
assertThat(result, not(equalTo(null)));
}
}
Во второй концептуальный код добавлено что-то:
Компонент B - это область, которую мыне хочу вмешиваться. Однако TestTarget имеет инициацию с componentB в своем конструкторе.Это довольно часто, когда у нас есть утилита, связанная с удаленным источником.Мы используем для тестирования componentB независимо с имитацией сервера или другой техники.Следовательно, мы можем использовать только mock (TestTarget.class).
Так как мы высмеивали TestTarget.Я пропустил одну вещь: нам нужно использовать doCallRealMethod (). When (targetInstance) для запуска testInvokation ().И это ограничивает нулевое объявление targetInstance.Нам нужно использовать mock () и перехватить doCallRealMethod.
Таким образом, в результате нам нужно оставить @Mock равным null без mock (), чтобы @InjectMocks мог обрабатывать shim.Мы обнаружили, что это сложно, когда мы используем @ InjectMocks.