測試驅動開發(fā)(Test-driven development)在維基百科上的定義是一種軟件開發(fā)過程中的應用方法,由極限編程中倡導,以其倡導先寫測試程序,然后編碼實現(xiàn)其功能得名。測試驅動開發(fā)始于20世紀90年代。測試驅動開發(fā)的目的是取得快速反饋并使用“illustrate the main line”方法來構建程序。簡單來說,就是從用戶的角度先編寫測試用例,再實現(xiàn)功能代碼,再進行重構和代碼測試。
現(xiàn)在就以liteStruts
為例,簡要說明TDD開發(fā)的過程。liteStruts
所要實現(xiàn)的功能是讀取struts.xml
,執(zhí)行Action
,例如用戶需要登錄(login
),發(fā)出請求后,web應用(Struts類對象)
- 首先解析
struts.xml
配置文件,其中有一個execute(String actionName,Map parameters)
方法; - 然后根據(jù)
actionName
找到對應的類class,反射實例化Action對象,調用setter
方法設置該對象參數(shù)parameters
; - 第三步反射執(zhí)行
Action
對象的execute
獲取返回值;第四步通過Action對象getter
返回參數(shù), - 最后返回
View
視圖對象。
現(xiàn)在就以第一步——解析struts.xml
為例,進行測試驅動開發(fā)。因為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>
我們需要根據(jù)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
參數(shù),來獲取我們希望的類名,最后進行斷言,判斷類名是否正確,這就是簡單的測試用例開發(fā)。
然后,我們開始開發(fā)解析結果Struts.xml
類的開發(fā)(所需功能)。
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
儲存我們的結果。這樣編寫實現(xiàn)功能之后,我們就可以開始測試我們的功能代碼是否正確。于是,回到ConfigurationTest
類,我們進行JUnit
單元測試,測試通過。如果,發(fā)現(xiàn)有可以重構的代碼,我們可以進行重構簡化。
在完成該作業(yè)的過程中,可以按照這種TDD
的思路進行功能開發(fā)。這樣做,有好處:
- 可以有效的避免過度設計帶來的浪費。
- 開發(fā)者在開發(fā)中擁有更全面的視角。