我是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 上提供的最新稳定版本;谷歌搜索并没有告诉我太多关于 mockito 在 Java 8 中这个新功能的状态......
你能让它以其他方式工作,而不是实现接口和spy()
(这有效)?
在 mockito 2.x 中,支持 JDK 8 默认方法。
使用mockito 1.x是不可能的,
<小时 />旧答案
不幸的是,这还不可能(mockito 1.10.19),从github页面上的README.md
JDK8 状态
Mockito应该可以很好地与JDK8一起使用,如果你远离默认方法(又名防御方法)。Lambda 用法可能同样适用于答案。我们目前不确定JDK8的每个功能,比如序列化使用lambda的模拟。欢迎错误报告和拉取请求(贡献指南)。
编辑1:防御方法和默认方法是同一事物的不同名称。
我希望有一个模拟者替代品,可以正确处理java 8操作码,因为某些操作码在Java 8中具有不同的语义。
编辑2:更新了模拟自述文件,并相应地引用了此报价
我刚刚尝试了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);
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-
您可以通过实现接口(在 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 { }
}
}
我刚刚遇到了同样的问题 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 的解决方案进行比较)。这样做可以让您远离"实现"接口所需的所有方法的麻烦。
女工程师:
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());
}
}
另一种解决方案是使用如下所述的Spy
:https://stackoverflow.com/a/54915791/3636822
只是一点旁注,特别是功能接口可以很容易地创建,而无需模拟。如果之后需要调用一些验证方法,可以将实例包装到间谍中。
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;
}
}
}
有时我们习惯于使用这些框架,以至于我们很容易忘记明显的解决方案。