通过JMockit调用私有方法来测试结果

时间:2013-03-24 06:18:05

标签: java junit jmockit private-methods

使用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,因为我认为我不想模拟这个类..只是测试调用私有方法的结果。

6 个答案:

答案 0 :(得分:33)

我认为你这太复杂了。您根本不应该使用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);
}

基本上,通过Deencapsulation调用私有方法并测试实际值是否等于预期值。就像你公开的方法一样。没有嘲弄,所以不需要Expectations阻止。

答案 1 :(得分:3)

此时,我不知道是否可以或应该使用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炸掉! :)

答案 2 :(得分:1)

因为在最新的 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。

答案 3 :(得分:0)

从1.35(?)开始,jmockit删除了该辅助方法。由于它不再有用(我不太了解)的原因

但是,可以在其他地方使用此实用程序

org.springframework.test.util.ReflectionTestUtils

答案 4 :(得分:0)

如@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);
}

```

答案 5 :(得分:-1)

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