首页 文章

Mockito:试图窥探方法是调用原始方法

提问于
浏览
245

我正在使用Mockito 1.9.0 . 我想在JUnit测试中模拟一个类的单个方法的行为,所以我有

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

问题是,在第二行, myClassSpy.method1() 实际上被调用,导致异常 . 我使用模拟的唯一原因是,以后,每当调用 myClassSpy.method1() 时,将不会调用真实方法并返回 myResults 对象 .

MyClass 是一个接口, myInstance 是一个实现,如果这很重要 .

我需要做些什么来纠正这种 Spy 行为?

7 回答

  • 23

    我发现了 Spy 调用原始方法的另一个原因 .

    有人有想法嘲笑一个 final 类,并发现 MockMaker

    由于这与我们当前的机制不同,而且这个机制有不同的限制,因为我们想要收集经验和用户反馈,所以必须明确激活此功能才能使用;它可以通过mockito扩展机制通过创建包含单行的文件src / test / resources / mockito-extensions / org.mockito.plugins.MockMaker来完成:mock-maker-inline

    资料来源:https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

    合并并将该文件带到我的机器后,我的测试失败了 .

    我只需删除行(或文件), spy() 工作 .

  • 0

    让我引用the official documentation

    Spy 真实物体的重要问题!有时不可能在(对象)时使用咒语 Spy . 例:

    List list = new LinkedList();
    List spy = spy(list);
    
    // Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
    when(spy.get(0)).thenReturn("foo");
    
    // You have to use doReturn() for stubbing
    doReturn("foo").when(spy).get(0);
    

    在你的情况下它是这样的:

    doReturn(resulstIWant).when(myClassSpy).method1();
    
  • 434

    我的案子与接受的答案不同 . 我试图为一个没有存在于该包中的实例模拟一个包私有方法

    package common;
    
    public class Animal {
      void packageProtected();
    }
    
    package instances;
    
    class Dog extends Animal { }
    

    和测试类

    package common;
    
    public abstract class AnimalTest<T extends Animal> {
      @Before
      setup(){
        doNothing().when(getInstance()).packageProtected();
      }
    
      abstract T getInstance();
    }
    
    package instances;
    
    class DogTest extends AnimalTest<Dog> {
      Dog getInstance(){
        return spy(new Dog());
      }
    
      @Test
      public void myTest(){}
    }
    

    编译是正确的,但是当它尝试设置测试时,它会调用实际方法 .

    声明方法受到保护或公开修复问题,这不是一个干净的解决方案 .

  • 0

    可能导致 Spy 问题的另一种可能情况是,当您测试 spring beans (使用 spring 测试框架)或其他一些 framework that is proxing your objects during test 时 .

    @Autowired
    private MonitoringDocumentsRepository repository
    
    void test(){
        repository = Mockito.spy(repository)
        Mockito.doReturn(docs1, docs2)
                .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
    }
    

    在上面的代码中,Spring和Mockito将尝试代理您的MonitoringDocumentsRepository对象,但Spring将是第一个,这将导致findMonitoringDocuments方法的真实调用 . 如果我们在将一个 Spy 放在存储库对象之后调试我们的代码,它将在调试器中看起来像这样:

    repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$
    

    @SpyBean to the rescue

    如果改为 @Autowired 注释我们使用 @SpyBean 注释,我们将解决上面的问题,SpyBean注释也将注入存储库对象,但它将首先由Mockito代理并在调试器中看起来像这样

    repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$
    

    这是代码:

    @SpyBean
    private MonitoringDocumentsRepository repository
    
    void test(){
        Mockito.doReturn(docs1, docs2)
                .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
    }
    
  • 0

    The answer by Tomasz Nurkiewicz appears not to tell the whole story!

    NB Mockito版本:1.10.19 .

    我非常喜欢Mockito newb,所以无法解释以下行为:如果有专家可以改进这个答案,请随意 .

    这里讨论的方法 getContentStringValueNOT finalNOT static .

    这行 does 调用原始方法 getContentStringValue

    doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));
    

    这行 does not 调用原始方法 getContentStringValue

    doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));
    

    由于我无法回答的原因,使用 isA() 会导致 doReturn 的预期(?)"do not call method"行为失败 .

    让我们看一下这里涉及的方法签名:它们都是 Matchersstatic 方法 . Javadoc都说两者都返回了 null ,这本身就有点难以理解 . 据推测,作为参数传递的 Class 对象被检查但结果要么从未计算过,要么被丢弃 . 鉴于 null 可以代表任何类,并且你希望不被调用的模拟方法, isA( ... )any( ... ) 的签名只能返回 null 而不是通用参数* <T>

    无论如何:

    public static <T> T isA(java.lang.Class<T> clazz)
    
    public static <T> T any(java.lang.Class<T> clazz)
    

    API文档没有提供任何关于此的线索 . 它似乎也表示需要这样的"do not call method"行为是"very rare" . 我个人使用这种技术 all the time :通常我发现嘲讽涉及几行"set the scene" ...然后调用一个方法,然后在你上演的模拟上下文中的场景......当你设置风景时道具你想要的最后一件事就是让演员们进入舞台并开始表现他们的心灵......

    但这超出了我的工资等级...我邀请任何通过Mockito高级牧师的解释......

    *是“通用参数”的正确用语吗?

  • 14

    在我的例子中,使用Mockito 2.0,我必须将所有any()参数更改为nullable()以便存根真实的调用 .

  • 12

    确保未调用类中的方法的一种方法是使用哑元覆盖该方法 .

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
                @Override
                public void select(TreeItem i) {
                    log.debug("SELECT");
                };
            });
    

相关问题