`
yydy1983
  • 浏览: 46411 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

J2EE开发平台的软件测试技术

阅读更多

J2EE开发平台的软件测试技术

作者:佚名(来自:cybercorlin.net 

    前言 - 以测试为导向的软件开发流程

  软件开发流程的新兴观念是将软件测试的角色,提升为系统开发时每一个阶段都必须要持续且反复进行的重要任务,确保每一个阶段都能及早发现潜藏于系统内的危险因子。当某一个阶段的测试结果无法达到预期的要求时,就必须回溯到之前的开发阶段,再次分析和审核,这种过程称之为重构(Refactoring)。配合重构的机制,让系统的品质都能够在严密的测试监控下持续成长。不过由于网络时代的革命兴起之后,软件系统的架构变得更为复杂,相对的软件测试的发展也更显得重要。
     J2EE平台内的软件测试
   XP只定义了两种测试的层级,第一种层级是「单元测试」,因为单元测试的用意是为了检验程序代码是否合乎逻辑,而且是针对系统内部的模块来测试,因此又可以称做为程序逻辑测试(Code Logic Testing)。为了因应不同的应用程序开发平台的特殊架构,在J2EE的平台里,还发展出与J2EE Container 紧密结合的整合测试(Integration Testing)。
   另一种层级是接受度测试(Acceptance Testing),又称作功能测试(Functional Testing)。在软件测试中还有一个大家常听到的是效能测试(Performance Testing)。由于效能测试与客户的需求是密不可分,所以将它归类为接受度测试的延伸应用。最后归纳起来,一个J2EE平台可能所需要的测试流程,以及测试之间的关系,如图一所示。
                        
  在Web层内软件测试的概念与流程,如图二所示,其中键头旁的数字符号代表着整个测试流程的执行步骤。首先先准备好受测数据与受测系统之后,借着虚拟浏览器来发出request,向受测系统取得包裹着HTML code的response。然后再利用测试平台来协助我们进行受测数据与预期值的比对工作。当比对后所回报的结果都是正确无误时,代表着受测系统的功能可以正常运作了。图中的测试平台与虚拟浏览器在测试中扮演着关键的角色。
                 
    虽然发展测试平台的概念已经行之有年了,然而具备有可延伸且开放式架构的测试平台并不多,其中OpenSource社群以Java开发出来的JUnit,是极具代表性的测试平台。

1. 单元测试平台─ JUnit

   JUnit平台的设计架构是采用了命令(Command)和复合(Composite)两种设计模式(Design Pattern)做为关键的组成架构。在JUnit平台中的核心类别是TestCase,而每一个TestCase代表着一个命令对象。TestCase包含数个test method,用来测试被测类别内public method的产出对象与预期的结果是否相同。在JUnit平台内有提供数种用来协助比对的assert method。
   JUnit平台里还有另一个核心类别是TestSuite,而每一个TestSuite代表着一个复合的对象。一个TestSuite可以由数个TestCase或是数个TestSuite组成,因此可以根据测试的需求,拼凑出多个的TestSuite。整个JUnit测试平台的组成架构,如图三所示。在了解了JUnit平台的架构之后,我们便可以运用JUnit平台来发展受测系统的整合测试与功能测试。

                        
代码实例:
import junit.framework.*;
import java.util.Vector;
public class VectorTest extends TestCase {
protected Vector fEmpty;
protected Vector fFull;
public VectorTest(String name) {
super(name);
}
public static void main (String[] args) {
junit.textui.TestRunner.run (suite());
}
protected void setUp() {
fEmpty= new Vector();
fFull= new Vector();
fFull.addElement(new Integer(1));
fFull.addElement(new Integer(2));
fFull.addElement(new Integer(3));
}
public static Test suite() {
return new TestSuite(VectorTest.class);
}
public void testCapacity() {
int size= fFull.size();
for (int i= 0; i < 100; i++)
fFull.addElement(new Integer(i));
assertTrue(fFull.size() == 100+size);
}
public void testClone() {
Vector clone= (Vector)fFull.clone();
assertTrue(clone.size() == fFull.size());
assertTrue(clone.contains(new Integer(1)));
}
public void testContains() {
assertTrue(fFull.contains(new Integer(1)));
assertTrue(!fEmpty.contains(new Integer(1)));
}
public void testElementAt() {
Integer i= (Integer)fFull.elementAt(0);
assertTrue(i.intValue() == 1);
try {
Integer j= (Integer)fFull.elementAt(fFull.size());
} catch (ArrayIndexOutOfBoundsException e) {
return;
}
fail("Should raise an ArrayIndexOutOfBoundsException");
}
public void testRemoveAll() {
fFull.removeAllElements();
fEmpty.removeAllElements();
assertTrue(fFull.isEmpty());
assertTrue(fEmpty.isEmpty());
}
public void testRemoveElement() {
fFull.removeElement(new Integer(3));
assertTrue(!fFull.contains(new Integer(3)) );
}
}
import junit.framework.*;
import junit.runner.BaseTestRunner;
public class AllTests {
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
public static Test suite() {
TestSuite suite= new TestSuite("Framework Tests");
suite.addTestSuite(ExtensionTest.class);
suite.addTestSuite(TestCaseTest.class);
suite.addTest(SuiteTest.suite()); suite.addTestSuite(ExceptionTestCaseTest.class);
suite.addTestSuite(TestListenerTest.class);
suite.addTestSuite(ActiveTestTest.class);
suite.addTestSuite(AssertTest.class);
suite.addTestSuite(StackFilterTest.class);
suite.addTestSuite(SorterTest.class);
suite.addTestSuite(RepeatedTestTest.class);
suite.addTestSuite(TestImplementorTest.class);
if (!BaseTestRunner.inVAJava()) {
suite.addTestSuite(TextRunnerTest.class);
if (!isJDK11())
suite.addTest(new TestSuite(TestCaseClassLoaderTest.class));
}
return suite;
}
static boolean isJDK11() {
String version= System.getProperty("java.version");
return version.startsWith("1.1");
}
}

2 整合测试的观念与Cactus应用

   整合测试提供了J2EE Container的环境,可以快速轻易地检验出Domain Object与J2EE Container的互动行为是否合乎逻辑。因此整合测试的对象是以一个EJB、Servlet或是JSP的程序代码为基本单元。Open Source社群的Jakarta计划中的子计划Cactus,即是为了实作整合测试用的平台而诞生的。
   Cactus基本上也是延伸JUnit平台而发展出来的,因此它除了原有基本的method之外,还提供了可以用来模拟浏览器的内部行为的beingxxx( )和endxxx( )的method。这两个method来这些method的执行顺序和与Web Container互动的行为模式,如图四所示。
                        
  我们利用beginxxx( )来设定要传递给受测对象的字符串参数。执行完beginxxx( )后,会发出request将参数名称与参数值传递到Web Container。TestCase会执行setUp( ),将受测对象所需要的对象环境建立起来,接着在testxxx( )执行存取受测对象的动作。当存取受测对象的动作执行完后,便可以检验受测对象可能存放在session的产出物。然后在Web Container会执行释放资源的动作,然后将response回传到Client端。最后在Client端执行endxxx( )来进行比对HTML code是否和预期值相同,执行完endxxx()时也代表一个整合测试的结束。将这五个method所执行的功能汇整如表一所示。
  虽然Cactus架构提供了受测对象产出物与预期结果的比对功能,但是当回传的HTML code的内容过于庞大复杂时,反而不利于比对的工作。因此采用了一个实用性的做法。此做法是在JSP或servlet欲产出的HTML code的程序代码里,于关键的卷标内添加ID这种属性。当endxxx( )要进行比对前,先读取记载着ID属性值与预期值的外部数据文件,再透过DOM的存取机制来取得HTML code,便能够快速地比对关键的数据。不仅可以将比对的工作模块化,更能够在不需要重新编译测试码的情形下,随时变更预期值。读者们若有遇到相似的问题时,不妨可以采用与相同的策略来解决。
   整合测试不同于单元测试,虽然减低了撰写测试码的困难度,但也因为Domain Object与J2EE Container的结合,而不能为Domain Object提供单纯的测试环境。因此若有其它的测试可以单纯地检验整个系统,便可以弥补整合测试的不足。功能测试即是扮演这样的一个角色。
配置信息与代码实例:
<servlet>
<servlet-name>ServletRedirector</servlet-name>
<servlet-class>
org.apache.cactus.server.ServletTestRedirector
</servlet-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1 used for testing</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>ServletTestRunner</servlet-name>
<servlet-class>
org.apache.cactus.server.runner.ServletTestRunner
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletRedirector</servlet-name>
<url-pattern>/ServletRedirector</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletTestRunner</servlet-name>
<url-pattern>/ServletTestRunner</url-pattern>
</servlet-mapping>
实例testcase:
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;
public class TestSampleServlet extends ServletTestCase
{
public TestSampleServlet(String theName)
{
super(theName);
}
public static Test suite()
{
return new TestSuite(TestSampleServlet.class);
}
public void beginSaveToSessionOK(WebRequest webRequest)
{
webRequest.addParameter("testparam", "it works!");
}
public void testSaveToSessionOK()
{
SampleServlet servlet = new SampleServlet();
servlet.saveToSession(request);
assertEquals("it works!", session.getAttribute("testAttribute"));
}
}
 
 
 
 

3 业务逻辑测试与StrutsTestcase应用

      struts的使用越来越广泛,但是并没有合适测试框架与之对应.cactus, httpunit虽然都可以测试jsp,servlet,但基于struts框架的应用程序的测试依然比较麻烦。
      不过,StrutsTestCase的出现多少解决了些问题,下面主要讲讲StrutsTestCase的应用。和其他的JSP/SERVLET测试框架一样,StrutsTestCase也有两种测试结构,一种是Mock结构,另一种是利用Cactus的结构。
      因为StrutsTestCase也是基于junit上开发的,所以它的使用方法也类似于junit.可以参考他的例子和api.
      可惜的是StrutsTestCase例子里没有使用ant,因此它的运行较让人头痛,特别是有关配置文件的处理。StrutsTestCase在运行时必须能够找到struts-config.xml和web.xml文件,默认的位置是web-inf/目录下,所以web-inf目录要存在于classpath或者在运行的时候指定。例如:我的位于d:/struts-test/下,struts-config.xml和web.xml位于d:/struts-test/webapp/WEB-INF/下,那么在运行测试的时候应当把d:/struts-test/webapp路径放到classpath里。如果我的struts-config.xml不在/web-inf目录里,而是在/web-inf/conf/目录下,那么就须调用setConfigFile(String path)方法,按照刚才的情况,
public void testSuccessfulLogin() {
setRequestPathInfo("/login"); addRequestParameter("username","deryl"); addRequestParameter("password","radar");
actionPerform();
verifyForward("success"); }
public void testFailedLogin() {
addRequestParameter("username","deryl");
addRequestParameter("password","express");
setRequestPathInfo("/login");
actionPerform();
verifyForward("login");
}
public void setUp() throws Exception {
super.setUp() ;
setConfigFile("/WEB-INF/conf/struts-config.xml") ;
}
 
 

4 功能测试的观念与HttpUnit应用

  以UML的术语来说,功能测试的对象是检验Use Case所规范的行为,测试系统是否符合所需要的功能,是否能达到使用者的需求?而单元测试的对象是检验对象Classes Diagram与Sequence Diagram所描述的关系与行为,测试单元是否执行正确,是否符合程序逻辑?。每当完成一个阶段性的功能测试,也代表着完成了一部分的系统实作。
   Open Source社群的HttpUnit API套件,即是为了功能测试而发展出来的。HttpUnit是以Java撰写出来的虚拟浏览器,用来模拟浏览器的内部行为。前一节所提到的Cactus检验HTML code的机制,也是采用HttpUnit来完成的。
   除此之外,HttpUnit还可以结合JUnit平台撰写测试码来检验回传的网页内容是否与预期结果相符合。HttpUnit平台的运作机制是建构在Http标准通讯协议之下,藉由模拟使用者浏览网站时,所发出的以对象的形式封装的request讯号,将其送至到目的网站,然后等到该网站处理完此request之后,便将同样以对象形式封装的response讯号回传给HttpUnit。
   由于HttpUnit所接收的是标准HTTP协议的response对象,因此不论该网站是静态网页语言或是用任何的动态服务器端语言写成的,都可以透过HttpUnit来模拟网站浏览的行为并且取得标准的HTML code。
   市面上也有提供功能测试用的预录播放软件,可以事先录下网站浏览的步骤,然后反复地播放预录好的流程,最后回报测试的数据给测试人员,供测试人员进行分析,大大节省撰写测试码的负担。然而此类软件有以下的缺点:
1. 当网站设计的复杂度越高,浏览的分支流程越多,预录好的流程便无法作有效的模块化管理。
2. 预录播放软件虽然有提供记录浏览步骤的script或是XML文件,虽然这些指令码可以重复利用,然而若无法提供有效的侦错机制,一旦安插了错误的程序,反而容易造成无法预期的错误产生。
3. 测试人员需要重新学习专属于预录播放软件的script语言或是XML文件语法,无法从既有熟悉的程序语言来编写浏览网站的程序。
4. 当网站的操作接口时常为了需求而新增或是修改原有的互动设计时,必须重新录制新的浏览网站的程序,而无法重复利用。
   HttpUnit解决了软件开发人员以上的困扰。HttpUnit是一种黑箱作业形式的测试工具,因此我们只要专注如何在JUnit平台上撰写模拟浏览器行为的测试码即可。HttpUnit内的method执行顺序和与Web Container互动的行为模式,如图五所示,箭头符号旁所标示的数字,代表着这些method的执行顺序。
 
   当我们在setup( )设定好受测的网址与相关的环境后,setUp( )会执行向受测网址进行存取的动作。当存取动作完成时,会将response回传至Client端。此时可以在testxxx( )做HTML code和预期值比对的工作。最后在Client端执行释放资源的动作,执行完tearDown()时也代表一个功能测试的完成。将这三个method所执行的功能汇整如表二所示。
      虽然HttpUnit提供强大的仿真功能,但是HttpUnit本身还是存在两个缺点。第一,当HttpUnit结合JUnit平台做测试时,由于HttpUnit存取HTML code的方式与HTML内部的文件结构的关联过于紧密,因此当网页版面需要变动时,也需要修改相对应的测试码。对于这样的困扰,采用了与Cactus检测HTML code同样的改良策略,来达到快速比对而不用调整测试码的好处。
   第二,在HttpUnit与JUnit平台结合做测试的情况时,由于JUnit特殊的运作机制,无法记住每一个已经浏览过的网址状态,因此当某个受测网址与其它网址的依存性强时,若要回传正确的浏览状态时,必须要用递归记忆的方式来达成。例如要存取第二个网页必须记住第一个网页的状态,存取第三个网页要记住第一个和第二个网页的状态,同理存取第n个网页时需要记住第一个网页到n-1个网页,这样的做法不易将测试码模块化,如图六所示。
 
       于是利用了HttpUnit本身也可以写成独立运作的程序代码的特性,写成一个浏览网站步骤的仿真器。然后利用JUnit的setUp( )来存取受测网站的浏览状态,便可以在testxxx( )取得正确的网页状态来进行比对的工作,经过模块化后的HttpUnit测试架构如图七所示。      
                 

5 可以参考的测试项目:

Junit 包中有比较简单的测试用例的例子。
Eclipse 开发平台的 JUnit Plugin Tests and Automated Testing Framework 插件中的JunitTest项目例子,比较详细的介绍了junit测试平台。
参考文献
1. JUnit官方网站http://www.junit.org/index.htm
2. Cactus官方网站http://jakarta.apache.org/cactus/index.html
3. Strutstestcase网站http://strutstestcase.sourceforge.net/
4. HttpUnit 网站 http://sourceforge.net/projects/httpunit/
5. Eclipse 网站 http://www.eclipse.org/downloads/index.php
 
分享到:
评论

相关推荐

    软件测试技术在J2EE项目中的应用

    本文给出及J2EE项目开发时的测试过程,以及各测试阶段用到的软件测试工具。

    软件测试方法和技术(朱少民).rar

    9.4 基于J2EE平台的测试 9.5 其他应用服务器应用的测试 4 9 第10章 软件本地化测试 10.1什么是软件本地化 10.2软件本地化的翻译问题 10.3软件本地化测试的技术问题 10.4本地化测试的重点 2 10 第11章 软件测试自动化...

    软件测试方法和技术(四)

    软件测试方法和技术 共17章 1.1 软件的含义 1.2 软件开发过程的特性 1.3 软件测试的重要性 2.1 软件质量就是客户的满意度 2.2 软件缺陷(Bug)是什么 2.3 软件测试的基本方法 2.4 软件测试的分类和阶段 2.5 软件测试...

    软件测试方法和技术(二)

    软件测试方法和技术,共17章 1.1 软件的含义 1.2 软件开发过程的特性 1.3 软件测试的重要性 2.1 软件质量就是客户的满意度 2.2 软件缺陷(Bug)是什么 2.3 软件测试的基本方法 2.4 软件测试的分类和阶段 2.5 软件测试...

    软件测试方法和技术(五)

    软件测试方法和技术 共17章 1.1 软件的含义 1.2 软件开发过程的特性 1.3 软件测试的重要性 2.1 软件质量就是客户的满意度 2.2 软件缺陷(Bug)是什么 2.3 软件测试的基本方法 2.4 软件测试的分类和阶段 2.5 软件测试...

    软件测试技术与实践培训班

    陈渌萍 测试专家、从事 it行业 21年, 7年丰富的软件测试经验;5年的软件开发和测试教学经验,高级工程师;设计并组织大型电子政务、 workfolw软件项目测试 6个,中小项目十多个; 具有丰富制造业、通讯业行业工作...

    j2ee概述,包括平台的架构和技术标准等

    1.1 J2EE介绍 1.2 组件的层次 1.3 J2EE应用程序的结构 1.4 J2EE的技术体系 1.5 J2EE的优势 1.6 J2EE设计层面上的层次划分 1.7 开发环境的搭建 1.8 Junit单元测试

    基于J2EE架构的电信运营计费系统的设计与实现

    本论文对软件架构进行了研究,论述了J2EE平台在软件开发中的特点和优势,并且对J2EE平台中常用的技术和开发模式进行介绍。本系统不是基于EJB的J2EE应用的开发,因为EJB应用的开发周期过长,且必须运行在J2EE容器中...

    J2EE电子商务系统开发从入门到精通

    1.2.3 J2EE主要技术..... 6 1.3 小结..... 7 第2章 Struts基础...... 8 2.1 MVC模式概述..... 8 2.1.1 MVC设计模式..... 8 2.1.2 JSP Model 1与JSP Model 2.. 9 2.2 Struts的体系架构..... 10 2.3 Struts控制器组件...

    基于web的医患交流平台(J2EE版本)设计文档

    本文设计和实现一个基于Web的医患交流平台,使用J2EE技术,从需求分析开始,到概要设计、详细设计和实现,并有实现界面截图。是我们课程设计完成的项目。目录如下:(代码由于太大还无法上传……) 第一章 引言 1 ...

    学生成绩管理系统-课程设计

    本系统是基于J2EE开发的成绩管理系统,弥补了人工管理的不足,提高了一定的效率。主要功能包括教师对学生成绩的记录,生成总评成绩,成绩单的提交。学生查询相关的成绩信息。管理员对学生、教师,课程、班级进行综合...

    J2EE实例讲解RUP10-1

    3 月 这个由多篇文章组成的系列文章讲述了如何在很紧的时间和预算的情况下通过应用 Rational 统一过程(RUP)以及 Rational 的其他工具来开发一个软件项目的。 文章的第一部分包含了高层次的计划和需求的引出。...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    在进行软件系统开发的最初环节,一般都需要进行系统的选型,即根据系统功能的实际需求,选择合适的开发工具及软件架构。 blog对系统的可靠性、稳定性有比较高的要求。本系统设计时,比较主流的B/S设计有基于JSP、...

    基于J2EE的财务管理系统设计与实现.doc

    全文分为项目意义及功能简介、开发工具介绍、系统分析及设计、系统的实现、系统页面的演示、系统测试、总结、参考文献、致谢、附录共十个部分。 首先第一部分介绍了项目意义及功能简介;接着第二部分对Struts、...

    技术投标标书.zip

    2.4 J2EE研发平台 5 2.5 Web应用服务环境 6 2.6 系统流程设计 6 第3章 关键技术解决方案 7 3.1 基本技术介绍 7 3.1.1 MVC模式 7 3.1.2 三层技术 8 3.2 技术路线的可行性和解决关键技术的途径 9 3.3 数据资源解决方案...

Global site tag (gtag.js) - Google Analytics