像大多数找到这个主题的人一样,我正在编写一些单元测试,并需要修改环境变量以设置正确的条件来运行测试。然而,我发现最受欢迎的答案存在一些问题和/或非常神秘或过于复杂。希望这可以帮助其他人更快地解决方案。
首先,我最终发现@Hubert Grzeskowiak的解决方案是最简单的,并且对我有用。我希望我第一个就能想到那个。它基于@Edward Campbell的答案,但没有复杂的循环搜索。
然而,我从@pushy的解决方案开始,它获得了最多的投票。它是@anonymous和@Edward Campbell's的组合。@pushy声称两种方法都需要覆盖Linux和Windows环境。我在OS X下运行,发现两者都可以使用(一旦修复了@anonymous方法中的问题)。正如其他人所指出的,这个解决方案大多数时候有效,但并非全部。
我认为大部分混淆的源头来自@anonymous的解决方案操作“theEnvironment”字段。查看
ProcessEnvironment结构的定义,“theEnvironment”不是Map< String, String >,而是Map< Variable, Value >。清除映射很好用,但putAll操作会重建一个Map< String, String >,这在后续操作使用期望Map<Variable, Value>的正常API的数据结构时可能会引起问题。此外,访问/删除单个元素也是一个问题。解决方案是间接地通过“theUnmodifiableEnvironment”访问“theEnvironment”。但由于这是类型
UnmodifiableMap,因此必须通过UnmodifiableMap类型的私有变量'm'进行访问。请参见下面代码中的getModifiableEnvironmentMap2。
在我的情况下,我需要删除一些环境变量以进行测试(其他变量应保持不变)。然后我想在测试后将环境变量恢复到它们以前的状态。以下例程使此变得直截了当。我在OS X上测试了getModifiableEnvironmentMap的两个版本,两者都同样有效。尽管根据这个主题中的评论,其中一个可能比另一个更好,具体取决于环境。
注意:我没有包括对“theCaseInsensitiveEnvironmentField”的访问,因为那似乎只适用于Windows,而我无法测试它,但添加它应该很简单。
private Map<String, String> getModifiableEnvironmentMap() {
try {
Map<String,String> unmodifiableEnv = System.getenv();
Class<?> cl = unmodifiableEnv.getClass();
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Map<String,String> modifiableEnv = (Map<String,String>) field.get(unmodifiableEnv);
return modifiableEnv;
} catch(Exception e) {
throw new RuntimeException("Unable to access writable environment variable map.");
}
}
private Map<String, String> getModifiableEnvironmentMap2() {
try {
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
Field theUnmodifiableEnvironmentField = processEnvironmentClass.getDeclaredField("theUnmodifiableEnvironment");
theUnmodifiableEnvironmentField.setAccessible(true);
Map<String,String> theUnmodifiableEnvironment = (Map<String,String>)theUnmodifiableEnvironmentField.get(null);
Class<?> theUnmodifiableEnvironmentClass = theUnmodifiableEnvironment.getClass();
Field theModifiableEnvField = theUnmodifiableEnvironmentClass.getDeclaredField("m");
theModifiableEnvField.setAccessible(true);
Map<String,String> modifiableEnv = (Map<String,String>) theModifiableEnvField.get(theUnmodifiableEnvironment);
return modifiableEnv;
} catch(Exception e) {
throw new RuntimeException("Unable to access writable environment variable map.");
}
}
private Map<String, String> clearEnvironmentVars(String[] keys) {
Map<String,String> modifiableEnv = getModifiableEnvironmentMap();
HashMap<String, String> savedVals = new HashMap<String, String>();
for(String k : keys) {
String val = modifiableEnv.remove(k);
if (val != null) { savedVals.put(k, val); }
}
return savedVals;
}
private void setEnvironmentVars(Map<String, String> varMap) {
getModifiableEnvironmentMap().putAll(varMap);
}
@Test
public void myTest() {
String[] keys = { "key1", "key2", "key3" };
Map<String, String> savedVars = clearEnvironmentVars(keys);
setEnvironmentVars(savedVars);
}