Как использовать Mockito для подделки защищенного метода?
Im, используя Mockito 1.9.5. Как я издеваюсь над тем, что возвращается из защищенного метода? У меня есть этот защищенный метод...
protected JSONObject myMethod(final String param1, final String param2)
{
…
}
Однако, когда я пытаюсь сделать это в JUnit:
final MyService mymock = Mockito.mock(MyService.class, Mockito.CALLS_REAL_METHODS);
final String pararm1 = "param1";
Mockito.doReturn(myData).when(mymock).myMethod(param1, param2);
В последней строке появляется ошибка компиляции "Метод myMethod не отображается". Как использовать Mockito для подделки защищенных методов? Я готов обновить свою версию, если это ответ.
Ответы
Ответ 1
Это не проблема с Mockito, но с простой старой java. Когда вы вызываете метод, у вас нет видимости. Вот почему это проблема времени компиляции, а не проблема времени выполнения.
Пара вариантов:
- объявить ваш тест в том же пакете, что и класс издевательства
- измените видимость метода, если вы можете
- создать локальный (внутренний) класс, который расширяет насмешливый класс, а затем издевается над этим локальным классом. Поскольку класс будет локальным, у вас будет видимость метода.
Ответ 2
Отвечая на запрос образца кода варианта 3 от ответа Джона Б.:
public class MyClass {
protected String protectedMethod() {
return "Can't touch this";
}
public String publicMethod() {
return protectedMethod();
}
}
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
class MyClassMock extends MyClass {
@Override
public String protectedFunction() {
return "You can see me now!";
}
}
@Mock
MyClassMock myClass = mock(MyClassMock.class);
@Test
public void myClassPublicFunctionTest() {
when(myClass.publicFunction()).thenCallRealMethod();
when(myClass.protectedFunction()).thenReturn("jk!");
}
}
Ответ 3
Джон Б прав, это потому, что метод, который вы пытаетесь протестировать, защищен, это не проблема с Mockito.
Другой вариант, который он перечислил, - использовать отражение, чтобы получить доступ к методу. Это позволит вам избежать изменения метода, который вы тестируете, и не менять шаблон, который вы используете для написания тестов, и где вы храните эти тесты. Я должен был сделать это сам для некоторых тестов, где мне не разрешалось изменять существующую базу кода, которая включала большое количество частных методов, которые необходимо было тестировать в блоке.
Эти ссылки объясняют Reflection и как использовать его очень хорошо, поэтому я буду ссылаться на них, а не копировать:
Ответ 4
WhiteBox.invokeMethod() может быть удобен.
Ответ 5
Вы можете использовать Spring ReflectionTestUtils, чтобы использовать свой класс, как он есть, и без необходимости изменять его только для тестов или переносить его в другой класс.
public class MyService {
protected JSONObject myProtectedMethod(final String param1, final String param2) {
return new JSONObject();
}
public JSONObject myPublicMethod(final String param1) {
return new JSONObject();
}
}
И затем в тесте
@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
@Mock
private MyService myService;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(myService.myPublicMethod(anyString())).thenReturn(mock(JSONObject.class));
when(ReflectionTestUtils.invokeMethod(myService, "myProtectedMethod", anyString(), anyString())).thenReturn(mock(JSONObject.class));
}
}