测试驱动开发(Test-driven development)在维基百科上的定义是一种软件开发过程中的应用方法,由极限编程中倡导,以其倡导先写测试程序,然后编码实现其功能得名。测试驱动开发始于20世纪90年代。测试驱动开发的目的是取得快速反馈并使用“illustrate the main line”方法来构建程序。简单来说,就是从用户的角度先编写测试用例,再实现功能代码,再进行重构和代码测试。
现在就以liteStruts
为例,简要说明TDD开发的过程。liteStruts
所要实现的功能是读取struts.xml
,执行Action
,例如用户需要登录(login
),发出请求后,web应用(Struts类对象)
- 首先解析
struts.xml
配置文件,其中有一个execute(String actionName,Map parameters)
方法; - 然后根据
actionName
找到对应的类class,反射实例化Action对象,调用setter
方法设置该对象参数parameters
; - 第三步反射执行
Action
对象的execute
获取返回值;第四步通过Action对象getter
返回参数, - 最后返回
View
视图对象。
现在就以第一步——解析struts.xml
为例,进行测试驱动开发。因为struts.xml
文件如下
<?xml version="1.0" encoding="UTF-8"?>
<struts>
<action name="login" class="com.coderising.litestruts.LoginAction">
<result name="success">/jsp/homepage.jsp</result>
<result name="fail">/jsp/showLogin.jsp</result>
</action>
<action name="logout" class="com.coderising.litestruts.LogoutAction">
<result name="success">/jsp/welcome.jsp</result>
<result name="error">/jsp/error.jsp</result>
</action>
</struts>
我们需要根据execute(String actionName,Map parameters)
方法的actionName,来解析xml
文件,获取对应的类名。因此,先编写测试类
package com.coderising.litestruts;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ConfigurationTest {
Configuration cfg = new Configuration("struts.xml");
@Test
public void testGetClassName() {
String clzName = cfg.getClassName("login");
Assert.assertEquals("com.coderising.litestruts.LoginAction", clzName);
clzName = cfg.getClassName("logout");
Assert.assertEquals("com.coderising.litestruts.LogoutAction", clzName);
}
}
其中,Configure
对应于xml
解析结果。ConfigurationTest
对应测试类。从测试类中可以看出,我们铜鼓哦调用Configure
的对象,调用其getClassName
方法,传入login
参数,来获取我们希望的类名,最后进行断言,判断类名是否正确,这就是简单的测试用例开发。
然后,我们开始开发解析结果Struts.xml
类的开发(所需功能)。
package com.coderising.litestruts;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
public class Configuration {
Map<String,ActionConfig> actions = new HashMap<>();
public Configuration(String fileName) {
String packageName = this.getClass().getPackage().getName();
packageName = packageName.replace('.', '/');
InputStream is = this.getClass().getResourceAsStream("/" + packageName + "/" + fileName);
parseXML(is);
try {
is.close();
} catch (IOException e) {
throw new ConfigurationException(e);
}
}
private void parseXML(InputStream is){
SAXBuilder builder = new SAXBuilder();
try {
Document doc = builder.build(is);
Element root = doc.getRootElement();
for(Element actionElement : root.getChildren("action")){
String actionName = actionElement.getAttributeValue("name");
String clzName = actionElement.getAttributeValue("class");
ActionConfig ac = new ActionConfig(actionName, clzName);
for(Element resultElement : actionElement.getChildren("result")){
String resultName = resultElement.getAttributeValue("name");
String viewName = resultElement.getText().trim();
ac.addViewResult(resultName, viewName);
}
this.actions.put(actionName, ac);
}
} catch (JDOMException e) {
throw new ConfigurationException(e);
} catch (IOException e) {
throw new ConfigurationException(e);
}
}
public String getClassName(String action) {
ActionConfig ac = this.actions.get(action);
if(ac == null){
return null;
}
return ac.getClassName();
}
}
这里,使用jDom2来解析我们的struts.xml
文件,用一个map
结合actions
储存我们的结果。这样编写实现功能之后,我们就可以开始测试我们的功能代码是否正确。于是,回到ConfigurationTest
类,我们进行JUnit
单元测试,测试通过。如果,发现有可以重构的代码,我们可以进行重构简化。
在完成该作业的过程中,可以按照这种TDD
的思路进行功能开发。这样做,有好处:
- 可以有效的避免过度设计带来的浪费。
- 开发者在开发中拥有更全面的视角。
网友评论