【问题标题】:Mocking CGLIB enhanced objects模拟 CGLIB 增强对象
【发布时间】:2013-11-01 19:39:12
【问题描述】:

mockito 真的不能 mock 已经被 CGLIB 增强的对象吗?

public class Article {

     @Autowired
     private dbRequestHandler

     @Autowired
     private filesystemRequestHandler

     @Transactional
     public ArticleDTO getArticleContents() {

         //extractText() and then save the data in DTO
         //extractImages() and then save the data in DTO
         // some other calls to other databases to save data in dto

       return articleDTO;

     }
     public void extractText() {

        //call to DB

   }

   public void extractImages() {

        // call to file system

   }
}


public class IntegrationTest {

  @Autowired
  private Article article;

  //setup method {

  articleMock = Mockito.spy(article);

  doNothing().when(articleMock).extractImages();
 }
}

在上面的例子中,当涉及到doNothing().when(articleMock).extractImages(); 时,它实际上调用了真正的函数。仔细看看文章 Mock 得到了两次增强。 autowiring 的一个原因和spying 的第二个原因。

如果我无法监视增强的对象,那么如何在我的集成测试中测试 getArticle() 方法,以便我可以验证是否返回了正确的 DTO。

注意:我实际上不想测试执行文件系统调用的方法。只是数据库。这就是为什么我需要测试getArticle 方法。

【问题讨论】:

  • 按照我对documentation 的发现,我没有模仿地看到问题所在。您是否尝试过自己创建Article,但没有自动装配(或至少在自动装配后验证正确性)?
  • 是的,如果我自己创建Article,我就可以进行间谍活动。但我必须自动装配它,因为在我的应用程序中,每个对象都是通过自动装配创建的,如果我自己启动Article,那么 Article 类中的字段为空(例如 reqHandler 对象)。如果我也启动这些字段,那么这些类中的字段为空,并且链继续..
  • 过滤后的代码和您的问题不适合 - 有问题的 getArticle(),代码中的 getArticleContents() - 这会造成一些混乱。您应该考虑提供更多IntegrationTest 代码。
  • 实际上,我们需要更多的IntegrationTest 代码才能了解发生了什么。

标签: java mocking integration-testing mockito cglib


【解决方案1】:

如果我理解正确,您的课程是由 Spring 连接的。 Spring 仅在没有接口时才使用 CGLIB 来确保事务行为,该接口由您的对象实现。如果有接口,它使用简单的 JDK 动态代理。 (见http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html

也许你可以尝试提取一个接口,让 Spring 使用动态代理。也许那时 Mockito 会表现得更好。

【讨论】:

    【解决方案2】:

    如果您作为真正的单元测试而不是作为集成测试运行,则无需在具有 Spring 自动装配功能的容器中运行。在您的一个 cmets 中,我认为您暗示要尝试此操作,并且您注意到您还必须提供无穷无尽的链式对象引用集。但是有办法解决这个问题。 Mockito 提供了一些预定义的Answer 类,您可以使用它们来初始化您的模拟。你可能想看看RETURNS_DEEP_STUBS,它可能会解决这个问题。

    【讨论】:

      【解决方案3】:

      请您使用现成的可编译代码更新您的问题。以下是一些代码审查建议:

      此问题代码的问题:

      • Article.java 缺少导入:org.springframework.beans.factory.annotation.Autowired
      • Article.java 缺少导入:org.springframework.transaction.annotation.Transactional
      • Article.java 属性语法问题:dbRequestHandler
      • Article.java 属性语法问题:filesystemRequestHandler
      • Article.java 方法没有初始化返回语句:articleDTO

      以下是您在问题解决上述问题的代码时可能应该使用的内容:

      Article.java

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.transaction.annotation.Transactional;
      
      public class Article {
      
          @Autowired
          private Object dbRequestHandler;
      
          @Autowired
          private Object filesystemRequestHandler;
      
          @Transactional
          public ArticleDTO getArticleContents() {
      
              // extractText() and then save the data in DTO
              // extractImages() and then save the data in DTO
              // some other calls to other databases to save data in dto
      
              ArticleDTO articleDTO = null;
              return articleDTO;
      
          }
      
          public void extractText() {
      
              // call to DB
      
          }
      
          public void extractImages() {
      
              // call to file system
      
          }
      }
      

      IntegrationTest.java 对于 testClass 来说是个糟糕的名字,因为它是通用的。我建议将 ArticleTest 用于 Java 单元测试。

      ArticleTest.java

      import org.junit.Assert;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.mockito.InjectMocks;
      import org.mockito.Mock;
      import org.mockito.Mockito;
      import org.powermock.core.classloader.annotations.PrepareForTest;
      import org.powermock.modules.junit4.PowerMockRunner;
      import org.springframework.beans.factory.annotation.Autowired;
      
      @RunWith(PowerMockRunner.class)
      @PrepareForTest(ClassWithPrivate.class)
      public class ArticleTest {
      
          @InjectMocks
          private Article cut;
      
          @Mock
          private Object dbRequestHandler;
      
          @Mock
          private Object filesystemRequestHandler;
      
          @Test
          public void testeExtractImages() {
      
              /* Initialization */
              Article articleMock = Mockito.spy(cut);
      
              /* Mock Setup */
              Mockito.doNothing().when(articleMock).extractImages();
      
              /* Test Method */
              ArticleDTO result = cut.getArticleContents();
      
              /* Asserts */
              Assert.assertNull(result);
      
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        您可以使用AdditionalAnswers.delegatesTo 方法。在下面的示例中,secondProxyDoingMocking 声明创建了类似于 spy 的东西(与 spy() 方法的实现相比),但它使用“轻量级”方法委托。

        import org.mockito.AdditionalAnswers;
        
        public class ArticleTest {
        
            @Autowired
            private Article firstProxyDoingAutowiring;
        
            @Test
            public void testExtractImages() {
                Article secondProxyDoingMocking = Mockito.mock(Article.class,
                        Mockito.withSettings().defaultAnswer(
                                AdditionalAnswers.delegatesTo(firstProxyDoingAutowiring)
                        )
                );
                Mockito.doNothing().when(secondProxyDoingMocking).extractImages();
                ...
            }
        
        }
        

        我没有测试这个例子,但是我是从我的工作代码中组装出来的。我的用例类似:为给定方法返回常量值,为 Spring @Transactional-annotated bean 的所有剩余方法调用真实方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-04-06
          相关资源
          最近更新 更多