【问题标题】:Can you make mockito (1.10.17) work with default methods in interfaces?你能让 mockito (1.10.17) 在接口中使用默认方法吗?
【发布时间】:2014-12-27 00:15:25
【问题描述】:

我是 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 上可用的最新稳定版本;谷歌搜索并没有告诉我关于 Java 8 中这个新功能的 mockito 状态...

除了在它们上实现接口和spy() 之外,你能否让它以其他方式工作(这可行)?

【问题讨论】:

  • 我怀疑这是 Mockito 处理模拟的动态代理生成方式的影响,并且需要更新基础架构。您是否检查过 Mockito 是否存在未解决的问题?
  • @chrylis 不,不是特别针对这一点;有一个与 Java 8 和默认方法相关的问题被打开,问题开启者正确地告诉(我一开始没想到这一点很愚蠢)他必须用 Java 8 编译 mockito 才能使测试工作。看起来很阴沉:/
  • 是的。仍然有一些库维护 1.4 的分支。

标签: java mockito java-8 default-method


【解决方案1】:

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

mockito 1.x不可能


旧答案

不幸的是,这还不可能(mockito 1.10.19),来自 github'page 上的 README.md

JDK8 状态

Mockito 应该可以在 JDK8 上正常工作如果您远离默认方法(也称为防御方法)。 Lambda 的使用可能与 Answers 一样好。目前我们不确定 JDK8 的每个特性,比如序列化使用 lambda 的模拟。不过欢迎提交错误报告和拉取请求(贡献指南)。

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

我希望有一个 mockmaker 替代品能够正确处理 Java 8 操作码,因为某些操作码在 Java 8 中具有不同的语义。

编辑 2:更新了 mockito 自述文件和相应的引用

【讨论】:

  • 嗯,是的,我看到了,但我想知道“防御者方法”是否实际上意味着接口中的默认方法;什么是“防御者方法”?
  • 好吧,好吧,我想我会提交一个更新自述文件的拉取请求......我相信很多人都不知道这两个是同义词
  • 我现在就更新它 :) 顺便说一下,它在某些方面是不完整的,但你可能想试试这个构建:github.com/bric3/mockito/tree/bytebuddy-mockmaker(你必须自己构建它)
  • 好的,我会试试看。再次感谢!
  • 来自 1.10.0 版本的发布说明(2014-09-25 22:25 UTC):“允许调用 jdk8 扩展方法的真正实现(#39)”。 “扩展方法”不是一回事吗? github.com/mockito/mockito/blob/master/doc/release-notes/…
【解决方案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);

【讨论】:

    【解决方案3】:

    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-

    【讨论】:

      【解决方案4】:

      您可以通过实现接口来绕过这个限制(在 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 { }
        }
      }
      

      【讨论】:

      • 我成功地将此提示与@Jan X Marek 的提示结合使用,以使 Mockito 1.10.x 在模拟上具有默认方法。
      【解决方案5】:

      我刚刚遇到了与 Mockito (org.mockito:mockito-core:1.10.19) 相同的问题。问题是:由于依赖于我们正在使用的 org.springframework.boot:spring-boot-starter-test:1.4.3.RELEASE,我无法更改 Mockito 版本(2.7.22 可以使用)(@987654321 @)。

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-16
          • 2019-01-28
          • 2010-12-30
          相关资源
          最近更新 更多