我有需要模拟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时会误解一些内容吗?
感谢您的帮助。
答案 0 :(得分:6)
由于JMockit中的错误,mock参数为null,如果碰巧被模拟,会影响某些广泛使用的JRE类(包括ArrayList,HashMap和其他一些类)。
错误可以修复,但真正的问题是这些类是否应该首先被允许嘲笑。在我看来,它们不应该,因为几乎肯定没有合法的用例。
在问题中暴露的案例中,测试应该是:
a)通过公共getter验证某些内部集合/ map的状态或其他一些方法的返回值;
b)或作为最后的手段,使用Reflection来获取对内部状态的访问权限(mockit.Deencapsulation
可以在这里提供帮助。)
同样的规则应该适用于AbstractCollection
,HashMap
等的子类,因为它们也只是不应该被嘲笑的数据持有者。更一般地说,没有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 注释会更好。