`
zengbo0710
  • 浏览: 414906 次
社区版块
存档分类
最新评论

追求代码质量: 测试 Struts 遗留的应用程序

阅读更多

用 StrutsTestCase 和 DbUnit 保证 Struts 平稳运行

developerWorks

级别: 中级

Andrew Glover (aglover@stelligent.com), 总裁, Stelligent Incorporated

2006 年 8 月 17 日

虽然 Struts 正在慢慢退出 Web 框架的历史舞台,但它的遗产仍然存在,存在的形式主要是需要测试和维护的应用程序。这个月,Andrew Glover 向您介绍如何使用 JUnit 的 StrutsTestCase、DbUnit 以及在这个系列中迄今为止学到的一些工具,把以质量为中心的方法用于 Struts 上的测试(可以这么说)。
<!----><!----><!---->

基于 Java™ 的 Web 开发领域最近出现了丰富的竞争性技术。启动新项目的开发人员可以在许多不同的框架之间进行选择,包括 JavaServer Faces、Tapestry、Shale、Grails 和 Seam (只列举众多机灵的名称中的几个)。很快,我们就可以通过 JRuby 框架在 Java 编程中使用 Ruby on Rails 了!

但就在不远的过去,只有一个 Java Web 开发框架卓然而立。Struts 是第一个在 Java 世界掀起风暴的框架,而且多年以来,好像是如果一个项目不用 Struts 构建就没有前途一样。没有 Struts 经验的 Java 开发人员很稀少,也很不幸,就像今天的开发人员没有听说过 Ruby on Rails 一样。

提高代码质量
不要错过 Andrew 的附带 讨论组 ,可以得到最迫切问题的答案。

即使 Struts 正慢慢地从舞台中央退去(原来的基本框架,现在叫做 Struts 1,似乎正在退出 Web 框架的历史舞台),但它的遗产仍然存在,既以 Shale (请参阅 参考资料)的形式存在,又以运行在世界各地的成千上万的遗留应用程序的形式存在。因为许多企业宁愿测试和维护这些应用程序而不愿意花钱重新编写它们,所以理解 Struts 应用程序的一些缺陷,以及如何围绕它们进行重构,是个好主意。

这个月,我要把以质量为核心的方法用于 Struts 应用程序的测试场景。结合现实,这个场景围绕着最普遍的 Struts 构造:深受喜爱的 Action 类。

1、2、3,行动!

Struts 的革新之一就是把 Web 开发从 Servlet 移进了 Action 类。这些类包含业务逻辑,以 JavaBean 的形式(通常叫做 ActionForm)把数据传送到 JSP。然后 JSP 处理应用程序视图。Struts 到 MVC 的方法非常容易掌握,以至于许多开发团队冒失地闯进去,而很少考虑与 Action 相关的长期设计和维护问题。

测试和复杂性

我已经发现,在开发人员的测试和代码的复杂性之间存在强烈的相关性:没有其中一个的地方,通常也没有另一个。高度复杂的编码难于测试,结果是很少有人会真正为它编写测试。反过来,编写测试可以降低代码的复杂性。因为给复杂代码编写测试更困难,而且因为会边走边测试,所以会发现自己朝着更简单的代码构造前进。如果代码太复杂,而且知道不得不测试它,您可能就会在测试之前对复杂性进行重构。不论如何看待,为不那么简单的代码编写测试是消灭代码复杂性的好实践。

虽然在那个时候(过去的自由时光啊)可能没人想过,但 Struts Action 类通常成为复杂性的保护所。像在老的 EJB 架构中声名狼籍的会话 Facade 一样,Action 类会成为特定业务过程的严格伪装,或者通过直接调用 EJB,通过打开数据库连接,或者通过调用其他高度依赖的对象。Action 类还有输出耦合(通过 java.servlet API 包中的对象,例如 HttpServletRequestHttpServletResponse),从而极难把它们隔离出来测试。

隔离出来测试 Action 类的困难意味着它们可以很容易变得相当复杂 —— 特别是当它们变成越来越深入地与遗留框架耦合的时候。现在我们来看这个困难在真实的遗留应用程序场景中作用的情况。

测试挑战

即使最简单的 Struts Action 类也会是个测试挑战。例如,以清单 1 中的 execute() 方法为例;它看起来足够简单,可以测试,但是真的么?


清单 1. 这个方法看起来容易测试……
public ActionForward execute(ActionMapping mapping, ActionForm aForm, 		HttpServletRequest req, HttpServletResponse res) throws Exception { try{     String newPassword = ((ChangePasswordForm)aForm).getNewPassword1();   String username = ((ChangePasswordForm)aForm).getUsername();   IUser user = DataAccessUtils.getDaos().getUserDao().findUserByUsername(username);   user.digestAndSetPassword(newPassword);   DataAccessUtils.getDaos().getUserDao().saveUser(user);	 }catch(Throwable thr){				     return findFailure(mapping, aForm, req, res); } return findSuccess(mapping, aForm, req, res);	}


图 1. Action 类的输出耦合

但是,就像在图 1 中可以看到的,在试图隔离 ChangePasswordAction 类并检验 execute() 方法时,该类给出了一些有代表性的挑战。为了有效地测试 execute() 方法,必须处理三层耦合。首先,到 Struts 自身的耦合;其次,Servlet API 代表一个障碍;最后,到业务对象包的耦合,进一步检查业务对象包,还会有数据访问层使用 Hibernate 和 Spring。

每种情况一个 mock?

即使在我编写本文时,我还可以听到开发人员的嘲笑者 认为我的测试问题通过明智地使用 mock 对象就能轻易解决。可以 用 mock 对象创建一级隔离,它会形成更容易的测试;但是,我要说的是,把目标对象通过 mock 排除所需要的付出级别,比起承认隔离测试困难所需要的付出,要多得多。在这种情况下,我会采用在更高层次上的测试,这级测试有时叫做集成测试。

对于更高的复杂性,请注意 清单 1 中的代码如何把 aForm 参数转换成 ChangePasswordForm 对象,它是 Struts ActionForm 类型。这些 JavaBeans 有一个 validate 方法,这个方法由 Struts 在调用 Action 类的 execute() 方法之前调用。

犯错误太容易了

在清单 2 中,可以看到所有这个复杂性会在哪里发生。ChangePasswordFormvalidate() 方法的代码片段演示了保证两个属性(newPassword1newPassword2)不为空并彼此相等的简单逻辑。但是,如果 Struts 发现 errors 集合(类型为 ActionErrors)包含一些 ActionError 对象,就会沿着错误路径走,例如带着出错消息重新显示 Web 页面。


清单 2. ChangePasswordForm 的验证逻辑
if((newPassword1 == null) || (newPassword1.length() < 1)) {  errors.add("newPassword1",     new ActionError("error.changePassword.newPassword1Required"));}if((newPassword2 == null) || (newPassword2.length() < 1)) {  errors.add("newPassword2",     new ActionError("error.changePassword.newPassword2Required"));}if((newPassword1 != null) && (newPassword2 != null)) {  if(!newPassword1.equals(newPassword2)) {    errors.add(ActionErrors.GLOBAL_ERROR, 	 new ActionError("error.changePassword.passwordsDontMatch"));  }}

清单 1清单 2 的代码不特殊也不特定于某个领域。它是无数应用程序中都包含的简单口令修改逻辑。如果正在测试 Struts 遗留应用程序,将不得不花些时间处理口令逻辑,但是如何用可重复的方式测试它呢?





回页首


两个测试用例

在企图为 清单 1(间接的是 清单 2)的代码编写测试之前,可能想确定实际需要测试什么。在这个具体示例中,逻辑清楚地是为了方便用户口令的修改;所以,应当编写至少两个层次的测试用例:

  • 口令修改在数据正确时是否工作?
  • 如果数据不正确,口令是不是 修改?

这些测试不会太容易只是个假设。不仅需要对付 Struts,还必须处理数据层以及数据层与数据库暗含的耦合!在面对复杂性时,我的第一本能是寻求帮助,在这个示例中,是以 JUnit 的 StrutsTestCase 的形式。





回页首


来自 StrutsTestCase 的帮助

StrutsTestCase 是一个 JUnit 扩展,专门针对 Struts 应用程序。这个框架实际上模拟了一个 servlet 容器,这样就能虚拟地运行和测试 Struts 应用程序,而不必在 Tomcat(举例)中运行它了。框架还有一个方便的 MockStrutsTestCase 类,它扩展了 TestCase 并处理许多 Struts 配置方面(例如装入 struts-config.xml 配置文件)。

但是,在您认为自己完全脱离了 Struts 配置的痛苦之前,应当了解一些正确配置 MockStrutsTestCase 的事情。也就是说,需要把它指向代表 Web 应用程序的目录,然后指向必要的 web.xml 和 struts-config.xml 文件。默认情况下,MockStrutsTestCase 扫描这些项目的类路径;但是,要把 MockStrutsTestCase 配置成在特定环境中工作,操作很简单,只需覆盖一些设置并编写一些特定的配置代码即可。

返回口令验证示例,包含 ChangePasswordAction 类的项目有图 3 所示的目录结构:


清单 3. 示例目录结构
root/  src/    conf/    java/    webapp/      images/      jsp/      WEB-INF/  test/

WEB-INF 目录包含 web.xml 和 struts-config.xml 文件,webapp 目录代表 Web 上下文环境。知道了这些,我就如清单 4 所示配置 MockStrutsTestCase


清单 4. MockStrutsTestCase 的定制 fixture 代码
public void setUp() throws Exception { try {  super.setUp();  this.setContextDirectory(new File("src/webapp/"));  this.setServletConfigFile("src/webapp/WEB-INF/web.xml");  this.setConfigFile(       this.getSession().getServletContext()	      .getRealPath("WEB-INF/struts-config.xml"));    }catch (Exception e) {   fail("Unable to setup test");  }}

其他测试方式

在某些情况下,基于 Action 类中发现的对应逻辑,可能能够用基于 Web 的测试框架(像 JWebUnit 或 Selenium)间接地 测试代码。使用这些框架从测试设置的角度来说,确实增加了复杂性。例如,要使用 JWebUnit,必须把应用程序部署到一个运行着配置好的数据库的 servlet 容器。把 StrutsTestCase 和 DbUnit 协同使用,可以方便测试,不必 把 war 文件部署到运行着的 servlet 容器。它还允许在 考虑应用程序的视图方面的情况下进行测试。

关于逻辑映射

正确地配置了 MockStrutsTestCase 的实例后,测试 Action 类就只包含一点点逻辑映射。要调用 Action 类,需要强制 StrutsTestCase 框架通过一个路径间接地 调用它,这是在 struts-config.xml 文件中定义的。

例如,要强制调用 ChangePasswordAction 类,必须告诉框架使用 /changePasswordSubmit 路径。在清单 5 中可以看到这点,清单 5 中的代码片段来自 struts-config.xml 文件,它把 ChangePasswordAction 类映射到 /changePasswordSubmit 路径:


清单 5. struts-config.xml 代码片段显示了动作类路径映射
<action path="/changePasswordSubmit"         type="com.acme.ccb.action.ChangePasswordAction"          name="changePasswordForm" scope="request"          input="/jsp/admin/changepassword.jsp">      <forward name="success" path="/viewUsers.do"            redirect="true" contextRelative="false" /></action> 

一旦某个用户点击了提交按钮(举例),Struts 就把来自 HTTP 请求的参数值映射到 ActionForm,在这个示例中,是上面的 struts-config.xml 代码片段中(在清单 5 中)定义的 ChangePasswordForm。要模拟这个行为,在测试用例中必须有另一个逻辑映射 —— JSP 表单名称必须映射到值。在口令修改场景中,提交了四个参数:usernamecurrentPasswordnewPassword1newPassword2 newPassword2 参数是多数 Web 页面为了校验新口令正确的确认信息)。





回页首


成功的测试用例!

请求路径和参数映射好之后,编写测试用例就成了利用 MockStrutsTestCase API 设置相关口令值的问题,如清单 6 所示。在这个测试用例中,用户 Jane 的口令从 “admin” 改成了 “meme”。


清单 6. 一个验证口令修改成功的简单测试用例
public void testExecute() throws Exception{		 setRequestPathInfo("/changePasswordSubmit");		 addRequestParameter("username","jane"); addRequestParameter("currentPassword","admin"); addRequestParameter("newPassword1","meme"); addRequestParameter("newPassword2","meme"); actionPerform();		 verifyForward("success");			}

setRequestPathInfo() 方法配置路径以映射到 Action 类,addRequestParameter() 方法把来自 JSP 文件的参数名称映射到值。例如,在清单 6 中,username 参数映射到 “jane”。

还请注意清单 6 中的最后两行。actionPerform() 方法实际上让 Struts 去调用对应的 Action 类。如果这个方法没被调用,什么也不会发生。最后调用的方法 verifyForward() 是在 MockStrutsTestCase 类中找到的一个类似于断言的方法,它验证正确的转发。在 Struts 中,这是一个 String,通常映射到成功或失败状态。(请注意,清单 5 中的 XML 定义了 “success” 转发。)





回页首


用 DbUnit 进行的可重复的成功

这时,您可能希望工作完成 —— 毕竟已经编写了一个企图验证口令修改的测试。但是还缺乏更深的验证。确实,这个方便的框架调用了 Struts,但是代码依赖于数据库。如果希望能够不止一次地运行这个测试,比如在构建过程中,就需要让它可重复

由于一些特定的假设,所以 清单 6 中的测试用例不是可重复的。首先,测试用例假设在系统中已经 有一个名为 “jane” 的用户,它的口令是 “admin”。其次,测试用例假设在某些永久存储 中口令 “admin” 被更新成 “meme”。正如所写的那样,只要代码没有生成异常,ActionForm 成功验证,Struts 就假定事情工作良好,测试用例也是一样。

现在需要的是更深层次的验证 —— 在数据库层次。对于应当更新口令的测试用例来说,理想情况下应当在数据库上 执行检查,确保那里有一个新口令。对于口令不应当修改的测试来说,需要进行验证,真正检验没有修改 口令。最后,要让这个测试套件可重复,最好是不要 对数据完整性做任何假设。

DbUnit 是个专门方便把数据库放进测试状态中已知状态的 JUnit 扩展。使用 XML 种子文件,可以把特定数据插入到测试用例可以依靠的数据库中。而且,使用 DbUnit API,可以容易地比较数据库的内容和 XML 文件的内容,从而提供一个在应用程序代码之外 校验预期数据库结果的机制。





回页首


用 DbUnit 进行测试

要使用 DbUnit,需要两样东西:

  • 通过普通 JDBC 的数据库连接
  • 一个文件,包含需要放到数据库中的数据

清单 7 是一个 DbUnit 种子文件,只定义了几样东西:首先,有一个叫做 user 的表和另一个叫做 user_role 的表。在 user 表中定义了一个新行,并映射一些值到列(例如列 username 拥有值 “jane”)。在 user_role 中还定义了一行。请注意这个数据库中的口令是通过 SHA 加密的


清单 7. 用于测试表 user 和 user_role 的 DbUnit 种子文件
<?xml version='1.0' encoding='WINDOWS-1252'?><dataset> <!-- user with password admin --> <user username="jane"    password="d033e22ae348aeb5660fc2140aec35850c4da997"    name="Jane Admin"   date_created="2003-8-14 10:10:10"   email="jane@elsewhere.org"/>  <user_role username="jane" rolename="ADMIN"/></dataset>

有了这个文件,就可以利用 DbUnit 插入数据、更新数据库来反映数据,甚至删除数据。数据库修改逻辑包含在 DbUnit 的 DatabaseOperation 类中。在这个示例中,只是通过 清单 4 中定义的 MockStrutsTestCase 类型的 setUp() 方法中的一些增强的 fixture 逻辑中的 CLEAN_INSERT 标志来保证干净的数据集。例如,在清单 8 中,定义了三个方法,分别利用 DbUnit API 把 dbunit-user-seed.xml 文件的内容插入数据库。


清单 8. 定制的 DbUnit fixture 逻辑
private void executeSetUpOperation() throws Exception{		 final IDatabaseConnection connection = this.getConnection(); try{  DatabaseOperation.CLEAN_INSERT.execute(connection, this.getDataSet()); }finally{  connection.close(); }}			private IDataSet getDataSet() throws IOException, DataSetException { return new FlatXmlDataSet(new File("test/conf/dbunit-user-seed.xml"));}private IDatabaseConnection getConnection() throws ClassNotFoundException, SQLException { final Class driverClass = Class.forName("org.gjt.mm.mysql.Driver"); final Connection jdbcConnection = DriverManager.   getConnection("jdbc:mysql://localhost/ccb01",      "9043", "43xli");                return new DatabaseConnection(jdbcConnection);}

清单 8 中定义的 executeSetUpOperation() 方法将在前面的 清单 4 中定义的 setUp() 方法中调用。这个方法再调用清单 8 中的另两个方法:getDataSet() 把 XML 转换成 DbUnit 的 IDataSet 类型,getConnection() 则返回包装成 DbUnit 的 IDatabaseConnection 类型的数据库连接。





回页首


更好的测试用例

配置好 DbUnit 后,剩下的就只有改进 清单 6 的测试用例,验证数据库中的一切 OK。然后,添加验证其他问题场景的其余测试用例。

要确认数据库中的口令更新,可以使用 DbUnit 的查询 API,它帮助比较数据库的结果与静态定义的 XML 文件,例如清单 9 中定义的那个。请注意这个文件没有列出 user 表中的所有列 —— 实际上,它只列出了两个:usernamepassword


清单 9. 比较测试 XML 文件
<?xml version='1.0' encoding='WINDOWS-1252'?><dataset> <user username="jane"     password="58117e24e4d0b8a958146c9eaa28336184f4d491"/> </dataset>

DbUnit 的查询 API 足够灵活,可以帮助过滤掉没有意义的值,在这个示例中就是 usernamepassword 之外的值。同样,在清单 10 中,verifyPassword() 方法用 DbUnit 的 createQueryTable() 方法构建 ITable 类型,以与清单 9 中定义的 XML 进行比较:


清单 10. 使用 DbUnit 查询 API 的 verifyPassword 方法
private void verifyPassword(String fileName) throws Exception{ final IDataSet expectedDataSet = new FlatXmlDataSet(   new File(fileName)); final ITable defJoinData = this.getConnection().  createQueryTable("TestResult",    "select user.username, user.password " +    "from user where user.username=\"jane\""); final ITable defTable = expectedDataSet.getTable("user"); Assertion.assertEquals(defJoinData, defTable);}

Assertion 类型是 DbUnit 定义的定制类,可以进行特定于数据库结果集的额外断言。还请注意 verifyPassword() 接受一个文件路径,这意味着我可以定义多个期望的数据集(一个用于修改的口令,一个用于相同的口令)。





回页首


反复测试 Struts

综合起来,现在有了一个可以完成以下工作的测试用例:

  1. 通过 DbUnit 填充数据库
  2. 配置 Struts
  3. 间接地调用 ChangePasswordActionChangePasswordForm
  4. 关联参数值
  5. 验证成功转发
  6. 验证数据库内容

从清单 11 可以看出,ChangePasswordAction 测试用例只通过 testExecute 测试处理一个正常场景:


清单 11. ChangePasswordAction 测试用例
package test.com.acme.ccb.action;import java.io.File;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import org.dbunit.Assertion;import org.dbunit.database.DatabaseConnection;import org.dbunit.database.IDatabaseConnection;import org.dbunit.dataset.DataSetException;import org.dbunit.dataset.IDataSet;import org.dbunit.dataset.ITable;import org.dbunit.dataset.xml.FlatXmlDataSet;import org.dbunit.operation.DatabaseOperation;import servletunit.struts.MockStrutsTestCase;public class ChangePasswordActionTest extends MockStrutsTestCase { public ChangePasswordActionTest(String arg0) {  super(arg0);		 } public void setUp() throws Exception {  try {   super.setUp();	   this.executeSetUpOperation();   this.setContextDirectory(new File("src/webapp/"));   this.setServletConfigFile("src/webapp/WEB-INF/web.xml");   this.setConfigFile(     this.getSession().getServletContext()        .getRealPath("WEB-INF/struts-config.xml"));  } catch (Exception e) {    fail("Unable to setup test");  }} public void testExecute() throws Exception{		   setRequestPathInfo("/changePasswordSubmit");		   addRequestParameter("username","jane");   addRequestParameter("currentPassword","admin");   addRequestParameter("newPassword1","meme");   addRequestParameter("newPassword2","meme");   actionPerform();		   verifyForward("success");	   verifyPassword("test/conf/dbunit-expect-user.xml"); } private void executeSetUpOperation() throws Exception{		  final IDatabaseConnection connection = this.getConnection();  try{    DatabaseOperation.CLEAN_INSERT.execute(connection, this.getDataSet());  }finally{    connection.close();  } }			 private IDataSet getDataSet() throws IOException, DataSetException {  return new FlatXmlDataSet(new File("test/conf/dbunit-user-seed.xml")); } private IDatabaseConnection getConnection() throws ClassNotFoundException, SQLException {  final Class driverClass = Class.forName("org.gjt.mm.mysql.Driver");  final Connection jdbcConnection = DriverManager.   getConnection("jdbc:mysql://localhost/ccb01",      "9043", "43xli");                 return new DatabaseConnection(jdbcConnection); } private void verifyPassword(String fileName) throws Exception{  final IDataSet expectedDataSet = new FlatXmlDataSet(   new File(fileName));  final ITable defJoinData = this.getConnection().   createQueryTable("TestResult",    "select user.username, user.password " +    "from user where user.username=\"jane\"");  final ITable defTable = expectedDataSet.getTable("user");  Assertion.assertEquals(defJoinData, defTable); }}

只多一个测试……

请注意这个测试用例没有测试边界用例,例如:如果两个口令字段(newPassword1newPassword2())不匹配。谢天谢地,一旦设置好了,添加另一个测试用例并不难。在清单 12 中,验证了如果两个值不匹配,就生成 ActionError,用户 “jane” 口令在数据库中的值保持不变


清单 12. 添加新测试
public void testExecuteWithErrors() throws Exception{		   setRequestPathInfo("/changePasswordSubmit");		   addRequestParameter("username","jane");   addRequestParameter("currentPassword","admin");   addRequestParameter("newPassword1","meme");   addRequestParameter("newPassword2","emem");   actionPerform();		   verifyActionErrors(     new String[]{"error.changePassword.passwordsDontMatch"});   verifyPassword("test/conf/dbunit-expect-user-same.xml"); }

在清单 12 中,我验证了 清单 2 中的逻辑正确地捕捉到了口令值不匹配的情况。MockStrutsTestCase 类包含一个方便方法可以断言错误条件,这个方法是 verifyActionErrors(),在这个方法中,错误 String 被传递进来进行验证。还请注意,数据库被检查,这次是根据另一个包含相同值的文件(在这个示例中,username “jane” 有一个未加密的 password “admin”)。





回页首


Struts 的集成测试

多数 Struts 应用程序不会 很快消失,所以重要的是知道如何在重写之前用开发人员测试构建一定层次的保证。这个月,我介绍了在测试 Struts 遗留应用程序时的一些挑战,并介绍了如何用 StrutsTestCase 和 DbUnit 处理它们。

StrutsTestCase 只要配置正确就会处理 Struts 的工作,而 DbUnit 处理与数据库有关的代码的工作。一起使用这两个框架,可以在 Struts 应用程序上进行集成级别的测试,而不用通过更高层次的框架(例如 JWebUnit 或 Selenium)模拟浏览器(也是一个值得采用的方法,但是生成的结果非常不同。)

Struts 应用程序对测试来说 具有挑战性的,并且没有解决的方法。这个困难是 Struts 框架被更加创新的框架所掩盖的原因之一,特别是那些解决了测试容易问题的框架。另一方面,就像我在这里介绍的,测试 Struts 可能的 —— 只是需要费些力气。

 

分享到:
评论

相关推荐

    精通Java EE:Eclipse Struts2 Hibernate Spring整合应用案例代码6/6

    精通Java EE:Eclipse Struts2 Hibernate Spring整合应用案例代码和数据库压缩包6

    Struts2 应用程序示例

    Struts2是一个强大的Java web应用程序框架,用于构建可维护、可扩展且结构良好的Web应用程序。这个"Struts2应用程序示例"是专为初学者和开发者设计的,它利用Eclipse 3.3作为集成开发环境,JDK 6.0作为Java运行环境...

    struts-config详解

    Struts-config详解 Struts-config.xml 是Struts框架...在struts应用程序中,struts-config.xml文件扮演着核心角色,它控制着struts应用程序的流程和逻辑。因此,了解struts-config.xml文件的配置和使用是非常重要的。

    struts2小程序 struts2代码

    Struts2是一个强大的Java web应用程序框架,用于构建和管理MVC(模型-视图-控制器)架构的应用。这个“struts2小程序”很可能是开发者利用Struts2框架开发的一个小型项目,可能包含了基本的CRUD操作或其他特定功能。...

    Struts2小程序源代码

    Struts2是一个非常流行的Java Web框架,用于构建和维护可扩展且易于管理的企业级应用程序。这个"Struts2小程序源代码"提供了丰富的学习资源,帮助开发者深入理解Struts2的核心概念和技术。 首先,我们来看看标题...

    项目实践精解:基于Struts-Spring-Hibernate的Java应用开发

    ### 项目实践精解:基于Struts-Spring-Hibernate的Java应用开发 #### 一、引言 随着信息化进程的推进,Web应用已成为现代软件工程的核心。Java作为一门广泛使用的编程语言,在Web应用开发领域占据着重要地位。然而...

    Struts2SpringUnitDemo单元测试

    Struts2SpringUnitDemo是一个示例项目,展示了如何在Java应用程序中将Struts2和Spring框架进行集成,并进行单元测试。这两个框架都是Java Web开发中的关键组件,Struts2负责控制层逻辑,Spring则提供了全面的依赖...

    Struts2框架程序示例

    通过学习和实践这个Struts2框架程序示例,开发者可以深入理解Struts2的工作原理,掌握如何创建Action、编写Interceptor、配置Action与Result的映射,以及如何利用Struts2的其他特性来提高开发效率和代码质量。...

    struts2介绍及应用

    Struts2是一个强大的Java Web应用程序框架,用于构建和维护可扩展、高效且易于维护的Web应用。它是Apache软件基金会下的一个开源项目,基于Model-View-Controller(MVC)设计模式,提供了高度灵活的控制层,使开发者...

    struts2实例小程序1

    Struts2是一个强大的Java web应用程序框架,用于构建和部署可维护、高性能的MVC(Model-View-Controller)架构的应用程序。在这个“Struts2实例小程序1”中,你将开始接触并理解Struts2的基本概念和工作流程,这对于...

    Struts 原理 与 应用

    Struts 是一个开源的Java Web框架,主要用于构建基于MVC(模型-视图-控制器)设计模式的Web应用程序。这个框架旨在提高应用的结构化和可维护性,它是在J2EE平台上发展起来的,特别是在JSP Model 2的基础上进行了...

    struts2学习测试代码,struts2学习测试代码2

    struts2学习测试代码,struts2学习测试代码2struts2学习测试代码,struts2学习测试代码

    利用Myeclipse开发struts应用程序

    Struts 是一个经典的Java Web开发框架,用于构建基于MVC(模型-视图-控制器)模式的应用程序。MyEclipse 是一个集成开发环境(IDE),它提供了对Struts框架的强大支持,包括图形化的Struts Designer工具,使得开发...

    moke测试struts的action

    Struts主要用于构建基于MVC(Model-View-Controller)设计模式的Web应用程序,以提高开发效率和代码的可维护性。在标题“moke测试struts的action”中,我们关注的焦点是Struts框架中的Action组件。 Action是Struts...

    一个Action多方法调用的Struts 2的应用程序

    利用Struts 2框架创建一个web项目chap2_e22,实现用户登录过程。具体要求是在loginAction类中分别用login()和registered()处理用户登录和注册的过程,分别创建login.jsp和register.jsp两个页面实现登录和注册的...

    语言程序设计资料:struts2基本配置使用手册.doc

    Struts 2.0 作为一款功能强大且广泛应用的 Web 框架,其优点包括 MVC 2 模型的使用、功能齐全的标志库(Tag Library)和开放源代码。然而,Struts 也存在一些缺点,如需要编写的代码过多、单元测试困难等。为了解决...

    Struts2单元测试

    Struts2是一个流行的Java web应用程序框架,用于构建和维护可扩展、模块化且易于管理的MVC(模型-视图-控制器)架构的应用程序。在软件开发过程中,单元测试是确保代码质量的重要环节,它允许开发者独立地测试代码的...

Global site tag (gtag.js) - Google Analytics