代码之家  ›  专栏  ›  技术社区  ›  Stefan Kendall

使用反射更改单元测试的静态final File.separatorChar?

  •  23
  • Stefan Kendall  · 技术社区  · 14 年前

    File.separatorChar 在windows和unix上构建路径。代码必须在两个平台上运行,但是当我尝试更改这个静态final字段时,JUnit会出错。

    有人知道发生了什么吗?

    Field field = java.io.File.class.getDeclaredField( "separatorChar" );
    field.setAccessible(true);
    field.setChar(java.io.File.class,'/');
    

    当我这么做的时候

    IllegalAccessException: Can not set static final char field java.io.File.separatorChar to java.lang.Character
    

    7 回复  |  直到 14 年前
        1
  •  67
  •   Rogério    8 年前

    Field.set :

    IllegalAccessException 除非 setAccessible(true) 此字段已成功 这个字段是非静态的 .

    File.separatorChar static . 令人惊讶的是 解决这个问题的方法是:简单地 静止的 场不再 final 通过反思。

    from javaspecialist.eu :

    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
    
        // remove final modifier from field
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    
        field.set(null, newValue);
    }
    

    setFinalStatic(File.class.getField("separatorChar"), '#');
    System.out.println(File.separatorChar); // prints "#"
    

    使用这种技术时一定要格外小心 . 抛开毁灭性的后果不谈,以下事实上是有效的:

    setFinalStatic(Boolean.class.getField("FALSE"), true);
    System.out.format("Everything is %s", false); // "Everything is true"
    

    :上述解决方案 没有 在任何情况下都有效。如果在重置字段之前使其可访问并通过反射读取,则 被抛出。它失败是因为反射API创建了内部 FieldAccessor 缓存和重用的对象(请参阅java.lang.reflect.Field#acquireFieldAccessor(布尔)实现)。 失败的测试代码示例:

    Field f = File.class.getField("separatorChar"); f.setAccessible(true); f.get(null);
    // call setFinalStatic as before: throws IllegalAccessException
    
        2
  •  2
  •   Karussell    14 年前

    尝试在文件实例上调用,而不是在类文件实例上调用

    例如。

    File file = ...;    
    field.setChar(file,'/');
    

    你也可以试试 http://code.google.com/p/jmockit/

        3
  •  2
  •   user207421    14 年前

    在构建文件时只需使用/everywhere。我已经做了13年了,从来没有遇到过问题。也没什么可测试的。

        4
  •  1
  •   Brian Agnew    14 年前

    我知道这不能直接回答你的问题,但是 Apache Commons FileNameUtils 将进行跨平台的文件名构造,并可能节省您编写自己的类来执行此操作。

        5
  •  1
  •   anand krish    7 年前

    这里我要设置“android.os.Build.VERSION.RELEASE”的值,其中VERSION是类名,RELEASE是最终的静态字符串值。

    如果基础字段是final,则方法 非法访问例外 NoSuchFieldException异常 使用时需要添加 field.set() 方法

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({Build.VERSION.class})
    public class RuntimePermissionUtilsTest {
    @Test
    public void hasStoragePermissions() throws IllegalAccessException, NoSuchFieldException {
        Field field = Build.VERSION.class.getField("RELEASE");
        field.setAccessible(true);
        field.set(null,"Marshmallow");
     }
    }
    

    现在字符串的值 释放 “会回来的” 棉花糖 ".

        6
  •  0
  •   Itay Maman    14 年前

    与其使用File.separatorChar声明服务类,不如称之为PathBuilder之类的。这个类将有一个concatPaths()方法,它将连接两个参数(使用操作系统的分隔符char)。最妙的是,你正在编写这个类,这样你就可以在单元测试的时候随意调整它。

        7
  •  0
  •   Stephen Denne    14 年前

    您可以获取java.io.File的源代码,并对其进行修改,使separatorChar和separator不是最终的,然后添加一个setSeparatorChar方法来更新它们中的两个,然后在bootclasspath中包含已编译的类。