通过单元测试中的反射访问私有地图

时间:2014-01-06 14:30:32

标签: java unit-testing reflection

我需要访问私有地图进行单元测试,但无法执行此操作。

主要课程:

public class TestMe{
private Map<String, SomeObject> testMap = new HashMap<String, SomeObject>();
}

单元测试:

public class TestMeTester{
   public testTestMe(){
      TestMe obj = new TestMe();

      Class clazz = obj.getClass();

      Field field = clazz.getDeclaredField("testMap");
      field.setAccessible(true);
      Map<String, SomeObject> refMap = (HashMap<String, SomeObject>) field.get(new HashMap<String, Object>());
            System.out.println("==>" + refMap);
   }
}

例外:

java.lang.IllegalArgumentException: Can not set java.util.Map field com.xx.TestMe.testMap to java.util.HashMap
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:55)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)

4 个答案:

答案 0 :(得分:3)

您必须使用Field.get()对象调用TestMe

TestMe obj = new TestMe(); 
....
Map<String, SomeObject> refMap = (HashMap<String, SomeObject>) = field.get(obj);

您的代码会在Field.get()的新实例上调用HashMap

field.get(new HashMap<String, Object>()); // This will not work in your case

编辑评论:这不起作用。

我试图弄明白什么不行。这是我的测试代码,它可以工作。

public class TestMeTester {

    public static class TestMe {
        private Map<String, String> testMap = new HashMap<String, String>();
    }

    public static void main(String[] args) throws IllegalArgumentException,
        IllegalAccessException, SecurityException, NoSuchFieldException {
        TestMe obj = new TestMe();
        Class clazz = obj.getClass();
        Field field = clazz.getDeclaredField("testMap");
        field.setAccessible(true);
        Map<String, String> refMap = (HashMap<String, String>) field.get(obj);
        System.out.println("==>" + refMap);
    }
}

答案 1 :(得分:0)

如果您需要访问地图进行单元测试,请添加默认访问getter方法。编写代码以支持单元测试没有错:

Map<String, SomeObject> getTestMap() {...}

反射应该是您的最后选择,通常是在您没有机会编辑目标类时。

答案 2 :(得分:0)

我认为你应该这样做

Map<String, SomeObject> refMap = (HashMap<String, SomeObject>) field.get(obj);

如以下(工作)代码所示:

import java.lang.reflect.Field;

public class Test {
    static public void main(String[] args) throws Exception {
        Foo f = new Foo();
        Field x = f.getClass().getDeclaredField("x");
        x.setAccessible(true);
        System.out.println("Before: " + x.get(f)); // prints 1
        x.set(f, (Integer)2);
        System.out.println("After: " + x.get(f)); // prints 2
    }
}

class Foo {
    private Integer x = 1;
}

否则,您正在尝试检索testMap的{​​{1}}字段,该字段不存在。

除了您通过反射访问私有字段的问题之外,编写依赖于这些实现细节(即私有字段的存在)的单元测试很可能很快就会破解。我建议看一下以下链接:

http://xunitpatterns.com/Fragile%20Test.html#Overcoupled%20Software http://xunitpatterns.com/Principles%20of%20Test%20Automation.html#Use%20the%20Front%20Door%20First

答案 3 :(得分:0)

所以我只是通过我的代码的一些试验和错误来解决它:

如果你像我一样,你必须使用Java Reflection,那么请继续阅读,否则,@ Duncan已经提到了一种有效的方法,只需制作一个方法来获取HashMap用于测试目的。

现在,你要做的是:

  1. 声明类型字段作为您对私有地图的引用,并初始化包含
    的类 private Map: Field map = ClassName.class.getDeclaredField(String "name of map")

  2. 将您的Field实例设置为可访问:map.setAccessible(true)

  3. 由于您知道Field是Map,因此您可以将Object转换为Map,然后在Field pass参数上调用Map方法。 Field instance.get(obj) call. (Map) map.get(classNameInstance)

  4. 在测试中我得到一个错误,说明我的情况下的assertEquals(Object,Object)。为了避免这个问题,我简单地将它转换为原语。我也检查了它也适用于String,所以它应该适用于所有其他定义的对象。

  5. 这是一个编码示例供参考。在我的情况下,我不得不使用将字符串映射到整数的HashMap,我最后的转换错误是我无法使用assertEquals比较int和Integer,所以我将Integer转换为int:

    public class KeywordCollection{
      private static HashMap<String, Integer> map = new HashMap<String, Integer>();
      //Etc. for KeywordCollection, only the private initialization is relevant
    }
    
    public void addTests() throws SecurityException, NoSuchFieldException,
            Exception, IllegalAccessException {
        KeywordCollection keys1 = new KeywordCollection();
        Field map = KeywordCollection.class.getDeclaredField("keywordTable");
        map.setAccessible(true);
    
        // Test if add works trivially
        assertTrue(keys1.addKeyword("First"));
        assertFalse(keys1.addKeyword("First"));
        assertFalse(keys1.addKeyword("FIrST"));
        assertTrue(keys1.addKeyword("Inductive"));
        assertTrue(keys1.addKeyword("Not in keys2"));
        System.out.println(map.get(keys1).toString());
        output: {Not in keys2=3, Inductive=2, First=1}
    
        //For getting values
        assertEquals(1,(int) ((HashMap<String, Integer>) map.get(keys1)).get("First"));
    

    所以你可以看到使用强制调用(HashMap<String, Integer>) map.get(keys1) 我设置Field map实例来引用我的对象类KeywordCollection,它有我的HashMap,然后将它从Object转换为HashMap,这样我就可以访问我的HashMap方法了。否则它只访问Object方法。使用它,您可以像往常一样在地图上运行测试(访问map.keySet,contains,get(key)等等。)

    另外,不要担心所有例外情况。我在Eclipse中进行编码,因此它给出了未处理异常的警告,所以我设置为抛出根据API的方法抛出的异常。如果你很好奇它们属于Field变量,但如果你知道你所调用的方法中你的字段是什么,那么使用这种方法就不会有问题。

    NoSuchFieldException表示您在您调用的类中传递的名称没有变量

    和IllegalAccessException意味着您无权访问该字段(即您未将setAccessible设置为true)

    另外不要忘记你的进口。对于Field,您导入java.lang.reflect.Field(我喜欢使用*)

    希望它有所帮助。