通向架構師的道路(第二十五天)SSH 的單元測試與 dbunit 的整合
(點擊
上方公眾號
,可快速關注)
來源:袁鳴凱,
blog.csdn.net/lifetragedy/article/details/8251056
一、前言
在二十三天中我們介紹了使用maven來下載工程的依賴庫文件,用ant來進行war包的建立。今天我們在這個基礎上將使用junit+dbunit來進行帶有單元測試報告的框架的架構。
目標:
每次打包之前自動進行單元測試並生成單元測試報告
生成要布署的打包文件即war包
單元測試的代碼不能夠被打在正式的要布署的war包內,單元測試僅用於unit test用
使用模擬數據對dao層進行測試,使得dao方法的測試結果可被預料
二、Junit+Ant生成的單元測試報告
上面是一份junit生成的測試報告,它可以與ant任務一起運行然後自動生成這麼一份html的測試報告,要生成這樣的一份junit test report我們需要調用ant任務中的<junitreport>這個task,示例代碼如下:
<target name="junitreport">
<junit printsummary="on" haltonfailure="false" failureproperty="tests.failed" showoutput="true">
<classpath>
<pathelement path="${dist.dir}/${webAppQAName}/WEB-INF/classes" />
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
<fileset dir="${ext-lib.dir}">
<include name="*.jar" />
</fileset>
</classpath>
<formatter type="xml" />
<batchtest todir="${report.dir}">
<fileset dir="${dist.dir}/${webAppQAName}/WEB-INF/classes">
<include name="org/sky/ssh/ut/Test*.*" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report.dir}">
<fileset dir="${report.dir}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="report" />
</junitreport>
<fail if="tests.failed">
---------------------------------------------------------
One or more tests failed, check the report for detail...
---------------------------------------------------------
</fail>
</target>
在一般的產品級開發時或者是帶有daily building/nightly building的項目組中我們經常需要檢查最新check in的代碼是否影響到了原有的工程的編譯,因為每天都有程序員往源碼伺服器里check in代碼,而有時我們經常會碰到剛剛被check in的代碼在該程序員本地跑的好好的,但是check in源碼伺服器上後別人從源碼伺服器「拉」下來的最新代碼跑不起來,甚至編譯出錯,這就是regression bug,
因此我們每天的打包要乾的事情應該是:
程序員check in代碼時必須把相關的unit test也check in源碼伺服器
次日的零晨由持續集成構件如:cruisecontrol自動根據設好的schedule把所有的源碼伺服器的代碼進行編譯
運行單元測試
生成報告
打包布署到QA伺服器上去
如果考究點的還會生成一份「單元測試覆蓋率」報告。
那麼有了這樣的單元測試報告,項目組組長每天早上一上班檢查一下單元測試報告就知道昨天代碼check in的情況,有多少是成功多少是失敗,它們分別是哪些類,哪些方法,以找到相關的負責人。
同時,有了單元測試報告,如果測試報告上顯示的是有fail的地方,該版本就應被視之為fail,不能被送給QA進行進一步的測試,直到所有的單元測試成功才能被送交QA。
三、如何在Spring下書寫一個單元測試方法
3.1使用spring的注入特性書寫一個單元測試
Spring是一個好東西,一切依賴注入,連單元測試都變成了依賴注入了,這省去我們很多麻煩。
我們可以將web工程中的applicationContext、Datasource甚至iBatis或者是Hibernate的配署都可以注入給junit,這樣使得我們可以用IoC的方法來書寫我們的單元測試類。
此處,我們使用的junit為4.7, 而相關的spring-test庫文件為3.1,我都已經在pom.xml文件中註明了.
我們先在eclipse里建立一個專門用來放單元測試類的src folder:test/main/java。
注意一下單元測試類的coding convention:
所有的測試類必須以Test開頭
所有的測試方法名必須為public類型並且以test開頭
所有的測試類全部放在test/main/java目錄下,不可和src/main/java混放
類 org.sky.ssh.ut.BaseSpringContextCommon
package org.sky.ssh.ut;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "/spring/appconfig/applicationContext.xml", "/org/sky/ssh/ut/ds/datasource.xml",
"/spring/hibernate/hibernate.xml" })
public class BaseSpringContextCommon {
}
該類為一個基類,我們所有的單元測試類全部需要繼承自該類,大家可以把這個類認為一個spring的context載入器,注意這邊的datasource.xml。
因為我們在做測試方法時勢必會涉及到對一些數據進行操作,因此我們在資料庫里除了平時開發和布署用的資料庫外,還有一個專門用於運行「單元測試」的「單元測試資料庫」或者「單元測試資料庫實例」,因此我們在單元測試時會把我們當前的資料庫連接「硬」指向到「單元測試用資料庫」上去.
這個datasource.xml文件位於/org/sky/ssh/ut/ds目錄下,見下圖(當然它也必須被放在test/main/java目錄里哦:
該文件內容如下:
org.sky.ssh.ut.ds.datasource.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource" />
<!-- configure data base connection pool by using JNDI -->
<!--
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>${jdbc.jndiname}</value>
</property>
</bean>
-->
<!-- configure data base connection pool by using C3P0 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.databaseURL}" />
<property name="user" value="alpha_test" />
<property name="password" value="password_1" />
<property name="initialPoolSize" value="10" />
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="15" />
<property name="acquireIncrement" value="1" />
<property name="maxIdleTime" value="5" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="submit*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="upd*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="query*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="get*" read-only="true" />
<tx:method name="view*" read-only="true" />
<tx:method name="search*" read-only="true" />
<tx:method name="check*" read-only="true" />
<tx:method name="is*" read-only="true" />
<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* org.sky.ssh.service.impl.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>
</beans>
注意兩行:
<property name="user" value="alpha_test" />
<property name="password" value="password_1" />
可以得知我們測試時用的是同一個資料庫上的另一個實例,該實例是專門為我們的單元測試用的.
我們先來書寫一個單元測試類吧
org.sky.ssh.ut.TestLoginDAO
package org.sky.ssh.ut;
import static org.junit.Assert.assertEquals;
import javax.annotation.Resource;
import org.junit.Test;
import org.sky.ssh.dao.LoginDAO;
import org.springframework.test.annotation.Rollback;
public class TestLoginDAO extends BaseSpringContextCommon {
@Resource
private LoginDAO loginDAO;
@Test
@Rollback(false)
public void testLoginDAO() throws Exception {
String loginId = "alpha";
String loginPwd = "aaaaaa";
long answer = loginDAO.validLogin(loginId, loginPwd);
assertEquals(1, answer);
}
}
很簡單吧,把原來的LongDAO注入進我們的單元測試類中,然後在test方法前加入一個@Test代碼該方法為「單元測試」方法即可被junit可識別,然後我們調用一下LoginDAO中的.validLogin方法,測試一下返回值。
運行方法為:
在eclipse打開該類的情況下右鍵->run as Junit Test
然後選junit4來運行,運行後直接出錯拋出:
Class not found org.sky.ssh.ut.TestLoginDAO
java.lang.ClassNotFoundException: org.sky.ssh.ut.TestLoginDAO
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClass(RemoteTestRunner.java:693)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClasses(RemoteTestRunner.java:429)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
這樣一個錯誤,為什麼?
其原因在於我們的工程是在eclipse里使用的m2 eclipse這個插件生成的,因此在做單元測試時由於我們的unit test的類是放在test/main/java這個目錄下,而這個目錄是我們手工建的,因此eclipse不知道這個目錄的對應的編譯輸出的class的目錄了.
沒關係,按照下面的方法:
右鍵->選擇run as->run configuration,打開如下的設置
選擇classpath這個選項欄
單擊user Entries
單擊Advanced按鈕
在彈出框中選擇Add Folders
點ok按鈕
在下一個彈出框中選擇我們的junit test的源碼在被編譯後輸出的目錄即myssh2工程的WebContent/WEB-INF/classes目錄,對吧。
點OK按鈕
點Apply按鈕
點Run按鈕,查看運行效果
運行成功,說明該unit test書寫的是對的。
3.2 結合dbunit來做單元測試
我們有了junit為什麼還要引入一個dbunit呢?這不是多此一舉嗎?
試想一下下列場景:
我們開發時連的是開發用的資料庫,一張表裡有一堆的數據,有些數據不是自己的插的是其它的開發人員插的,那麼我想要測試一個dao或者是service方法,獲得一個List,然後判斷這個List里的值是否為我想要的時候,有可能會碰到下屬這樣的情況:
運行我的service或者dao方法得到一個list,該list含有6個值,但正好在運行時另一個開發人員因為測試需要往資料庫里又插了一些值,導致我的測試方法失敗,對不對,這種情況是有可能的。
怎麼辦呢?比較好的做法是我們需要準備一份自己的業務數據即prepare data,因為是我們自己準備的數據數據,因此它在經過這個方法運行後得到的值,這個得到的值是要經過一系列的業務邏輯的是吧?因此這個得到的值即:expected data是可以被精確預料的。
因此,我們拿著這個expected data與運行了我們的業務方法後得到的結果進行比對,如果比對結果一致,則一定是測試成功,否則失敗,對吧?
這就是我們常說的,測試用數據需要是一份乾淨的數據。
那麼為了保持我們的數據乾淨,我們在測試前清空我們的業務表,插入數據,運行測試地,比對結果,刪除數據(也可以不刪除,因為每次運行時都會清空相關的業務表),這也就是為什麼我們事先要專門搞一個資料庫或者是資料庫實例,在運行單元測試時我們的資料庫連接需要指向到這個單元測試專用的資料庫的原因了,見下面的測試流程表:
有了DbUnit,它就可以幫助我們封裝:
準備測試用數據
清空相關業務表
插入測試數據
比對結果
清除先前插入的業務數據
這一系列底層的操作。
現在我們可以開始搭建我們的單元測試框架了,下面是這個單元測試框架的」邏輯表達圖「(一個架構設計文檔不僅需要有logic view還要有physical view。。。當然還有更多,以後會一點點分享出來)
這邊的Session Factory是結合的原有框架的Hibernate的Session Factory,我們也可以把它改成iBatis,Jdbc Template等等等。。。它可以稍作變動就可適用於一切SSX這樣的架構。
該框架的優點如下:
3.3 構建spring+junit+dbunit的框架
除去上述的一些類和配置我們還需要3個基類,它們分別位於test/main/java目錄下(因為它們都屬於unit test對吧)
org.sky.ssh.ut.util.CleanTableXmlAdapter
package org.sky.ssh.ut.util;
import org.dom4j.Element;
import org.dom4j.VisitorSupport;
import java.util.*;
public class CleanTableXmlAdapter extends VisitorSupport {
private ArrayList tableList = new ArrayList();
public CleanTableXmlAdapter() {
}
public void visit(Element node) {
try {
if ((node.getName().toLowerCase()).equals("table")) {
TableBean tBean = new TableBean();
tBean.setTableName(node.getText());
tableList.add(tBean);
}
} catch (Exception e) {
}
}
public ArrayList getTablesList() {
if (tableList == null || tableList.size() < 1) {
return null;
} else {
return tableList;
}
}
}
org.sky.ssh.ut.util.TableBean
package org.sky.ssh.ut.util;
import java.io.*;
public class TableBean implements Serializable{
private String tableName = "";
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
org.sky.ssh.ut.util.XmlUtil
package org.sky.ssh.ut.util;
import java.util.*;
import java.io.*;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.VisitorSupport;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.ClassPathResource;
public class XmlUtil {
public ArrayList getCleanTables(String xmlFile) {
ArrayList tablesList = new ArrayList();
try {
SAXReader reader = new SAXReader();
File file = new File(xmlFile);
Document doc = reader.read(file);
CleanTableXmlAdapter xmlAdapter = new CleanTableXmlAdapter();
doc.accept(xmlAdapter);
tablesList = xmlAdapter.getTablesList();
return tablesList;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
3.4使用框架
我們準備兩份測試用數據
test_del_table.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<Tables>
<table>t_student</table>
</Tables>
test_insert_table.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<t_student student_no="101" student_name="alice"/>
<t_student student_no="102" student_name="jil"/>
<t_student student_no="103" student_name="leon"/>
<t_student student_no="104" student_name="chris"/>
<t_student student_no="105" student_name="Ada Wong"/>
</dataset>
測試類org.sky.ssh.ut.TestStudentService
package org.sky.ssh.ut;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.DefaultDataSet;
import org.dbunit.dataset.DefaultTable;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.ext.mysql.MySqlDataTypeFactory;
import org.dbunit.operation.DatabaseOperation;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.sky.ssh.service.StudentService;
import org.sky.ssh.ut.util.TableBean;
import org.sky.ssh.ut.util.XmlUtil;
import org.sky.ssh.vo.StudentVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.test.annotation.Rollback;
public class TestStudentService extends BaseSpringContextCommon {
private final static String INSERT_TBL = "org/sky/ssh/ut/xmldata/student/test_insert_table.xml";
private final static String DEL_TBL = "org/sky/ssh/ut/xmldata/student/test_del_table.xml";
@Autowired
private DataSource dataSource;
@Resource
private StudentService stdService;
@SuppressWarnings("deprecation")
@Before
public void setUp() throws Exception {
IDatabaseConnection connection = null;
try {
connection = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
DatabaseConfig config = connection.getConfig();
config.setProperty("http://www.dbunit.org/properties/datatypeFactory", new MySqlDataTypeFactory());
//trunkTables(connection);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource(INSERT_TBL);
if (url == null) {
classLoader = ClassLoader.getSystemClassLoader();
url = classLoader.getResource(INSERT_TBL);
}
IDataSet dateSetInsert = new FlatXmlDataSetBuilder().build(new FileInputStream(url.getFile()));
DatabaseOperation.CLEAN_INSERT.execute(connection, dateSetInsert);
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
if (connection != null) {
connection.close();
}
}
}
@After
public void tearDown() throws Exception {
IDatabaseConnection connection = null;
try {
connection = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
DatabaseConfig config = connection.getConfig();
config.setProperty("http://www.dbunit.org/properties/datatypeFactory", new MySqlDataTypeFactory());
//trunkTables(connection);
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
if (connection != null) {
connection.close();
}
}
}
private void trunkTables(IDatabaseConnection connection) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource(DEL_TBL);
if (url == null) {
classLoader = ClassLoader.getSystemClassLoader();
url = classLoader.getResource(DEL_TBL);
}
XmlUtil xmlUtil = new XmlUtil();
List tablesList = xmlUtil.getCleanTables(url.getFile());
Iterator it = tablesList.iterator();
while (it.hasNext()) {
TableBean tBean = (TableBean) it.next();
IDataSet dataSetDel = new DefaultDataSet(new DefaultTable(tBean.getTableName()));
DatabaseOperation.DELETE_ALL.execute(connection, dataSetDel);
}
}
@Test
@Rollback(false)
public void testGetAllStudent() throws Exception {
List<StudentVO> stdList = new ArrayList<StudentVO>();
stdList = stdService.getAllStudent();
assertEquals(5, stdList.size());
}
}
該測試方法每次都清空t_student表
往t_student表裡注入5條數據
運行業務方法getAllStudent
比較getAllStudent方法返回的list里的size是否為5
清空注入的數據(也可不用去清空)
然後我們在eclipse里用junit來運行我們這個測試類吧。
我們現在用我們的單元測試用資料庫帳號連入我們的資料庫,查詢t_student表
我們往該表中手動插入一條數據
再重新運行一遍我們的單元測試
測試結果還是成功,再重新連入我們單元測試用資料庫實例查詢t_student表,發覺還是5條記錄,說明我們的框架達到了我們的目標。
四、將ant與我們的單元測試框架連接起來並生成單元測試報告
先來看一下我們的nightly building,即每天次日的零晨將要生成的單元測試與打包布署的流程吧
(需要ant1.8及以上版本運行)
然後下面給出build.xml文件(需要ant1.8及以上版本運行)(結合了maven的依賴庫機制)
build.properties文件
# ant
appName=myssh2
webAppName=myssh2
webAppQAName=myssh2-UT
local.dir=C:/eclipsespace/${appName}
src.dir=${local.dir}/src/main/java
test.src.dir=${local.dir}/test/main/java
dist.dir=${local.dir}/dist
report.dir=${local.dir}/report
webroot.dir=${local.dir}/src/main/webapp
lib.dir=${local.dir}/lib
ext-lib.dir=${local.dir}/ext-lib
classes.dir=${webroot.dir}/WEB-INF/classes
build.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project name="myssh2" default="buildwar" xmlns:artifact="urn:maven-artifact-ant">
<property file="build.properties" />
<property name="classes.dir" value="${dist.dir}/${webAppName}/WEB-INF/classes" />
<path id="maven-ant-tasks.classpath" path="C:/ant/lib/maven-ant-tasks-2.1.3.jar" />
<typedef resource="org/apache/maven/artifact/ant/antlib.xml" uri="urn:maven-artifact-ant" classpathref="maven-ant-tasks.classpath" />
<artifact:pom id="maven.project" file="pom.xml" />
<artifact:dependencies filesetId="deps.fileset.compile" useScope="compile">
<!--<pom file="pom.xml"/>-->
<pom refid="maven.project" />
</artifact:dependencies>
<path id="compile.classpath">
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
</path>
<target name="clean" description="Delete old build and dist directories">
<delete dir="${dist.dir}" />
<delete dir="${report.dir}" />
<mkdir dir="${report.dir}" />
<mkdir dir="${dist.dir}" />
<!-- create war structure for production env-->
<mkdir dir="${dist.dir}/${webAppName}" />
<mkdir dir="${dist.dir}/${webAppName}/WEB-INF" />
<mkdir dir="${dist.dir}/${webAppName}/WEB-INF/lib" />
<mkdir dir="${dist.dir}/${webAppName}/WEB-INF/classes" />
<mkdir dir="${dist.dir}/${webAppName}/css" />
<mkdir dir="${dist.dir}/${webAppName}/images" />
<mkdir dir="${dist.dir}/${webAppName}/jsp" />
<!-- create war structure for qa env -->
<mkdir dir="${dist.dir}/${webAppQAName}" />
<mkdir dir="${dist.dir}/${webAppQAName}/WEB-INF" />
<mkdir dir="${dist.dir}/${webAppQAName}/WEB-INF/lib" />
<mkdir dir="${dist.dir}/${webAppQAName}/WEB-INF/classes" />
<mkdir dir="${dist.dir}/${webAppQAName}/WEB-INF/classes/org/sky/ssh/ut/ds" />
<mkdir dir="${dist.dir}/${webAppQAName}/WEB-INF/classes/org/sky/ssh/ut/xmldata/student" />
<mkdir dir="${dist.dir}/${webAppQAName}/css" />
<mkdir dir="${dist.dir}/${webAppQAName}/images" />
<mkdir dir="${dist.dir}/${webAppQAName}/jsp" />
</target>
<target name="download-libs" depends="clean">
<copy todir="${lib.dir}">
<fileset refid="deps.fileset.compile" />
<mapper type="flatten" />
</copy>
</target>
<target name="compile" description="Compile java sources" depends="download-libs">
<!-- compile main class -->
<javac debug="true" destdir="${dist.dir}/${webAppName}/WEB-INF/classes" includeAntRuntime="false" srcdir="${src.dir}">
<classpath refid="compile.classpath" />
</javac>
<copy todir="${dist.dir}/${webAppName}/WEB-INF/lib">
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppName}/WEB-INF/classes">
<fileset dir="${resources.dir}">
<include name="**/*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppName}/css">
<fileset dir="${webroot.dir}/css">
<include name="**/*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppName}/images">
<fileset dir="${webroot.dir}/images">
<include name="**/*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppName}/jsp">
<fileset dir="${webroot.dir}/jsp">
<include name="**/*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppName}">
<fileset dir="${webroot.dir}">
<include name="*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppName}/WEB-INF">
<fileset dir="${webroot.dir}/WEB-INF">
<include name="*.*" />
</fileset>
</copy>
</target>
<target name="compileQA" description="Compile java sources" depends="compile">
<!-- compile main class -->
<javac debug="true" destdir="${dist.dir}/${webAppQAName}/WEB-INF/classes" includeAntRuntime="false" srcdir="${src.dir}">
<classpath refid="compile.classpath" />
</javac>
<javac debug="true" destdir="${dist.dir}/${webAppQAName}/WEB-INF/classes" includeAntRuntime="false" srcdir="${test.src.dir}">
<classpath refid="compile.classpath" />
</javac>
<copy todir="${dist.dir}/${webAppQAName}/WEB-INF/lib">
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppQAName}/WEB-INF/classes">
<fileset dir="${resources.dir}">
<include name="**/*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppQAName}/css">
<fileset dir="${webroot.dir}/css">
<include name="**/*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppQAName}/images">
<fileset dir="${webroot.dir}/images">
<include name="**/*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppQAName}/jsp">
<fileset dir="${webroot.dir}/jsp">
<include name="**/*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppQAName}">
<fileset dir="${webroot.dir}">
<include name="*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppQAName}/WEB-INF">
<fileset dir="${webroot.dir}/WEB-INF">
<include name="*.*" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppQAName}/WEB-INF/classes/org/sky/ssh/ut/ds">
<fileset dir="${test.src.dir}/org/sky/ssh/ut/ds">
<include name="*.xml" />
</fileset>
</copy>
<copy todir="${dist.dir}/${webAppQAName}/WEB-INF/classes/org/sky/ssh/ut/xmldata/student">
<fileset dir="${test.src.dir}/org/sky/ssh/ut/xmldata/student">
<include name="*.xml" />
</fileset>
</copy>
<antcall target="junitreport">
</antcall>
</target>
<target name="buildwar" depends="compileQA">
<war warfile="${dist.dir}/${webAppName}.war">
<fileset dir="${dist.dir}/${webAppName}" />
</war>
</target>
<target name="junitreport">
<junit printsummary="on" haltonfailure="false" failureproperty="tests.failed" showoutput="true">
<classpath>
<pathelement path="${dist.dir}/${webAppQAName}/WEB-INF/classes" />
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
<fileset dir="${ext-lib.dir}">
<include name="*.jar" />
</fileset>
</classpath>
<formatter type="xml" />
<batchtest todir="${report.dir}">
<fileset dir="${dist.dir}/${webAppQAName}/WEB-INF/classes">
<include name="org/sky/ssh/ut/Test*.*" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report.dir}">
<fileset dir="${report.dir}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="report" />
</junitreport>
<fail if="tests.failed">
---------------------------------------------------------
One or more tests failed, check the report for detail...
---------------------------------------------------------
</fail>
</target>
</project>
對照著上面的build的流程圖,很容易看懂
打開一個command窗口,進入到我們的工程的根目錄下,設置好ANT_HOME並將%ANT_HOME%in目錄加入到path中去,然後在工程的根據目錄下運行ant,就能看到打包和運行unit test的效果了。
build完後可以在工程的根目錄下找到一個report目錄,打開后里面有一堆的html文件
雙擊index.htm這個文件查看單元測試報告
我們在windows的資源管理器中打開我們的工程,在根目錄下有一個dist目錄,打開這個目錄,我們會開到兩個目錄與一個.war文件,它們分別是:
其中myssh2-UT是專門用來run unit test的,而myssh2是可以用於發布到production environment的,我們打開myssh2.war這個包,我們可以看到,由於這個是正確布署的war,因此裡面是不能夠含有unit test的相關類與方法的,完全按照上述的打包流程圖來做的。
系列
通向架構師的道路(第一天)之 Apache 整合 Tomcat
通向架構師的道路(第二天)之 apache tomcat https 應用
通向架構師的道路(第三天)之 apache 性能調優
通向架構師的道路(第四天)之 Tomcat 性能調優
通向架構師的道路(第五天)之 tomcat 集群 – 群貓亂舞
通向架構師的道路(第六天)之漫談基於資料庫的許可權系統的設計
通向架構師的道路(第七天)之漫談使用 ThreadLocal 改進你的層次的劃分
通向架構師的道路(第八天)之 Weblogic 與 Apache 的整合與調優
通向架構師的道路(第九天)之 Weblogic 的集群與配置
通向架構師的道路 ( 第十天 ) 之 Axis2 Web Service ( 一 )
通向架構師的道路 ( 第十一天 ) 之 Axis2 Web Service ( 二 )
通向架構師的道路 ( 第十二天 ) 之 Axis2 Web Service ( 三 )
通向架構師的道路 ( 第十三天 ) Axis2 Web Service 安全初步
通向架構師的道路 ( 第十四天 ) Axis2 Web Service 安全之 rampart
通向架構師的道路 ( 第十五天 ) IBM Websphere 的安裝與優化
通向架構師的道路 ( 第十六天 ) IBM Websphere 與 IBM HttpServer 的集成
通向架構師的道路 ( 第十七天 ) IBM Websphere 集群探秘 – WASND
通向架構師的道路(第十八天)萬能框架 Spring ( 一 )
通向架構師的道路(第二十天)萬能框架spring (二)
通向架構師的道路(第二十一天)萬能框架 Spring ( 三 ) 之 SSH
通向架構師的道路 ( 第二十二天 ) 萬能框架 spring ( 四 ) 使用 struts2
通向架構師的道路 ( 第二十三天 ) maven 與 ant 的奇妙整合
通向架構師的道路(第二十四天)之 Oracle 性能調優
看完本文有收穫?請轉發分享給更多人
關注「ImportNew」,提升Java技能
※通向架構師的道路(第二十一天)萬能框架 Spring ( 三 ) 之 SSH
※偵探劇場:堆內存神秘溢出事件
TAG:ImportNew |