Можете ли вы сделать 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());
    }
}