像大多数找到这个线程的人一样,我正在编写一些单元测试并且需要修改环境变量以设置正确的条件以运行测试。但是,我发现投票最多的答案存在一些问题和/或非常神秘或过于复杂。希望这将帮助其他人更快地解决问题。
首先,我终于发现@Hubert Grzeskowiak 的解决方案是最简单的,它对我有用。我希望我能先来那个。它基于@Edward Campbell 的回答,但没有复杂的 for 循环搜索。
但是,我从@pushy 的解决方案开始,它得到了最多的支持。它是@anonymous 和@Edward Campbell 的组合。@pushy 声称这两种方法都需要涵盖 Linux 和 Windows 环境。我在 OS X 下运行,发现两者都可以工作(一旦解决了@anonymous 方法的问题)。正如其他人所指出的,此解决方案在大多数情况下都有效,但并非全部。
我认为大多数混乱的根源来自@anonymous 在“环境”字段上运行的解决方案。查看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);
// do test
setEnvironmentVars(savedVars);
}