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

5us2dqdw  于 2022-11-08  发布在  其他
关注(0)|答案(7)|浏览(153)

我是mockito的忠实粉丝,不幸的是对于使用Java 8的one of my projects来说,它在我身上失败了...
场景:

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上可用的最新稳定版本;在Google上搜索并没有告诉我很多关于Java 8中这个新功能的mockito状态...
除了实现接口和spy()之外,您还能用其他方式使它工作吗?

kq0g1dla

kq0g1dla1#

使用mockito 2.x支持JDK 8默认方法。
对于mockito 1.x,这是不可能的
"旧答案"
不幸的是,这还不可能(mockito 1.10.19),来自github'页面上的README.md
JDK8状态
如果你不使用默认方法(也叫defender方法),Mockito在JDK8中应该可以很好地工作。Lambda的使用可能也同样适用于Answers。我们目前还不确定JDK8的每一个特性,比如序列化一个使用lambda的mock。但是欢迎错误报告和拉取请求(贡献指南)。

EDIT 1:*defender方法 * 和 *default方法 * 是同一个对象的不同名称。

我希望有一个mockmaker的替代品,它可以正确地处理java8操作码,因为在java8中某些操作码具有不同的语义。

编辑2:更新了mockito自述文件,并相应地更新了此报价

uubf1zoe

uubf1zoe2#

我刚刚试用了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);
iswrvxsc

iswrvxsc3#

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;
        }
    }
}

调用real方法的应答在最新的Mockito中确实有效。一般来说,扩展使mock的创建更具声明性,但是mock方法也提供了一个重载,它支持默认应答,如上面的@Mock注解所示:https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mock-java.lang.Class-org.mockito.stubbing.Answer-

wqlqzqxt

wqlqzqxt4#

您可以通过实现接口(在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 { }
  }
}
mv1qrgav

mv1qrgav5#

我刚刚在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的解决方案比较)。这样做可以让你远离“实现”接口所需的所有方法的麻烦。
我的天

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());
    }
}
ncecgwcz

ncecgwcz6#

另一种解决方案是使用Spy,如下所述:https://stackoverflow.com/a/54915791/3636822

2g32fytz

2g32fytz7#

只是一个小小的侧记,特别是函数接口可以很容易地创建,而不需要模仿。如果你需要调用一些验证方法之后,你可以 Package 示例到一个间谍。

public final class MockTest
{
    @Test
    public void testDefaultMethodsWithMocks()
    {
        final Foo foo = () -> 0;

        assertThat(foo.bar()).isEqualTo(42);
    }

    @FunctionalInterface
    private interface Foo
    {
        int foo();

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

有时候,我们太习惯于使用这些框架,以至于很容易忘记那些显而易见的解决方案。

相关问题