??xml version="1.0" encoding="utf-8" standalone="yes"?> 自动试可以节省重复执行相同试步骤?
旉和精力。本文将介绍 jWebUnitQ这是一l?Java c,用于?Web 应用E序开发测试用例。jWebUnit 是一个开源项目,可以?
BSD 许可下免费获得。我介l如何下?jWebUnit 库、配|?Eclipse q_来开?jWebUnit
试用例Q以及如何构Z个示例测试用例?/p>
jWebUnit ?/span> ?HttpUnit q行试 清单 1 昄了一个用 HttpUnit ~写的测试用例,它试囑֍?HttpUnit 主页上的“Cookbook”链接Q?/p>
清单 1. 单击 HttpUnit 主页?Cookbook 链接?HttpUnit 代码 清单 1 中的代码? ?jWebUnit q行的测试更?/span> 如果没注意清?2 中特定于 JUnit 的代码,那么您可以看刎ͼ试用例现在变得相当整洁、简l。需要查看的重要的行是第 6 行、第 11 行和W?12 行。在W?6 行,基本 URL 被设|到 HttpUnit 的主中。第 11 行用相对路径 jWebUnit APIQ进一步观?/span> 另一个重要的cL 下蝲 jWebUnitQ在 Eclipse 中配|?jWebUnit 现在可以?Eclipse 中的 jWebUnit 目下开?jWebUnit 试用例了? 构徏CZ应用E序 现在试场景已经qAQ可以编?Java 应用E序Q用 jWebUnit 实现q些需求了?/p>
W一步是声明一个从 正如我在前面提到q的QjWebUnit 要求每个试用例都是? 下一步是声明入口?—? 接下来, 清单 5 把基?URL 讄? 现在开始浏览站点ƈ输入搜烦参数。清?6 昄了访?Web 面Q然后测试所有场景的代码Q?/p>
清单 6. 试所有场?/strong> 清单 6 的代码连接到基本 URLQƈ相对? q行CZ应用E序 CZ输出 正如在清?7 中可以看到的Q可以用 清单 8 执行时用 l束?/span> 首?/span> 本文假设读者已l熟悉单元测试及JUnit工具的用,如果对单元测试及JUnit不了解请先学习单元试?/span>JUnit工具的相关知识。读者最好对Spring框架?/span>Spring框架提供的单元测试支持有所了解Q因为本文案例基?/span>Spring技术编写。但?/span>Spring不了解ƈ不媄响本文所讲述的单元测试用例编写及回调模式、模板方法的应用?/span> 单元试是编写高质量代码的前提,通过~写有效的单元测试即可以保证代码的质量又可以提高开发速度Q因为大多数问题都可以通过单元试发现q解册不需要部|到应用服务器。纵览网上流行的优秀开源框Ӟ无一不提供完整的单元试用例?/span>Spring框架便是其中的代表和g者,因ؓSpring所遵@的控制反转(IoCQ和依赖注入Q?/span>DIQ原则ɾ~写有效、干净的单元测试用例变得更加方ѝ快捗?/span>
~写单元试用例 本文所采用的案例非常简单,是Ҏ据库表的增、删、改、查操作q行试。假设我们有q样一个表urlQ?/span>MySql数据库)Q?/span>
字段
cd
描述
id int 主键Q自增类?/font> url varchar |站地址Q唯一不能重复 email varchar Email地址 name varchar 名称 正如你所见,该表只有几个字段Q但对于我们的案例来说完全够用?/font>
看到此处Q你应该清楚我们是要Ҏ据库操作q行单元试。如果你是一位经验丰富的开发h员,此时已经会有许多疑问Q甚臛_l失ȝl阅L文的兴趣Q?/span> ² 单元试不应该直接操作数据库Q?/font> ² Ҏ据库操作的单元测试可以采?/span>DAO模式Q?/span>Mock一个实现类Q?/span> ² 使用内存数据库? ² 其他Q?/font>
数据库表有了Q我们接下来~写DAO及其实现c:
DAO接口Q?/span>
DAO实现c,该类l承一个支持类Q封装了Ҏ据库的操作?/span> DO领域对象
因ؓ本文案例使用Spring作ؓ底层框架Q因此这里需要编?/span>Spring配置文g?/span>DAOq行l装?/span> DAO及其配置文g都已l准备完毕,我们接下来编写测试用例?/span>Spring为单元测试提供了很多有用的支持类Q我们在q里使用的是Q?/span>
org.springframework.test.AbstractDependencyInjectionSpringContextTests
该类提供?/span>POJO属性自动注入的能力Q只要ؓZ的属性字D|供一?/span>SetҎ卛_。下面我们来看完整的试用例Q?/span> getConfigLocations()Ҏ?/span>AbstractDependencyInjectionSpringContextTests 提供配置Q?/span>Spring会根据该配置文g自动注入bizUrlDAO属性?/span>testInsertQ)Ҏ用于试插入新数据,注意q里有个问题Q如果数据库中已l存在该URL的记录,则应用会报错Q所以这里还要进行数据清除准备处理,我们UC?#8220;试环境准备”Q以后会用到该名词;testDuplicateInsertQ)Ҏ用于试插入重复数据的情况,该方法同样存在上面的问题Q?/span>testDelete()Ҏ用于试删除数据的情况,q里管准备了数据,但仍没有考虑数据库中已经有记录的情况?
Callback Function & Template Method Pattern
关于本节是否要详l介l回调函敎ͼCallback FunctionQ和模板ҎQ?/span>Template MethodQ模式的问题Q笔者考虑了很长时间。因为网l上对这两种普遍使用的设计模式的定义层出不穷Q各有各的道理,很难说谁是谁非。况且,针对不同的应用场景,q两U模式也有许多变体,或者联合用?/span> 因此Q笔者最l决定不在此处对q两U模式做M定义或引用,误者自行参阅相x档资料?/font> 回调函数和模板方法模式在单元试中的应用
首先Q让我们单ȝ一下前面测试用例的问题Q?/font> 一?/font> 抽象层次太低Q不够通用Q?/span> 例如Q对?/font>getConfigLocationsQ)ҎQ我们完全可以放C个父cM实现Q因为对于一个项目而言Q其配置文g大多都是l一的,没有必要在没有测试类中都定义该方法?/font> 如图所C,我们提炼了一个抽象支持类Q实CgetConfigLocationsQ)ҎQ同时还提供?/font>getCustomConfigLocations()Ҏ供子cM用,子类可以通过重蝲该方法提供定制的配置?/font>
有了该支持类Q具体测试类只需要承该cdƈ~写试逻辑卛_?/font> 二?/font> ~少准备试环境和清除测试数据的环节Q?/span>
?
于大多数试用例Q可能都会涉及到初始化数据和清除试数据的问题,最典型的就是数据库操作Q这也是本文采用数据库操作作为案例的原因。那么如何实现呢Q?
很显然在每个试Ҏ中都~写准备环境和清除测试数据的代码是不合适的Q因为大多数时候对于一个测试类而言Q准备环境和清除数据的逻辑都是一L。聪明的
你一定会惛_定义两个ҎQ一个初始化环境Q一个清除测试数据。是的,是q样Q?/font> 如你所见,我们在这里定义了setupEnv()?/span>cleanEnv()两个ҎQ分别用于初始化环境和清除测试数据,然后在测试方法开始和l束时分别调用这两个Ҏ。这的确辑ֈ了我们的目的Q不用在每个试Ҏ中都~写初始化和清除逻辑Q但此时你一定发现在每个试Ҏ前后都调?/span>setupEnv()?/span>cleanEnv()也很不爽Q那说明我们的抽象程度还不够Q那么该如何做的更好呢?
q里该到模板ҎQ?/span>Template MethodQ模式发挥威力的时候了。我们将使用模板Ҏ来l重构前面的案例。让我们先来定义一个方法: 相比之前的方法,我们q里已经有了一些进步,定义了一?/span>executeҎQ在该方法开始和l束分别执行初始化和清除逻辑Q然后由doTestLogic()Ҏ实现试逻辑。实际测试方法中只要执行executeҎQƈ传入试逻辑可以了。瞧Q不l意间我们已l实C模板Ҏ模式—?strong>把通用的逻辑{hQ变化的部分由具体方法提?/strong>。怎么Q不怿么?呵呵Q设计模式其实ƈ不复杂,是前h解决通用问题的一些最佛_跉|ȝ而已?/span> 此时你可能会_TeseCasecdl提供了setUp()?/span>tearDown()Ҏ来做qg事情Q我也想CQ哈哈!但这q不和本文生冲H!
jWebUnit ?HttpUnitQ一个进?Web 应用E序自动试?Java 库)?JUnit 单元试框架为基Q请参阅 参考资?/a>Q?
jWebUnit 提供了导?Web 应用E序的高U?
APIQƈl合了一l断aQ用它们来验证链接导航、表单输入项和提交、表格内容以及其他典型商?Web 应用E序Ҏ的正确性?jWebUnit ?
JAR 文g形式提供的,可以很容易地它插入大多?IDE 中,jWebUnit 也包含其他必要的库?/p>
?
Web 应用E序自动q行试意味着跌 Web 览器,通过E序来处?Web 站点。首先,我要介绍 HttpUnitQJWebUnit
的构建块之一Q是如何化这工作的。HttpUnit 可以模拟帧、JavaScript、页面重定向 cookieQ等{。在?HttpUnit
用于 JUnit Ӟ它可以迅速地?Web 站点的功能进行验证?/p>
1 public class HttpUnitTest {
2 public static void main(String[] args) {
3 try {
4 WebConversation wc = new WebConversation();
5 WebRequest request =
new GetMethodWebRequest("http://httpunit.sourceforge.net/index.html");
6 wc.setProxyServer( "your.proxy.com", 80 );
7 WebResponse response = wc.getResponse(request);
8 WebLink httpunitLink =
response.getFirstMatchingLink(WebLink.MATCH_CONTAINED_TEXT,"Cookbook");
9 response = httpunitLink.click();
10 System.out.println("Test successful !!");
11 } catch (Exception e) {
12 System.err.println("Exception: " + e);
13 }
14 }
15 }
your.proxy.com
Q第 6 行)q接 Internet。如果存在直?Internet q接Q那么可以把q个语句注释掉。第 8 行的语句在页面中搜烦包含文本 Cookbook ?Web 链接。第 9 行的语句用于单击q个链接。如果找到链接,那么用户会看?Test Successful !!
q条消息?/p>
清单 2 的测试用例用 jWebUnit API 执行和清?1 相同的Q务:
1 public class JWebUnitTest extends WebTestCase{
2 public static void main(String[] args){
3 junit.textui.TestRunner.run(new TestSuite(JWebUnitTest.class));
4 }
5 public void setUp(){
6 getTestContext().setBaseUrl("http://httpunit.sourceforge.net");
7 getTestContext().setProxyName("webproxy.watson.ibm.com");
8 getTestContext().setProxyPort(8080);
9 }
10 public void testSearch(){
11 beginAt("/index.html");
12 clickLinkWithText("Cookbook");
13 }
14 }
/index.html
q接站点。第 12 行用于单击页面上h文本 Cookbook 的链接。如果链接有效,那么 JUnit 会报告成功;否则QJUnit 会报告异常?/p>
每个 jWebUnit 试的核心都?net.sourceforge.jwebunit.WebTestCase
c,它代表测试用例。每个测试用例都必须是从q个cL展而来。(net.sourceforge.jwebunit.WebTestCase
cLw则是从 junit.framework.TestCase
cL展而来的,它在 JUnit 中代表测试用例。) ?1 描述了这个类的一些常用方法:
Ҏ
说明
public TestContext getTestContext()
得到试用例的上下文。可以用它访问像地区、基?URL ?cookie 之类的项?/td>
public void beginAt(String relativeURL)
在相对于基本 URL ?URL 处开始对?/td>
public void setWorkingForm(String nameOrId)
与指定的表单开始交互。如果当前页面只有一个表单,׃需要调用这个方?/td>
protected void submit()
提交表单 —?{同于单击表单的 提交 按钮
public void gotoFrame(String frameName)
Ȁzd名
net.sourceforge.jwebunit.TestContext
。它为测试创Z下文。可以用q个cL处理?cookie、会话和授权之类的信息。表 2 昄了这个类的一些重要方法:
Ҏ
说明
public void addCookie(String name, String value)
向测试上下文中添?cookie。在 HttpUnitDialog
开始时Q添加的 cookie 被设|到 WebConversation
?/td>
public void setResourceBundleName(String name)
为测试上下文讄一个用的资源l定。用于按? WebTester
中的键查找期望的?/td>
public void setProxyName(String proxyName)
为测试上下文讄代理服务器名U?/td>
public void setBaseUrl(String url)
为测试上下文讄基本 URL
jWebUnit 是用U?Java 代码实现的,所以可以以 JAR 文g的Ş式获得它 Q请参阅 参考资?/a>Q从中获得下载链接)。在完成下蝲之后Q请按以下步骤在 Eclipse q_上配|?jWebUnit 库:
现在可以查?jWebUnit API 的实际应用了。我带您研I一个示例应用程序,帮助您更好地理解 jWebUnit 的真正威力。这个应用程序是一个测试用例,用于打开一?Google 搜烦面q搜索文?HttpUnit。应用程序需要测试以下场景:
q
的表单元素。(?Google 的主上Q名?q
的文本框是接受用h询输入的文本框。)应用E序用这个元素输入搜索参数?br />
HttpUnit Home
Qƈ提交表单?br />
WebTestCase
扩展而来的类Q如清单 3 所C:
public class GoogleTest extends WebTestCase {
static String searchLink = "";
}
WebTestCase
中扩展而来的?code>searchLink 保存传入的搜索参数。这个g命o行参数的形式传递给试用例?/p>
main()
ҎQ如清单 4 所C: main()
Ҏ
public static void main(String[] args) {
searchLink = args[0];
junit.textui.TestRunner.run(new TestSuite(GoogleTest.class));
}
main()
Ҏ调用 junit.textui.TestRunner.run()
执行 JTest
试用例。因为需要运?GoogleTest
试用例Q所以,作ؓ参数传递给 run()
Ҏ的测试套仉?GoogleTest.class
作ؓ参数?/p>
JTest
调用 setUp()
Ҏ来设|基?URL 和代理,如清?5 所C:
public void setUp() {
getTestContext().setBaseUrl("http://www.google.com");
getTestContext().setProxyName("proxy.host.com");
getTestContext().setProxyPort(80);
}
http://www.google.com
。这意味着试用例的启动是相对于这?URL 的。下面两个语句设|连接到 Internet 的代理主机和代理端口。如果是直接q接?InternetQ那么可以忽略代理设|语句?/p>
public void testSearch() {
beginAt("/");
assertFormElementPresent("q");
setFormElement("q", "HttpUnit");
submit("btnG");
assertLinkPresentWithText(searchLink);
clickLinkWithText(searchLink);
}
/
开始浏览。然后它断定面中包含一个名?q
的表单元?—?q ?Google 主页上查询输入文本框的名U。下一条语句用?HttpUnit
讄名ؓ q
的文本框。再下一条语a提交表单上名?btnG
的提交按钮。(?Google 的主上Q名?btnG
的按钮是标签?Google Search 的按钮。)表单是在q个对话中提交的Q下一列出搜索结果。在l果面上,代码首先查是否有一个链接的文本?HttpUnit Home。如果该链接不存在,那么试׃ AssertionFailedError
p|。如果该链接存在Q则试执行的下一个操作是单击链接?
现在把示例应用程序投入用当中:
JAR_BASE
指向包含所有必需 JAR 文g的目录。例如,如果?C:\temp 中释?jwebunit-1.2.zip 文gQ那么将 JAR_BASE 讄?C:\temp\jwebunit-1.2\lib?br />
setclasspath.bat
。这会设|执行测试用例所需?CLASSPATH
?br />
java com.jweb.test.GoogleTest "HttpUnit Home"
q行应用E序?/li>
在执行了试用例之后Q会在命令行输出一个测试用例报告。如果测试失败,报告看v来如清单 7 中所C:
C:\temp>java com.jweb.test.GoogleTest "HttpUnit Hwee"
.F
Time: 5.338
There was 1 failure:
1) testSearch(com.jweb.test.GoogleTest)junit.framework.AssertionFailedError: Link
with text [HttpUnit Hwee] not found in response.
at net.sourceforge.jwebunit.WebTester.assertLinkPresentWithText(WebTester.java:618)
at net.sourceforge.jwebunit.WebTestCase.assertLinkPresentWithText(WebTestCase.java:244)
at com.jweb.test.GoogleTest.testSearch(GoogleTest.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at com.jweb.test.GoogleTest.main(GoogleTest.java:19)
FAILURES!!!
Tests run: 1, Failures: 1, Errors: 0
HttpUnit Hwee
作ؓ参数来执行测试用例。这个测试用例遇到断a的地方会p|Q因为结果页面中不包含带有这个文本的链接。由此也׃生了junit.framework.AssertionFailedError
?/p>
HttpUnit Home
作ؓ参数。测试用例找C个带有这个文本的链接Q所以测试通过了:
C:\temp>java com.jweb.test.GoogleTest "HttpUnit Home"
.
Time: 6.991
OK (1 test)
?
文通过讨论 jWebUnit 框架的一些突出特性和最重要的类Q介l如何用它创建简z的试用例Q让您对 jWebUnit
框架有一个认识。jWebUnit q有更多Ҏ可以用在测试用例中。它支持试 Web
面中的链接行数。可以对字符丌Ӏ表或者带有指定标{表单输入元素是否存在于页面上q行断言。此外,jWebUnit q可以处?cookie
Q例如断a存在某个 cookie、删?cookie {。)试可以Ҏ个文本之后出现的特定文本的链接进行单凅R?如果想ؓ Web
应用E序构徏快而有效的试用例QjWebUnit 可能是您最好的朋友?/p>
]]>
]]>
/**
* @author tao.youzt
*/
public interface BizUrlDAO {
public Object insert(BizUrlDO bizUrlDO);
public int delete(String url);
public BizUrlDO getByUrl(String url);
}
/**
* @author tao.youzt
*/
public class BizUrlIbatisImpl extends GodzillaDaoSupport implements BizUrlDAO {
private static final String GET_BY_URL = "Select-BIZ-URL";
private static final String Delete = "Delete-BIZ-URL";
private static final String Insert = "Insert-BIZ-URL";
public int delete(String url) {
return this.delete(Delete, url);
}
public BizUrlDO getByUrl(String url) {
return this.queryForObject(GET_BY_URL, url, BizUrlDO.class);
}
public Object insert(BizUrlDO bizUrlDO) {
return this.insert(Insert, bizUrlDO);
}
}
/**
* @author tao.youzt
*/
public class BizUrlDO {
private int id;
private String url;
private String email;
private String name;
// getter and setter
}
/**
* @author tao.youzt
*/
public class TestBizUrlDAO extends AbstractDependencyInjectionSpringContextTests {
private BizUrlDAO bizUrlDAO;
@Override
protected String[] getConfigLocations() {
return new String[]{"godzilla-dao.xml","godzilla-db.xml"};
}
public void testInsert(){
bizUrlDAO.insert(generateDO());
assertNotNull(bizUrlDAO.getByUrl("www.easyjf.com"));
}
public void testDuplicateInsert(){
bizUrlDAO.insert(generateDO());
try{
bizUrlDAO.insert(generateDO());
assertFalse("Must throw an exception!",true);
}catch(Exception e){
assertTrue(true);
}
}
public void testDelete(){
bizUrlDAO.insert(generateDO());
assertNotNull(bizUrlDAO.getByUrl("www.easyjf.com"));
bizUrlDAO.delete("www.easyjf.com");
assertNull(bizUrlDAO.getByUrl("www.easyjf.com"));
}
private BizUrlSynchronizeDO generateDO() {
BizUrlDO bizUrlDO = new BizUrlDO();
bizUrlDO.setUrl("www.easyjf.com");
bizUrlDO.setName("EasyJWeb");
bizUrlDO.setEmail("webmaster@easyjf.com");
return bizUrlDO;
}
public void setBizUrlDAO(BizUrlSynchronzieDAO bizUrlDAO) {
this.bizUrlDAO = bizUrlDAO;
}
}
/**
* DAL层测试支持类.
*
*
* 除非Ҏ情况,所有DAO都要l承此类.
*
* @author tao.youzt
*/
public abstract class GodzillaDalTestSupport extends AbstractDependencyInjectionSpringContextTests {
/*
* @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#getConfigLocations()
*/
@Override
protected final String[] getConfigLocations() {
String[] configLocations = null;
String[] customConfigLocations = getCustomConfigLocations();
if (customConfigLocations != null && customConfigLocations.length > 0) {
configLocations = new String[customConfigLocations.length + 2];
configLocations[0] = "classpath:godzilla/dal/godzilla-db-test.xml";
configLocations[1] = "classpath:godzilla/dal/godzilla-dao.xml";
for (int i = 2; i < configLocations.length; i++) {
configLocations[i] = customConfigLocations[i - 2];
}
return configLocations;
} else {
return new String[] { "classpath:godzilla/dal/godzilla-db-test.xml",
"classpath:godzilla/dal/godzilla-dao.xml" };
}
}
/**
* 子类可以覆盖该方法加载个性化配置.
*
* @return
*/
protected String[] getCustomConfigLocations() {
return null;
}
}
/**
* @author tao.youzt
*/
public class TestBizUrlDAO extends AbstractDependencyInjectionSpringContextTests {
private BizUrlDAO bizUrlDAO;
@Override
protected String[] getConfigLocations() {
return new String[]{"godzilla-dao.xml","godzilla-db.xml"};
}
protected void setupEnv(){
bizUrlDAO.delete("www.easyjf.com");
}
protected void cleanEnv(){
bizUrlDAO.delete("www.easyjf.com");
}
public void testTemp(){
setupEnv();
bizUrlDAO.insert(generateDO());
assertNotNull(bizUrlDAO.getByUrl("www.easyjf.com"));
setupEnv();
}
}
/**
* @author tao.youzt
*/
public class TestBizUrlDAO extends AbstractDependencyInjectionSpringContextTests {
private BizUrlDAO bizUrlDAO;
@Override
protected String[] getConfigLocations() {
return new String[]{"godzilla-dao.xml","godzilla-db.xml"};
}
protected void setupEnv(){
bizUrlDAO.delete("www.easyjf.com");
}
protected void cleanEnv(){
bizUrlDAO.delete("www.easyjf.com");
}
public void testTemp(){
//do test logic in this method
execute();
}
protected void execute(){
setupEnv();
doTestLogic();
setupEnv();
}
}
可能此时此刻你已l想刎ͼ本文另一个重要概念——回调方法模式还没有用到Q是不是该用该模式了?没错Q就是它了!我先把代码给出,然后再详l解释?/font>
我们提供了一个抽象类TestExecutorQƈ定义一个抽象的executeҎQ然后ؓ试cȝexecuteҎ传入一?/span>TestExecutor的实例,q调用该实例?/span>executeҎ。最后,我们的测试方法中只需?/span>new一?/span>TestExecutorQƈ?/span>executeҎ中实现测试逻辑Q便可以按照预期的方式执行:准备试环境-执行试逻辑-清除试数据。这便是一个典型的回调Ҏ模式的应用!
模板Ҏ和回调函数模式说h挺悬Q其实也p么简单,明白了吧Q)
三?/font> 如何为每个测试方法单独提供环境方法呢Q?/span>
通过前面的讲解,怿大家Ҏ板方法和回调函数模式都已l掌握了Q这里直接给出相关代码:
/** * DAL层测试支持类. * * * 除非Ҏ情况,所有DAO都要l承此类. * * @author tao.youzt */ public abstract class GodzillaDalTestSupport extends AbstractDependencyInjectionSpringContextTests { /* * @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#getConfigLocations() */ @Override protected final String[] getConfigLocations() { String[] configLocations = null; String[] customConfigLocations = getCustomConfigLocations(); if (customConfigLocations != null && customConfigLocations.length > 0) { configLocations = new String[customConfigLocations.length + 2]; configLocations[0] = "classpath:godzilla/dal/godzilla-db-test.xml"; configLocations[1] = "classpath:godzilla/dal/godzilla-dao.xml"; for (int i = 2; i < configLocations.length; i++) { configLocations[i] = customConfigLocations[i - 2]; } return configLocations; } else { return new String[] { "classpath:godzilla/dal/godzilla-db-test.xml", "classpath:godzilla/dal/godzilla-dao.xml" }; } } /** * 子类可以覆盖该方法加载个性化配置. * * @return */ protected String[] getCustomConfigLocations() { return null; } /** * 准备试环境. */ protected void setupEnv() { } /** * 清除试数据. */ protected void cleanEvn() { } /** * 试用例执行? */ protected abstract class TestExecutor { /** * 准备试环境 */ public void setupEnv() { } /** * 执行试用例. */ public abstract void execute(); /** * 清除试数据. */ public void cleanEnv() { } } /** * 执行一个测试用? * * @param executor */ protected final void execute(final TestExecutor executor) { execute(IgnoralType.NONE, executor); } /** * 执行一个测试用? * * @param executor */ protected final void execute(final IgnoralType ignoral, final TestExecutor executor) { switch (ignoral) { case NONE: { setupEnv(); executor.setupEnv(); executor.execute(); executor.cleanEnv(); cleanEvn(); break; } case BOTH: { executor.execute(); break; } case GLOBAL: { executor.setupEnv(); executor.execute(); executor.cleanEnv(); break; } case LOCAL: { setupEnv(); executor.execute(); cleanEvn(); break; } case GLOBAL_S: { executor.setupEnv(); executor.execute(); executor.cleanEnv(); cleanEvn(); break; } case GLOBAL_C: { setupEnv(); executor.setupEnv(); executor.execute(); executor.cleanEnv(); break; } case LOCAL_S: { setupEnv(); executor.execute(); executor.cleanEnv(); cleanEvn(); break; } case LOCAL_C: { setupEnv(); executor.setupEnv(); executor.execute(); cleanEvn(); break; } case BOTH_SETUP: { executor.execute(); executor.cleanEnv(); cleanEvn(); break; } case BOTH_CLEAN: { setupEnv(); executor.setupEnv(); executor.execute(); break; } case GLOBAL_S_LOCAL_C: { executor.setupEnv(); executor.execute(); cleanEvn(); break; } case GLOBAL_C_LOCAL_S: { setupEnv(); executor.execute(); executor.cleanEnv(); break; } } } /** * 忽略cdEnum. */ public enum IgnoralType { /** 不忽略Q何环境相x?*/ NONE, /** 忽略全局环境相关Ҏ */ GLOBAL, /** 忽略局部环境相x?*/ LOCAL, /** 忽略所有环境相x?*/ BOTH, /** 忽略全局准备试环境Ҏ */ GLOBAL_S, /** 忽略全局清除试数据Ҏ */ GLOBAL_C, /** 忽略局部准备测试环境方?*/ LOCAL_S, /** 忽略局部清除测试数据方?*/ LOCAL_C, /** 忽略全部准备试环境Ҏ */ BOTH_SETUP, /** 忽略全部清楚试数据Ҏ */ BOTH_CLEAN, /** 忽略全局准备试环境和局部清除测试数据方?*/ GLOBAL_S_LOCAL_C, /** 忽略全局清除试数据和局部准备测试环境方?*/ GLOBAL_C_LOCAL_S } }
/** * URL DAO试c? * * @author tao.youzt */ public class TestBizUrlDAO extends GodzillaDalTestSupport { private BizUrlDAO bizUrlDAO; @Override protected void setupEnv() { bizUrlDAO.delete("www.easyjf.com"); } @Override protected void cleanEvn() { bizUrlDAO.delete("www.easyjf.com"); } /** * 试插入一条新数据. */ public void testInsert() { execute(new TestExecutor() { @Override public void execute() { bizUrlDAO.insert(generateDO()); assertNotNull(bizUrlDAO.getByUrl("www.easyjf.com")); } }); } /** * 试重复插入数据的情? */ public void testDuplicateInsert() { execute(new TestExecutor() { @Override public void setupEnv() { bizUrlDAO.insert(generateDO()); } @Override public void execute() { try { bizUrlDAO.insert(generateDO()); assertTrue("Must throw an exception!", false); } catch (Exception e) { assertTrue("Expect this exception.", true); } } }); } /** * 试删除一条已l存在的数据. */ public void testDelete() { execute(IgnoralType.GLOBAL_C, new TestExecutor() { @Override public void execute() { assertNotNull(bizUrlDAO.getByUrl("www.easyjf.com")); bizUrlDAO.delete("www.easyjf.com"); assertNull(bizUrlDAO.getByUrl("www.easyjf.com")); } @Override public void setupEnv() { bizUrlDAO.insert(generateDO()); } }); } /** * 生成一个用于测试的DO. * * @return */ private BizUrlSynchronizeDO generateDO() { BizUrlDO bizUrlDO = new BizUrlDO(); bizUrlDO.setUrl("www.easyjf.com"); bizUrlDO.setName("EasyJWeb"); bizUrlDO.setEmail("webmaster@easyjf.com"); return bizUrlDO; } public void setBizUrlDAO(BizUrlSynchronzieDAO bizUrlDAO) { this.bizUrlDAO = bizUrlDAO; } }
注意testDeleate()ҎQ我们传入了两个参数Q第一个参?/span>IgnoralType.GLOBAL_C 代表忽略哪个ҎQ有12U类型可以设|?/span>GLOBAL_C代表忽略全局的清除测试数据方法,其他见代码注释?/span>