在項目中,經常要用到讀系統文件.在項目的遺留代碼中,都是在系統啟動是傳入一個APP_HOME,然后根據相對路徑去讀文件.這樣做的缺點是比較難測試,而且自動化的測試更難.

比如說有這樣一個類Server,要根據server.properties來初始化,一開始的代碼是這樣的:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

/**
* @author sting
*/
public class Server {
private static final String FILE = "conf" + File.separator + "server.properties";

public void initial() throws IOException {
FileInputStream in = new FileInputStream(System.getProperty("APP_HOME") + File.separator + FILE);
Properties properties = new Properties();
properties.load(in);
// initial
}
}

文件路徑和文件名都是hard code,很難測試. 我們首先把initial()重構一下,代碼如下:


public void initial(InputStream in) throws IOException {
Properties properties = new Properties();
properties.load(in);
// initial
}

至少,測試時,我們可以傳進來自己的InputStream,也可以方便的時候測試用的server.properties,或者干脆使用內聯的文件,代碼如下:

class ServerTest extends TestCase {
private Server server;

public void setUp() throws Exception {
this.server = new Server();
}

public void testInitial() throws Exception {
String serverProperties = "port=8080\n" +
"run_mode=normal";
InputStream in = new ByteArrayInputStream(serverProperties.getBytes());

this.server.initial(in);
// assert
}
}

但是,在實際工作的代碼中,文件名和路徑依然要hard code進代碼中.這時,我們可以使用spring中的Resource接口來進一步改進我們的代碼.

public class Server {
private Resource resource;

public void setResource(Resource r) {
this.resource = r;
}

public void initial() throws IOException {
Properties properties = new Properties();
properties.load(this.resource.getInputStream());
// initial
}
}

再加一段spring的配置文件:

<beans>
<bean id="server" class="Server">
<property name="resource" value="classpath:server.properties"/>
</bean>
</beans>

這樣,Server的代碼完全與文件的具體路徑和文件名無關,僅僅用配置文件就可以指定,表達更清楚,也更易于測試.

當然,僅限于已經使用spring的項目.