??xml version="1.0" encoding="utf-8" standalone="yes"?>欧美激情精品,一本久久知道综合久久,97色伦图片97色伦在线电影http://www.aygfsteel.com/ashutc/category/46477.htmlj2eezh-cnWed, 29 Sep 2010 22:33:43 GMTWed, 29 Sep 2010 22:33:43 GMT60jWebUnit 框架 试用例http://www.aygfsteel.com/ashutc/archive/2010/09/29/333401.html西瓜西瓜Wed, 29 Sep 2010 07:40:00 GMThttp://www.aygfsteel.com/ashutc/archive/2010/09/29/333401.htmlhttp://www.aygfsteel.com/ashutc/comments/333401.htmlhttp://www.aygfsteel.com/ashutc/archive/2010/09/29/333401.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/333401.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/333401.html

自动试可以节省重复执行相同试步骤? 旉和精力。本文将介绍 jWebUnitQ这是一l?Java c,用于?Web 应用E序开发测试用例。jWebUnit 是一个开源项目,可以? BSD 许可下免费获得。我介l如何下?jWebUnit 库、配|?Eclipse q_来开?jWebUnit 试用例Q以及如何构Z个示例测试用例?/p>

jWebUnit ?/span>
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>

?HttpUnit q行试
? Web 应用E序自动q行试意味着跌 Web 览器,通过E序来处?Web 站点。首先,我要介绍 HttpUnitQJWebUnit 的构建块之一Q是如何化这工作的。HttpUnit 可以模拟帧、JavaScript、页面重定向 cookieQ等{。在?HttpUnit 用于 JUnit Ӟ它可以迅速地?Web 站点的功能进行验证?/p>

清单 1 昄了一个用 HttpUnit ~写的测试用例,它试囑֍?HttpUnit 主页上的“Cookbook”链接Q?/p> 清单 1. 单击 HttpUnit 主页?Cookbook 链接?HttpUnit 代码

            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 }
            

清单 1 中的代码?your.proxy.com Q第 6 行)q接 Internet。如果存在直?Internet q接Q那么可以把q个语句注释掉。第 8 行的语句在页面中搜烦包含文本 Cookbook ?Web 链接。第 9 行的语句用于单击q个链接。如果找到链接,那么用户会看?Test Successful !! q条消息?/p>

?jWebUnit q行的测试更?/span>
清单 2 的测试用例用 jWebUnit API 执行和清?1 相同的Q务:

清单 2. 单击 HttpUnit 主页?Cookbook 链接?jWebUnit 代码

            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 }
            

如果没注意清?2 中特定于 JUnit 的代码,那么您可以看刎ͼ试用例现在变得相当整洁、简l。需要查看的重要的行是第 6 行、第 11 行和W?12 行。在W?6 行,基本 URL 被设|到 HttpUnit 的主中。第 11 行用相对路径 /index.html q接站点。第 12 行用于单击页面上h文本 Cookbook 的链接。如果链接有效,那么 JUnit 会报告成功;否则QJUnit 会报告异常?/p>

jWebUnit APIQ进一步观?/span>
每个 jWebUnit 试的核心都?net.sourceforge.jwebunit.WebTestCase c,它代表测试用例。每个测试用例都必须是从q个cL展而来。(net.sourceforge.jwebunit.WebTestCase cLw则是从 junit.framework.TestCase cL展而来的,它在 JUnit 中代表测试用例。) ?1 描述了这个类的一些常用方法:

?1. net.sourceforge.jwebunit.WebTestCase cȝ重要Ҏ
Ҏ 说明
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名

另一个重要的cL net.sourceforge.jwebunit.TestContext。它为测试创Z下文。可以用q个cL处理?cookie、会话和授权之类的信息。表 2 昄了这个类的一些重要方法:

?2. net.sourceforge.jwebunit.TestContext cȝ重要Ҏ
Ҏ 说明
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

下蝲 jWebUnitQ在 Eclipse 中配|?jWebUnit
jWebUnit 是用U?Java 代码实现的,所以可以以 JAR 文g的Ş式获得它 Q请参阅 参考资?/a>Q从中获得下载链接)。在完成下蝲之后Q请按以下步骤在 Eclipse q_上配|?jWebUnit 库:

  1. 把下载的文g jwebunit-1.2.zip 释放C时目录中Q假设是 C:\tempQ?/li>
  2. ?Eclipse 中创建新 Java 目Q将其命名ؓ jWebUnit?/li>
  3. 叛_ Package Explorer 视图中的 jWebUnit 目Q然后选择 Properties?/li>
  4. 单击 Java Build Path。单?Libraries 标签中的 Add External JARs?/li>
  5. 览?C:\temp\jwebunit-1.2\lib 目录Q选择q个目录中的所?JAR 文g?/li>
  6. 单击 OK?/li>

现在可以?Eclipse 中的 jWebUnit 目下开?jWebUnit 试用例了?

构徏CZ应用E序
现在可以查?jWebUnit API 的实际应用了。我带您研I一个示例应用程序,帮助您更好地理解 jWebUnit 的真正威力。这个应用程序是一个测试用例,用于打开一?Google 搜烦面q搜索文?HttpUnit。应用程序需要测试以下场景:

  • 打开 Google 主页 http://www.google.com?br />
  • 定该页包含一个名?q 的表单元素。(?Google 的主上Q名?q 的文本框是接受用h询输入的文本框。)应用E序用这个元素输入搜索参数?br />
  • 在搜索文本框中输入字W串 HttpUnit HomeQƈ提交表单?br />
  • 获得l果,q确定该面包含的链接中包含文本 HttpUnit Home?br />
  • 单击包含文本 HttpUnit Home 的链接?/li>

现在试场景已经qAQ可以编?Java 应用E序Q用 jWebUnit 实现q些需求了?/p>

W一步是声明一个从 WebTestCase 扩展而来的类Q如清单 3 所C:

清单 3. 声明试用例c?/strong>

            public class GoogleTest extends WebTestCase {
            static String searchLink = "";
            }
            

正如我在前面提到q的QjWebUnit 要求每个试用例都是?WebTestCase 中扩展而来的?code>searchLink 保存传入的搜索参数。这个g命o行参数的形式传递给试用例?/p>

下一步是声明入口?—?main() ҎQ如清单 4 所C:

清单 4. 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:

清单 5. 讄

            public void setUp() {
            getTestContext().setBaseUrl("http://www.google.com");
            getTestContext().setProxyName("proxy.host.com");
            getTestContext().setProxyPort(80);
            }
            

清单 5 把基?URL 讄?http://www.google.com。这意味着试用例的启动是相对于这?URL 的。下面两个语句设|连接到 Internet 的代理主机和代理端口。如果是直接q接?InternetQ那么可以忽略代理设|语句?/p>

现在开始浏览站点ƈ输入搜烦参数。清?6 昄了访?Web 面Q然后测试所有场景的代码Q?/p> 清单 6. 试所有场?/strong>

            public void testSearch() {
            beginAt("/");
            assertFormElementPresent("q");
            setFormElement("q", "HttpUnit");
            submit("btnG");
            assertLinkPresentWithText(searchLink);
            clickLinkWithText(searchLink);
            }
            

清单 6 的代码连接到基本 URLQƈ相对?/ 开始浏览。然后它断定面中包含一个名?q 的表单元?—?q ?Google 主页上查询输入文本框的名U。下一条语句用?HttpUnit 讄名ؓ q 的文本框。再下一条语a提交表单上名?btnG 的提交按钮。(?Google 的主上Q名?btnG 的按钮是标签?Google Search 的按钮。)表单是在q个对话中提交的Q下一列出搜索结果。在l果面上,代码首先查是否有一个链接的文本?HttpUnit Home。如果该链接不存在,那么试׃ AssertionFailedError p|。如果该链接存在Q则试执行的下一个操作是单击链接?

q行CZ应用E序
现在把示例应用程序投入用当中:

  1. 下蝲CZ应用E序 j-webunitsample.jar Q请参阅 下蝲Q?br />
  2. 在一个目录中解压~?j-webunitsample.jar。例如,如果把它释放?C:\temp 中,那么p把源文g和类文g攑֜ C:\temp\com\jweb\test 中,?setclasspth.bat 则放?C:\temp 中?br />
  3. ~辑 setclasspath.batQ设|?JAR_BASE 指向包含所有必需 JAR 文g的目录。例如,如果?C:\temp 中释?jwebunit-1.2.zip 文gQ那么将 JAR_BASE 讄?C:\temp\jwebunit-1.2\lib?br />
  4. 打开命o行提C符Q切换到 C:\temp 目录?br />
  5. 执行 setclasspath.bat。这会设|执行测试用例所需?CLASSPATH?br />
  6. 用命?java com.jweb.test.GoogleTest "HttpUnit Home" q行应用E序?/li>

CZ输出
在执行了试用例之后Q会在命令行输出一个测试用例报告。如果测试失败,报告看v来如清单 7 中所C:

清单 7. 带有断言p|的输?/strong>

            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
            

正如在清?7 中可以看到的Q可以用 HttpUnit Hwee 作ؓ参数来执行测试用例。这个测试用例遇到断a的地方会p|Q因为结果页面中不包含带有这个文本的链接。由此也׃生了junit.framework.AssertionFailedError?/p>

清单 8 执行时用 HttpUnit Home 作ؓ参数。测试用例找C个带有这个文本的链接Q所以测试通过了:

清单 8. 成功试的输?/strong>

            C:\temp>java com.jweb.test.GoogleTest "HttpUnit Home"
            .
            Time: 6.991
            OK (1 test)
            

l束?/span>
? 文通过讨论 jWebUnit 框架的一些突出特性和最重要的类Q介l如何用它创建简z的试用例Q让您对 jWebUnit 框架有一个认识。jWebUnit q有更多Ҏ可以用在测试用例中。它支持试 Web 面中的链接行数。可以对字符丌Ӏ表或者带有指定标{表单输入元素是否存在于页面上q行断言。此外,jWebUnit q可以处?cookie Q例如断a存在某个 cookie、删?cookie {。)试可以Ҏ个文本之后出现的特定文本的链接进行单凅R?如果想ؓ Web 应用E序构徏快而有效的试用例QjWebUnit 可能是您最好的朋友?/p>
















西瓜 2010-09-29 15:40 发表评论
]]>
JUnit in javahttp://www.aygfsteel.com/ashutc/archive/2010/09/29/333382.html西瓜西瓜Wed, 29 Sep 2010 05:30:00 GMThttp://www.aygfsteel.com/ashutc/archive/2010/09/29/333382.htmlhttp://www.aygfsteel.com/ashutc/comments/333382.htmlhttp://www.aygfsteel.com/ashutc/archive/2010/09/29/333382.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/333382.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/333382.html阅读全文

西瓜 2010-09-29 13:30 发表评论
]]>
spring 试用例http://www.aygfsteel.com/ashutc/archive/2010/09/29/333349.html西瓜西瓜Wed, 29 Sep 2010 05:25:00 GMThttp://www.aygfsteel.com/ashutc/archive/2010/09/29/333349.htmlhttp://www.aygfsteel.com/ashutc/comments/333349.htmlhttp://www.aygfsteel.com/ashutc/archive/2010/09/29/333349.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/333349.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/333349.html

首?/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>

 

 

/**
* @author tao.youzt
*/
public interface BizUrlDAO {
public Object insert(BizUrlDO bizUrlDO);
public int delete(String url);
public BizUrlDO getByUrl(String url);
}

 

 

DAO实现c,该类l承一个支持类Q封装了Ҏ据库的操作?/span>

/**
* @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);
}
}

DO领域对象

 

 

/**
* @author tao.youzt
*/
public class BizUrlDO {
private int    id;
private String url;
private String email;
private String name;
// getter and setter
}

因ؓ本文案例使用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>

/**
* @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;
}
}

getConfigLocations()Ҏ?/span>AbstractDependencyInjectionSpringContextTests 提供配置Q?/span>Spring会根据该配置文g自动注入bizUrlDAO属性?/span>testInsertQ)Ҏ用于试插入新数据,注意q里有个问题Q如果数据库中已l存在该URL的记录,则应用会报错Q所以这里还要进行数据清除准备处理,我们UC?#8220;试环境准备”Q以后会用到该名词;testDuplicateInsertQ)Ҏ用于试插入重复数据的情况,该方法同样存在上面的问题Q?/span>testDelete()Ҏ用于试删除数据的情况,q里管准备了数据,但仍没有考虑数据库中已经有记录的情况?

 

    lg所qͼ管该测试类已经比较清晰Q但仍然存在许多不之处。我们将在后面的章节q行详细分析Qƈl出解决Ҏ?

 

Callback Function & Template Method  Pattern

 

       回调函数Q?/span>Callback FunctionQ和模板ҎQ?/span>Template MethodQ是软g架构设计中最常用的两U设计模式,q两U设计模式在Spring框架中随处可见?/span>

 

       关于本节是否要详l介l回调函敎ͼCallback FunctionQ和模板ҎQ?/span>Template MethodQ模式的问题Q笔者考虑了很长时间。因为网l上对这两种普遍使用的设计模式的定义层出不穷Q各有各的道理,很难说谁是谁非。况且,针对不同的应用场景,q两U模式也有许多变体,或者联合用?/span>

 

因此Q笔者最l决定不在此处对q两U模式做M定义或引用,误者自行参阅相x档资料?/font>

 

回调函数和模板方法模式在单元试中的应用

 

上一节我们简单的回顾了回调函数和模板Ҏ模式Q?/span>Spring框架中大量采用了q两U设计模式,有兴的读者可以阅?/span>Spring框架代码q一步m固对q两U模式的理解和运用。本节将l合回调函数模式和模板方法模式对前面的测试用例进行重构,读者可以在重构q程中逐步了解q两U设计模式的q用?/span>

 

首先Q让我们单ȝ一下前面测试用例的问题Q?/font>

 

一?/font>             抽象层次太低Q不够通用Q?/span>

例如Q对?/font>getConfigLocationsQ)ҎQ我们完全可以放C个父cM实现Q因为对于一个项目而言Q其配置文g大多都是l一的,没有必要在没有测试类中都定义该方法?/font>

/**
* 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;
}
}

如图所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>

/**
* @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();
}
}

如你所见,我们在这里定义了setupEnv()?/span>cleanEnv()两个ҎQ分别用于初始化环境和清除测试数据,然后在测试方法开始和l束时分别调用这两个Ҏ。这的确辑ֈ了我们的目的Q不用在每个试Ҏ中都~写初始化和清除逻辑Q但此时你一定发现在每个试Ҏ前后都调?/span>setupEnv()?/span>cleanEnv()也很不爽Q那说明我们的抽象程度还不够Q那么该如何做的更好呢?

 

 

q里该到模板ҎQ?/span>Template MethodQ模式发挥威力的时候了。我们将使用模板Ҏ来l重构前面的案例。让我们先来定义一个方法:

/**
* @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();
}
}

相比之前的方法,我们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!

 

问题g来清晎ͼ但我们遭遇了一条无法跨的鸿沟—?strong>如何才能把测试逻辑传递到
executeҎ中呢Q单靠传l的~程Ҏ已经无法解决q个问题Q因此我们必d扑օ他途径?/span> 

 

可能此时此刻你已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>








西瓜 2010-09-29 13:25 发表评论
]]>
վ֩ģ壺 | | | ޼| | ̶| Զ| | | ڻ| ʡ| | | | Ұ| | տ| | ̨| ƽ| | | ζ| ƽ̶| | ̨| | | ɽ| | | | ޼| | ζ| | | | Ϫ| տ| ֬|