`
suhenhappy
  • 浏览: 58664 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
文章分类
社区版块
存档分类
最新评论

通向架构师的道路(第二十五天)SSH的单元测试与dbunit的整合

 
阅读更多



一、前言

在二十三天中我们介绍了使用maven来下载工程的依赖库文件,用ant来进行war包的建立。今天我们在这个基础上将使用junit+dbunit来进行带有单元测试报告的框架的架构。

目标:

  1. 每次打包之前自动进行单元测试并生成单元测试报告
  2. 生成要布署的打包文件即war包
  3. 单元测试的代码不能够被打在正式的要布署的war包内,单元测试仅用于unit test用
  4. 使用模拟数据对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,因此我们每天的打包要干的事情应该是:
  1. 程序员check in代码时必须把相关的unit test也check in源码服务器
  2. 次日的零晨由持续集成构件如:cruisecontrol自动根据设好的schedule把所有的源码服务器的代码进行编译
  3. 运行单元测试
  4. 生成报告
  5. 打包布署到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这个选项栏


  1. 单击user Entries
  2. 单击Advanced按钮
  3. 在弹出框中选择Add Folders
  4. 点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());
	}
}
  1. 该测试方法每次都清空t_student表
  2. 往t_student表里注入5条数据
  3. 运行业务方法getAllStudent
  4. 比较getAllStudent方法返回的list里的size是否为5
  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
resources.dir=${local.dir}/src/main/resources

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%\bin目录加入到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的相关类与方法的,完全按照上述的打包流程图来做的。

结束今天的教程!!!

分享到:
评论

相关推荐

    通向架构师的道路(第二十五天)SSH的单元测试与dbunit的整合.docx

    SSH单元测试与DbUnit整合 在软件开发过程中,单元测试是一个非常重要的步骤,它可以确保代码的正确性和可靠性。今天,我们将讨论如何使用JUnit和DbUnit进行单元测试,并将其与SSH整合。 SSH单元测试 在SSH项目中...

    通向架构师的道路(第二十五天)SSH的单元测试与dbunit的整合的例子

    在IT行业中,尤其是在软件开发领域,单元测试是保证代码质量、可维护性和可扩展性的重要...在实际项目中,一个良好的测试策略能够帮助我们更快地发现和修复问题,提高开发效率,这也是通向架构师道路的关键步骤之一。

    通向架构师的道路.rar

    8. **通向架构师的道路(第二十五天)SSH的单元测试与dbunit的整合.docx** 单元测试是保证代码质量的重要手段,而DbUnit则支持数据库的单元测试。文档可能讲述了如何使用JUnit、Mockito等工具,结合DbUnit进行SSH...

    通向架构师的道路

    而SSH的单元测试与dbunit的整合,则是保证代码质量与系统稳定性的有效手段。 最后,文章提到了架构与设计文档的写作技巧,以及IBM网格计算和企业批处理任务架构,这些内容提升了架构师的软技能,有助于更好地规划和...

    DBUnit 进行单元测试

    DBUnit 是一个强大的Java库,专门用于数据库的单元测试。它与JUnit和其他测试框架结合使用,可以帮助开发者在数据库层面上确保代码的正确性。DBUnit 提供了一种结构化的方式来导入和导出数据库数据,使得测试环境...

    单元测试JUnit4和DbUnit

    DbUnit则是一个专门用于数据库单元测试的工具,它允许开发者在测试前后对数据库的状态进行操作,如填充测试数据、清理数据等,以保证每次测试都在一致的环境中进行。 首先,了解JUnit4的基础知识至关重要。JUnit4...

    unitils整合dbunit利用excel进行单元测试

    unitils整合dbunit利用excel进行单元测试 包含mock以及整合spring进行测试

    Junit+dbunit单元测试jar包

    `dbunit` 是一个与`Junit`配合使用的扩展库,专注于数据库的单元测试。它允许开发者在测试前后填充或清理数据库数据,确保每次测试都在一致的环境中进行。`dbunit` 提供了一种名为“数据集”的概念,数据集是一个XML...

    dbunit-2.2.3-prj.rar_DbUnit 2.2_dbunit_单元测试

    DbUnit 是一个专门为Java开发人员设计的开源工具,主要用于数据库系统的单元测试。它与JUnit紧密集成,使得在执行单元测试时能够对数据库的状态进行精确控制,确保测试的可重复性和可靠性。DbUnit 2.2.3 版本是这个...

    spring与dbunit集成测试

    本文将深入探讨如何将Spring与DBUnit整合,以实现高效、可靠的数据库集成测试。 首先,理解Spring的核心功能是至关重要的。Spring是一个开源的Java平台,它为构建应用程序提供了全面的支持,包括依赖注入...

    dbunit单元测试

    DBUnit 是一个强大的开源工具,专门用于数据库的单元测试,它是JUnit框架的一个扩展,使得开发者在进行测试时能够更有效地管理和控制数据库的状态。这个工具的主要目的是确保测试的隔离性和可重复性,使得每次测试都...

    用DbUnit进行SqlMap单元测试

    DbUnit 是一个开源工具,专为数据库驱动的代码单元测试设计。它允许开发者精确地控制测试环境中的数据库状态,确保测试的独立性和可重复性。在单元测试中,特别是当测试对象涉及到数据库操作,如 DAO(Data Access ...

    DBUnit与H2内存数据库结合(单元测试)

    DBUnit与H2内存数据库结合是进行单元测试的一种高效方法,尤其在开发Java应用程序时,它可以帮助开发者确保数据层的功能正确性。这篇文章将详细介绍如何利用DBUnit和H2内存数据库来构建单元测试环境。 首先,DBUnit...

    主题:在Spring中结合Dbunit对Dao进行集成单元测试

    在Spring框架中,进行Dao层的集成单元测试时,常常会结合Dbunit工具来实现数据库相关的测试。Dbunit是一款强大的Java库,它扩展了JUnit,为数据库提供了结构化数据的导入和导出,使得测试更加方便和可靠。下面将详细...

    使用dbunit测试数据库

    DbUnit 是一个开源的 Java 库,专门设计用于数据库的单元测试。它扩展了流行的单元测试框架 JUnit,使得在测试代码与数据库交互时能够更好地控制数据库的状态。通过 DbUnit,开发者可以设置和清理测试环境中的数据库...

    基于DbUnit的单元测试框架设计

    DbUnit是一款开源的数据库功能测试框架,使用它可以对数据库的基本操作进行白盒单元测试,对输入输出进行校验,从而保证数据的有效性。DbUnit使用XML文件提供测试数据,为测试人员增加了测试难度,降低了单元测试效率。...

    dbunit测试demo

    DBUnit 是一个 Java 编程语言的开源工具,专门用于数据库的单元测试。它与JUnit 结合使用,提供了一种结构化的方法来设置和验证数据库的状态,确保代码的正确性。在进行数据库驱动的应用程序开发时,DBUnit 可以帮助...

    数据库操作的单元测试

    #### 二、数据库单元测试测试流程介绍 **2.1 数据库单元测试的原因** 数据库单元测试对于确保应用程序的正确性和稳定性至关重要。它可以帮助发现早期阶段的问题,减少后期修复的成本,并提高整体代码质量。 **2.2...

Global site tag (gtag.js) - Google Analytics