JMockit无法模拟JRE的具体集合

时间:2015-01-26 16:18:00

标签: java unit-testing jmockit

我有需要模拟HashMap类的测试用例,但看起来JMockit在模拟时遇到了困难。 以下代码在Expectations块中触发NPE:

public class TestKO {

public class ClassUnderTest {

    private final Map<String, String> dependency;

    public ClassUnderTest() {
        this.dependency = new HashMap<String, String>();
    }

    public void register(final String key, final String value) {
        dependency.put(key, value);
    }
}

@Test
public void ko(@Mocked("put") final HashMap<String, String> dep) {
    new Expectations() {{
            dep.put("key", "value"); // dep is null => NullPointerException !
        }};
    final ClassUnderTest c = new ClassUnderTest();
    c.register("key", "value");
}

}

如果我用任何POJO替换HashMap,JMockIt可以模拟它并且测试将成功。 实际上,似乎JMockit不能从Java Collection Framework中模拟任何具体的类(也没有派生任何具体类的类)。

以下测试用例显示此问题:

public class MyTests {

public static class POJO {
}

public static class MyMapExtendingConcreteCollection extends HashMap<String, String> {
}

public static class MyMapExtendingAbstractCollection extends AbstractMap<String, String> {

    @Override
    public Set<java.util.Map.Entry<String, String>> entrySet() {
        return null;
    }

}

@Test
public void strangeBehaviors(@Mocked POJO mockedPOJO, @Mocked HashMap<String, String> mockedMap,
        @Mocked ConcurrentHashMap<String, String> mockedConcurrentMap,
        @Mocked MyMapExtendingConcreteCollection mockedMyMapFromConcrete,
        @Mocked MyMapExtendingAbstractCollection mockedMyMapFromAbstract) {
    assertNotNull(mockedPOJO); // OK : not null
    assertNotNull(mockedMap); // FAIL: null !
    assertNotNull(mockedConcurrentMap); // OK : not null
    assertNotNull(mockedMyMapFromConcrete); // FAIL: null !
    assertNotNull(mockedMyMapFromAbstract); // OK: not null
}

}

我在使用JMockit时会误解一些内容吗?

感谢您的帮助。

3 个答案:

答案 0 :(得分:6)

由于JMockit中的错误,mock参数为null,如果碰巧被模拟,会影响某些广泛使用的JRE类(包括ArrayList,HashMap和其他一些类)。

错误可以修复,但真正的问题是这些类是否应该首先被允许嘲笑。在我看来,它们不应该,因为几乎肯定没有合法的用例。

在问题中暴露的案例中,测试应该是: a)通过公共getter验证某些内部集合/ map的状态或其他一些方法的返回值; b)或作为最后的手段,使用Reflection来获取对内部状态的访问权限(mockit.Deencapsulation可以在这里提供帮助。)

同样的规则应该适用于AbstractCollectionHashMap等的子类,因为它们也只是不应该被嘲笑的数据持有者。更一般地说,没有java.util.*接口方法应该是可模拟的(除了少数例外)。在将来的版本中,JMockit将尝试使用Expectations API模拟此类方法的描述性异常。

自下而上,无论是因为该工具无法正确模拟它,或者它不会通过选择来模拟它,如果无法编写此类测试,最好是用户。 “价值对象”类和集合/地图不应该被嘲笑;总是有更好的方法来编写测试。

答案 1 :(得分:0)

无需模拟ArrayList,Hashmap等对象,因为我们可以使用虚拟对象进行测试。

而不是模拟列表

@Mock List<Object>;

使用虚拟对象提供输入

 public List<Object> get(){
  List<Object> list=new ArrayList<Object>();
  list.add(new Customer());
  return list;
  } 

答案 2 :(得分:0)

在测试对象有一个列表作为构造函数参数的情况下,能够使用@Injectable 注释列表会很有帮助,例如

    @Injectable
    List<Path> testList = new ArrayList<Path>();

    @Tested
    ObjectWithListAsConstructorParameter myTestedObject;

此类测试以 java.lang.IllegalArgumentException: java.util.List is not mockable 失败。一种解决方法是在每次测试之前创建测试对象。但是,使用 @Tested 和 @Injectable 注释会更好。