【问题标题】:Invoking a private method via JMockit to test result通过 JMockit 调用私有方法来测试结果
【发布时间】:2013-03-13 19:26:49
【问题描述】:

我正在使用 JMockit 1.1,我想要做的就是调用一个私有方法并测试返回值。但是,我无法从 JMockit De-Encapsulation 示例中准确理解如何执行此操作。

我要测试的方法是这个类中的私有方法:

public class StringToTransaction {
   private List<String> parseTransactionString(final String input) {
      // .. processing
      return resultList;
   }
}

我的测试代码如下。

@Test
public void testParsingForCommas() {
   final StringToTransaction tested = new StringToTransaction();
   final List<String> expected = new ArrayList<String>();
   // Add expected strings list here..
   new Expectations() {
      {
         invoke(tested, "parseTransactionString", "blah blah");
         returns(expected);
      }
   };
}

我得到的错误是:

java.lang.IllegalStateException:缺少对模拟类型的调用 这点;请确保此类调用仅在 声明一个合适的模拟字段或参数

也许我在这里误解了整个API,因为我不认为我想模拟类..只是测试调用私有方法的结果。

【问题讨论】:

    标签: java junit jmockit private-methods


    【解决方案1】:

    我认为你把这件事弄得太复杂了。您根本不应该使用 Expectations 块。您需要做的就是这样:

    @Test
    public void testParsingForCommas() {
       StringToTransaction tested = new StringToTransaction();
       List<String> expected = new ArrayList<String>();
       // Add expected strings list here..
    
       List<String> actual = Deencapsulation.invoke(tested, "parseTransactionString", "blah blah");
       assertEquals(expected, actual);
    }
    

    基本上,通过解封装调用私有方法并测试实际是否等于预期。就像方法是公开的一样。没有进行任何模拟,因此不需要 Expectations 块。

    【讨论】:

    • 嗨@JeffOlson - 在哪个类上调用()方法?在 Expectations 上,invoke() 是一个受保护的实例方法。或者也许只是使用 java.lang.reflect.Method.invoke() 并完全忽略 JMockit ..
    • 我终于明白了...是Deencapsulation.invoke,感谢@JeffOlson 和groups.google.com/d/msg/jmockit-users/oEgjW0DfmgU/3pEE1mjt1ncJ 的Rogerio。
    • 是的,没错。抱歉没有更清楚,但您在最初的示例中直接使用了invoke()(可能是通过静态导入),所以我想我会保持原样。我会更新我的答案,以便将来可能会阅读此内容的其他人更清楚。
    • @JeffOlson 在最新的 jmockit 中,他们已将其删除。那么有没有其他方法可以实现相同的行为?此答案需要更新。
    【解决方案2】:

    在这一点上,我不知道 JMockit 是否可以或应该用于此。测试我的私有方法可以用普通的反射来完成,尽管我开始这个练习是为了了解 JMockit(并测试我的代码)。如果 JMockit 不能用于此,我可以使用反射来代替。

    @Test
    public void testParsingForCommas() throws Exception {
       StringToTransaction tested = new StringToTransaction();
       ArrayList<String> expected = new ArrayList<>();
       expected.add("Test");
    
       Method declaredMethod =
             tested.getClass().getDeclaredMethod("parseTransactionString",
                   String.class);
       declaredMethod.setAccessible(true);
       Object actual = declaredMethod.invoke(tested, "blah blah");
       assertEquals(expected, actual);
    }
    

    在这里调用setAccessible(true) 很重要,否则调用私有方法时invoke 会爆炸。

    declaredMethod.setAccessible(true);
    

    但是你想知道什么是真正的酷吗?如果你不打电话给setAccessible(true),它会被java.lang.StackOverflowError炸毁! :)

    【讨论】:

      【解决方案3】:

      由于最新的 Jmockit 中不允许模拟私有方法。可以将私有方法中使用的 API 模拟为一种解决方法,而不是模拟私有方法。

      也可以将这种变通方法视为最终解决方案。

      示例:
      实际班级:

      class A {
      
        private int getId(String name){  //private method
            return DAOManager.getDao().getId(name);  //Call to non-private method can be mocked.
        }
      }  
      

      测试类:

      public class ATest{
      
        @Before
        public void setUp(){
          new MockDAOManager();
        }
      
        //Mock APIs used by the private method `getId`.
        public static class MockDAOManager extends MockUp<MockDAOManager>{
           static mocked_user_id = 101;
      
           @Mock
           public DAOManager getDao() throws Exception{
                return new DAOManager();
           }
      
           @Mock
           public Integer getId(String name){
               return mocked_user_id;
           }
        }
      }
      

      注意:

      • 如果你没有这样的逻辑(私有方法调用另一个非私有 方法) 那么你可能不得不重构你的代码,否则这将 不工作。
      • 这里的DAOManager.getDao().getId(name) 不是私有API。
      • 可能需要模拟该私有方法使用的所有 API。

      【讨论】:

        【解决方案4】:

        从 1.35(?) 开始,jmockit 删除了该辅助方法。因为它不再有用(我不太明白)

        是的,这个实用程序在其他地方可用

        org.springframework.test.util.ReflectionTestUtils
        

        【讨论】:

          【解决方案5】:

          正如@Jeff Olson 所说,您还可以通过声明@Tested 来调用bean 的私有方法。

          这是一个例子:

          // Java
          
              @Tested
              private YourServiceImplClass serviceImpl;
          
              @Test
              public void testPrivateMethod() {
             
                    List<String> expected = new ArrayList<String>();
                    // Add expected strings list here..
          
                    List<String> actual = Deencapsulation.invoke(serviceImpl, "yourPrivateMethod", "arguments");
                    assertEquals(expected, actual);
              }
          

          【讨论】:

            【解决方案6】:

            为什么要直接测试私有方法?大多数时候 API 方法,即公共接口方法是单元测试的,因为 私有方法将被间接测试以及它们。您可以将 带有来自私有方法的预期值的断言语句放在您在公共方法中调用它们的位置。因此,如果断言失败,您可以确定私有方法存在一些问题。所以不需要单独测试。

            【讨论】:

            • 嗨@Ankur,我很抱歉,但我正在寻找一个答案,涉及如何在这种情况下正确使用JMockit(或者我是否可以)。我不想讨论为什么我应该或不应该测试私有方法。我知道通过公共方法使用断言是另一种方法,但这不是我要在这里解决的问题。
            • 我可能有点密集@Ankur,但我无法弄清楚如何将该解决方案应用于我的问题。 :) 将 Expectations 块放在初始化 tested 之前会导致编译错误:tested 无法解析为变量
            • 我在网上寻找一些解决方案。我找到了一个很好的链接。请通过它。 rockycode.com/blog/easymock-cause-effect-exception-mapping
            • 我有时确实发现测试私有方法而不是通过公共方法依赖测试覆盖率是最有意义的。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-07-14
            • 1970-01-01
            • 2014-08-17
            • 1970-01-01
            • 2018-06-20
            相关资源
            最近更新 更多