Можете ли вы сделать mockito (1.10.17) работать со стандартными методами в интерфейсах?
Я большой поклонник mockito, к сожалению, для одного из моих проектов, который использует Java 8, он терпит неудачу на мне...
Сценарий:
public final class MockTest
{
@Test
public void testDefaultMethodsWithMocks()
{
final Foo foo = mock(Foo.class);
//when(foo.bar()).thenCallRealMethod();
assertThat(foo.bar()).isEqualTo(42);
}
@FunctionalInterface
private interface Foo
{
int foo();
default int bar()
{
return 42;
}
}
}
К сожалению, сбой теста и foo.bar()
возвращает 0.
Когда я раскомментирую строку when()
, я получаю трассировку стека...
java.lang.NoSuchMethodError: java.lang.Object.bar()I
at com.github.fge.lambdas.MockTest.testDefaultMethodsWithMocks(MockTest.java:18)
Это последняя стабильная версия, доступная на maven; googling around не рассказывал мне многое о статусе mockito в отношении этой новой функциональности в Java 8...
Можете ли вы заставить его работать каким-то другим способом, чем реализовать интерфейсы и spy()
на них (это работает)?
Ответы
Ответ 1
С помощью mockito 2.x методы по умолчанию JDK 8 поддерживаются .
С mockito 1.x он невозможен,
Старый ответ
К сожалению, пока это невозможно (mockito 1.10.19), из README.md
на github'page
Статус JDK8
Mockito должен отлично работать с JDK8 , если вы держитесь подальше от методов по умолчанию (так называемых методов защиты). Использование Lambda может работать так же хорошо для ответов. На данный момент мы не уверены в функциях JDK8, например, для сериализации макета, использующего лямбда. Отчет об ошибках и запрос на тягу приветствуются, хотя (справочник).
EDIT 1: методы защитника и методы по умолчанию - это разные имена для одной и той же вещи.
Я надеюсь на замену mockmaker, которая будет обрабатывать коды java 8 правильно для таких случаев, когда некоторые коды операций имеют другую семантику в Java 8.
РЕДАКТИРОВАТЬ 2: обновлено mockito readme, и эта цитата соответственно
Ответ 2
Я просто попробовал Mockito 2.0.38-бета, и он уже работает в этой версии. Но Mockito должно быть явно сказано, чтобы вызвать реализацию по умолчанию.
Foo foo = mock(Foo.class);
assertThat(foo.bar()).isEqualTo(0);
when(foo.bar()).thenCallRealMethod();
assertThat(foo.bar()).isEqualTo(42);
Ответ 3
Вы можете обойти это ограничение, реализовав интерфейс (протестированный в Mockito 1.10.19):
public class TestClass {
@Mock ImplementsIntWithDefaultMethods someObject;
@Test public void test() throws Exception {
// calling default method on mocked subtype works
someObject.callDefaultMethod();
}
/* Type that implements the interface */
static class ImplementsIntWithDefaultMethods implements IntWithDefaultMethod { }
/* Interface you need mocked */
interface IntWithDefaultMethod {
default void callDefaultMethod { }
}
}
Ответ 4
Я просто столкнулся с той же проблемой с Mockito (org.mockito: mockito-core: 1.10.19). Проблема в том, что я не могу изменить версию Mockito (2.7.22 будет работать) из-за зависимостей с org.springframework.boot: spring -boot-starter-test: 1.4.3.RELEASE, который мы используем (Spring, проблема Mockito).
Самое легкое решение, которое я нашел, - это реализовать интерфейс с частным абстрактным классом в моем тестовом классе и насмехаться над этим (также сравнить с решением @Mihai Bojin). Выполнение этого так же не позволяет вам "реализовать" все методы, необходимые для интерфейса (ов).
MWE:
public interface InterfaceWithDefaults implements SomeOtherInterface {
default int someConstantWithoutSense() {
return 11;
}
}
public class SomeTest {
private abstract class Dummy implements InterfaceWithDefaults {}
@Test
public void testConstant() {
InterfaceWithDefaults iwd = Mockito.mock(Dummy.class);
Assert.assertEquals(11, iwd.someConstantWithoutSense());
}
}