像大多数找到这个线程的人一样,我正在编写一些单元测试,需要修改环境变量以设置测试运行的正确条件。然而,我发现被点赞最多的答案有一些问题和/或非常神秘或过于复杂。希望这能帮助其他人更快地找到解决方案。
首先,我终于发现@Hubert Grzeskowiak的解决方案是最简单的,对我来说很管用。我希望我能先讲到这一点。它基于@Edward Campbell的回答,但是没有复杂的for循环搜索。
然而,我从@pushy的解决方案开始,它得到了最多的赞。它是@anonymous和@Edward Campbell's的组合。@pushy声称这两种方法都需要覆盖Linux和Windows环境。我在OS X下运行,发现两者都可以工作(一旦@匿名方法的问题被修复)。正如其他人所指出的,这种解决方案在大多数情况下都有效,但并非所有情况都有效。
I think the source of most of the confusion comes from @anonymous's solution operating on the 'theEnvironment' field. Looking at the definition of the ProcessEnvironment structure, 'theEnvironment' is not a Map< String, String > but rather it is a Map< Variable, Value >. Clearing the map works fine, but the putAll operation rebuilds the map a Map< String, String >, which potentially causes problems when subsequent operations operate on the data structure using the normal API that expects Map< Variable, Value >. Also, accessing/removing individual elements is a problem. The solution is to access 'theEnvironment' indirectly through 'theUnmodifiableEnvironment'. But since this is a type UnmodifiableMap the access must be done through the private variable 'm' of the UnmodifiableMap type. See getModifiableEnvironmentMap2 in code below.
在我的例子中,我需要为我的测试删除一些环境变量(其他的应该保持不变)。然后,我想在测试之后将环境变量恢复到它们之前的状态。下面的例程会让你更容易做到。我在OS X上测试了两个版本的getModifiableEnvironmentMap,两者的工作效果相当。尽管基于本线程中的注释,但根据环境的不同,其中一种可能比另一种更好。
注意:我没有包括对' casaseinsensitiveenvironmentfield '的访问,因为这似乎是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);
}