在项目中,经常要用到读系统文件.在项目的遗留代码中,都是在系统启动是传入一个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的项目.