你能用mockito(1.10.17)在接口中使用默认方法吗?

时间:2014-12-27 00:15:26

标签: java mockito java-8 default-method

我是mockito的忠实粉丝,不幸的是one of my projects使用了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上最新的稳定版本;谷歌周围没有告诉我关于Java 8中这个新功能的模拟状态...

除了实现接口和spy()之外,它能否以其他方式工作(这有效)?

6 个答案:

答案 0 :(得分:11)

使用 mockito 2.x ,JDK 8默认方法支持

使用 mockito 1.x 不可能


旧答案

不幸的是,github'页面上的README.md尚不可能(mockito 1.10.19)

  

JDK8状态

     

如果您远离默认方法(也就是后卫方法),Mockito应该可以正常使用JDK8 。 Lambda的使用可能对Answers有效。我们目前不确定每个JDK8的功能,比如序列化使用lambda的模拟。我们欢迎错误报告和拉取请求(贡献指南)。

编辑1 后卫方法默认方法是同一事物的不同名称。

我希望有一个模拟器替代品可以正确处理java 8操作码,因为某些操作码在Java 8中有不同的语义。

编辑2 :更新了mockito自述文件,并相应地引用了此引号

答案 1 :(得分:5)

我刚试过Mockito 2.0.38-beta,它已经在该版本中运行了。但必须明确告诉Mockito调用默认实现。

Foo foo = mock(Foo.class);
assertThat(foo.bar()).isEqualTo(0);

when(foo.bar()).thenCallRealMethod();
assertThat(foo.bar()).isEqualTo(42);

答案 2 :(得分:2)

您可以通过实施界面(在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 { }
  }
}

答案 3 :(得分:1)

我刚与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 issue)。

我找到的最简单的解决方案是在我的测试类中实现具有私有抽象类的接口并模拟该接口(也与@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());
    }
}

答案 4 :(得分:0)

另一种解决方案是使用https://stackoverflow.com/a/54915791/3636822

,如此处所述:

答案 5 :(得分:0)

Mockito(在版本3上有效)可以使用default方法。这是用JUnit 5重写的示例代码,因此它可以正常工作:

@ExtendWith(MockitoExtension.class)
class MockTest {
    @Mock(answer = Answers.CALLS_REAL_METHODS)
    private Foo foo;

    @Test
    void testDefaultMethodsWithMocks() {
        assertThat(foo.bar()).isEqualTo(42);
    }

    @FunctionalInterface
    private interface Foo {
        int foo();

        default int bar() {
            return 42;
        }
    }
}

真正的方法调用确实在最新的Mockito中起作用。通常,扩展使模拟的创建更具声明性,但是mock方法还提供了一个重载,该重载支持默认答案,如上面的@Mock批注所示:https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mock-java.lang.Class-org.mockito.stubbing.Answer- < / p>