Java 单元测试与 Web 服务:JUnit、JWebUnit 及 Axis 框架详解
1. 单元测试与 JUnit
1.1 单元测试方法示例
以 addOrder() 方法的单元测试为例,在订单添加完成后,从数据库中检索记录,以此确保数据值的准确性。当运行该测试用例时,由于它继承自购物车测试用例,所以会同时运行其父类的 testGetCartTotal() 测试用例。
1.2 边界测试用例构建
构建边界测试用例颇具复杂性,因为需要大量手动编写的 SQL。为解决这一问题,可以使用辅助类来消除代码的冗余。比如,构建一个 JDBCFixture 类,该类能够封装与数据库交互的大部分通用细节。此外,也可以利用通常用于客户端/服务器开发的组件来简化测试代码的生成。许多集成开发环境(IDE)都包含封装了 JDBC 大部分复杂性的组件。虽然在 Web 应用程序中使用这些组件可能会带来额外开销,但在单元测试中,开发速度更为重要,可扩展性和开销则是次要考虑因素。
1.3 JUnit 辅助工具
JUnit 网站上有一个名为 DbUnit 的辅助类集合,它能自动完成许多边界类的测试工作。若不想自己编写数据库访问代码,DbUnit 可以轻松生成针对关系型数据库的测试代码。
1.4 IDE 对 JUnit 的支持
众多商业和开源的 IDE 都支持 JUnit,它在 Java 开发领域已变得极为普遍。IDE 的支持涵盖了从预定义测试用例模板到在 IDE 内部运行测试的测试运行器等多个方面。
- JBuilder 的 JUnit 支持 :JBuilder 的“新建”库中包含一整页的预建 JUnit 测试类,并且它还提供了自己的图形化测试运行器。
- NetBeans 的 JUnit 支持 :NetBeans IDE 在测试生成和运行方面都支持 JUnit。对于任何类,只需右键单击,选择“工具”,NetBeans 就能为你生成 JUnit 测试。它还有一个基于 JUnit 文本测试运行器的自定义测试运行器。
1.5 自动化回归测试
为充分发挥单元测试的优势,必须将单元测试作为回归测试来运行。但没人愿意整天坐在电脑前运行回归测试。开源工具 Ant 可以帮助解决这个问题,其可选任务中包含一个 JUnit 任务。使用 Ant,你可以设置一个构建文件,在夜间运行多个测试套件的单元测试。根据自动化程度的需求,还可以让 Ant 运行测试,并将失败的测试列表通过电子邮件发送给你,以便第二天早上处理。以下是一个 Ant 调用 JUnit 任务的示例:
<junit printsummary="withOutAndErr" haltonfailure="yes" fork="true">
<classpath>
<pathelement location="${build.tests}" />
<pathelement path="${java.class.path}" />
</classpath>
<formatter type="plain" />
<test name="my.test.TestCase" haltonfailure="no"
outfile="result" >
<formatter type="xml" />
</test>
<batchtest fork="yes" todir="${reports.tests}">
<fileset dir="${src.tests}">
<include name="*Test.java" />
<exclude name="**/AllTests.java" />
</fileset>
</batchtest>
</junit>
2. Web 测试与 JWebUnit
2.1 Web 应用测试难题
Web 应用程序的单元测试是一项极具挑战性的任务。Web 应用依赖于浏览器这一部署平台,而开发者无法完全掌控浏览器。同时,Web 应用具有很强的视觉组件,自动化测试难度较大。市面上有一些商业产品可用于测试 Web 应用,它们通常允许用户与应用程序交互,记录按键和鼠标操作,然后回放这些记录以模拟用户交互,但这些工具专业性强且价格昂贵。
2.2 开源 Web 测试工具
开源社区也在积极应对这一问题。JUnit 项目的一个附属项目 HttpUnit 扩展了 JUnit,构建了一个用于测试基于 HTTP 运行的应用程序的框架,能有效验证实际输出是否符合预期。还有一些开源工具专注于测试 Web 应用的原子行为,例如测试网页上的 JavaScript。
2.3 JWebUnit 简介
JWebUnit 是 JUnit 网站上出现的一个新项目,它整合了许多现有的开源 Web 测试框架,包括 HttpUnit。它是一个开源测试框架,封装了许多现有开源工具,形成了一个更全面的测试包。同时,它还提供了新的类来封装许多现有的 HttpUnit 类,从而减少开发者需要编写的代码量。你可以从 JUnit 网站下载 JWebUnit,同时也应下载 HttpUnit,因为 JWebUnit 依赖于 HttpUnit 的一些类。
2.4 JWebUnit 测试用例
由于 JWebUnit 基于 JUnit,所以测试用例、测试套件和测试夹具的概念是相同的。以下以 eMotherEarth 应用程序的几个页面测试为例进行说明。
2.4.1 基础测试用例
所有 JWebUnit 测试用例都有一个共同的设置项,即 BaseURL,其他 URL 都基于此 URL。为避免在多个测试用例中重复相同的设置代码,可以创建一个基础测试用例 BaseWebTestCase 来处理这个设置任务:
package com.nealford.art.emotherearth.test;
import net.sourceforge.jwebunit.WebTestCase;
public class BaseWebTestCase extends WebTestCase {
public BaseWebTestCase(String name) {
super(name);
}
public void setUp() throws java.lang.Exception {
super.setUp();
getTestContext().setBaseUrl(
"http://localhost:8080/emotherearth");
}
}
2.4.2 登录页面测试用例
创建一个 TestLogonPage 测试用例,用于测试登录页面,确保页面上出现正确的元素,并能成功转发到目录页面:
package com.nealford.art.emotherearth.test;
public class TestLogonPage extends BaseWebTestCase {
public TestLogonPage(String name) {
super(name);
}
public void testIntro() {
beginAt("/welcome");
}
public void testLogonElements() {
beginAt("/welcome");
assertFormPresent("welcomeform");
assertFormElementPresent("user");
assertFormElementPresent("gotocatalog");
}
public void testForwardToCatalog() {
beginAt("/welcome");
setFormElement("user", "Homer");
submit();
}
}
HttpUnit 扩展了 JUnit 的标准断言方法,包含了特定于 Web 的断言。 testLogonElements() 方法用于检查页面上是否存在所需元素,而 testForwardToCatalog() 方法允许开发者以编程方式与应用程序进行交互,如填写表单值、“点击”按钮等。
2.4.3 测试运行注意事项
JUnit 定义的测试运行器同样适用于 JWebUnit,但在进行测试之前,Web 应用程序必须处于运行状态,即测试运行器不会自动启动 Web 应用程序。测试结果与其他 JUnit 测试一样,会显示绿色和红色条,且整个过程中不会看到与 Web 应用程序的交互,所有代码都是通过 HTTP 直接访问应用程序。
2.5 复杂元素测试
JWebUnit 包含用于测试复杂 HTML 元素(如表格)的方法。以下是一个测试目录页面表格属性的测试用例:
package com.nealford.art.emotherearth.test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class TestCatalogPage extends BaseWebTestCase {
private static String LOG_DIR = "c:/temp/emotherearth/";
public TestCatalogPage(String name) {
super(name);
}
public void setUp() throws java.lang.Exception {
super.setUp();
File outputDir = new File(LOG_DIR);
if (!outputDir.exists())
outputDir.mkdir();
}
public void testCatalog() {
beginAt("/welcome");
beginAt("/catalog?user=Homer");
assertTablePresent("catalogTable");
assertTextInTable("catalogTable",
new String[] {"ID", "NAME", "PRICE", "Buy"});
PrintStream ps = null;
try {
ps = new PrintStream(new FileOutputStream(
"c:/temp/emotherearth/catalogText.txt"));
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}
dumpTable("catalogTable", ps);
}
}
testCatalog() 方法首先调用两次 beginAt() 方法,因为欢迎页面会执行建立连接池和其他全局资源的代码,所以不能直接创建一个直接访问目录页面的测试用例。JWebUnit 提供了确保表格存在以及检查表格行内容的方法, assertTextInTable() 方法用于验证表头行是否包含正确的元素。此外, dumpTable() 方法可以将整个表格的内容输出到文件中,方便进行回归测试时对比不同运行结果的内容。
以下是 dumpTable() 方法输出的 catalogText.txt 文件内容示例:
catalogTable:
[ID][NAME][PRICE][Buy]
[1][Ocean][$1,393,456,200.00][Qty:]
[2][Leaves (green)][$3.50]Qty:]
[3][Leaves (brown)]$0.0]Qty:]
[4][Mountain]$2,694,381.3]Qty:]
[5][Lake]$34,563.1]Qty:]
[6][Snow]$2.4]Qty:]
通过自动化测试 Web 应用程序,即使生成的输出与原始表格外观不同,但包含相同的数据。使用 diff 工具比较不同运行结果的文件内容,无需查看表格,就能发现输出的变化并进行进一步调查。这种方式比手动查看视觉输出进行测试更加可靠和高效。
2.6 总结
单元测试虽然在开发项目中并非最引人注目的工作,但却是开发生命周期中至关重要的一环。JUnit 作为一个开源项目,使用方便,在 Java 开发中几乎被普遍采用。它使得除了 Servlet 和 Web 用户界面之外的单元测试变得相对容易。而基于 JUnit 的 JWebUnit 则提供了强大的框架,用于自动化 Web 应用程序视觉部分的一致性和有效性测试,这部分通常是最难手动测试的。
3. Web 服务与 Axis
3.1 Web 服务的重要性
在过去几年里,Web 服务成为了行业内的热门话题。作为 Web 开发者,未来很可能会参与到需要支持 Web 服务的项目中。
3.2 Web 服务的关键概念
3.2.1 远程过程调用(RPC)与现有协议
Web 服务代表了一种执行远程过程调用(RPC)和无状态消息传递的新范式。RPC 已经存在多年,为了在不同机器之间分担处理负载,出现了多种协议,如组件对象模型(COM)/分布式 COM(DCOM)、公共对象请求代理体系结构(CORBA)、远程方法调用(RMI)。然而,这些协议在广泛应用方面面临着巨大障碍,一方面是大家无法就使用哪种协议达成一致,另一方面,它们都使用二进制协议在网络上传输信息,这在内部网络应用中可能不是问题,但对于 Internet 应用来说,需要在防火墙上开放二进制数据端口,这会带来安全风险。
3.2.2 Web 服务 API 的诞生
为解决上述问题,一些公司联合定义了 Web 服务 API,它部分基于现有的 XML、HTTP 等开放标准。Microsoft、DevelopMentor 和 Userland Software 创建了一个基于 XML 的协议,用于在 HTTP 上传递过程调用信息,并提交给 Internet 工程任务组(IETF)推荐,随后被万维网联盟(W3C)迅速采用,Web 服务由此诞生。当版本 1.1 发布时,许多大公司加入该项目,使其成为事实上的标准。
3.2.3 Web 服务的组成
Web 服务是一个统称,它使用简单对象访问协议(SOAP)作为数据编组机制,通过 HTTP 进行 RPC。Web 服务标准还包括 Web 服务描述语言(WSDL),用于提供远程方法的元数据信息;以及通用描述、发现和集成(UDDI)标准,用于查找 Web 服务,类似于 Web 服务的电话簿。
3.2.4 Web 服务的无状态特性
Web 服务基于 HTTP 等现有标准协议,因此本质上是无状态的。与 Web 应用程序一样,不能依赖对象在不同调用之间的状态。无状态特性有利于可扩展性,但也限制了部分代码的编写方式。虽然有些框架允许创建有状态的 Web 服务调用,但这会带来状态管理问题,需要明确谁负责维护状态。一般来说,Web 服务方法必须全面,能够在一次调用中完成所有必要的工作。
3.3 Axis 框架
在 Java 中使用 Web 服务有多种框架可供选择,这里介绍的是 Apache 的开源 Axis(Apache Extensible Interaction System)框架,可从 http://ws.apache.org/axis 下载。
综上所述,无论是单元测试、Web 应用测试还是 Web 服务的使用,都有相应的工具和框架来帮助开发者提高开发效率和软件质量。在实际开发中,合理运用这些工具和技术,能够更好地应对各种开发挑战。
以下是一个简单的 mermaid 流程图,展示 Web 服务的基本调用流程:
graph LR
A[客户端] -->|发送请求| B[Web 服务端]
B -->|处理请求| C[执行相关操作]
C -->|返回结果| B
B -->|返回响应| A
通过这个流程图,可以更直观地理解 Web 服务的调用过程。同时,我们也可以用表格来总结不同测试工具和 Web 服务相关概念的特点:
| 工具/概念 | 特点 |
| — | — |
| JUnit | 开源,使用方便,广泛用于 Java 单元测试 |
| JWebUnit | 基于 JUnit,封装多种开源工具,用于 Web 应用测试 |
| Web 服务 | 基于开放标准,使用 SOAP 进行 RPC,包含 WSDL 和 UDDI |
| Axis | 开源框架,用于在 Java 中使用 Web 服务 |
3.4 Axis 的使用场景与优势
Axis 框架在 Java 开发中使用 Web 服务时具有显著的优势和广泛的应用场景。
3.4.1 优势
- 开源免费 :作为开源框架,Axis 允许开发者自由使用、修改和分发,降低了开发成本。
- 广泛支持 :它基于开放标准构建,与多种 Web 服务技术兼容,能够处理不同格式的消息和协议。
- 易于集成 :可以方便地集成到现有的 Java 项目中,无需进行大规模的代码修改。
- 性能优化 :经过优化的实现,能够提供高效的消息处理和传输,减少响应时间。
3.4.2 使用场景
- 企业级应用 :在企业内部系统之间进行数据交互和业务流程整合时,Axis 可以帮助实现不同系统之间的远程调用。
- 跨平台通信 :由于 Web 服务基于标准协议,Axis 可以实现不同平台和语言之间的通信,促进系统的互操作性。
- 分布式系统 :在分布式系统中,Axis 可以作为服务提供者和消费者之间的桥梁,实现服务的注册、发现和调用。
3.4.3 使用 Axis 开发 Web 服务的步骤
以下是使用 Axis 开发 Web 服务的基本步骤:
1. 下载和配置 Axis :从 http://ws.apache.org/axis 下载 Axis 框架,并将其配置到项目的类路径中。
2. 定义服务接口 :创建一个 Java 接口,定义 Web 服务的方法和参数。
public interface HelloService {
String sayHello(String name);
}
- 实现服务接口 :创建一个类来实现服务接口。
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello, " + name + "!";
}
}
- 部署服务 :将服务类部署到 Axis 服务器中。可以通过配置文件或编程方式进行部署。
<service name="HelloService">
<parameter name="className" value="com.example.HelloServiceImpl"/>
<parameter name="allowedMethods" value="*"/>
</service>
- 发布服务 :启动 Axis 服务器,将服务发布到网络上。客户端可以通过 URL 访问服务。
import org.apache.axis.client.Service;
import org.apache.axis.client.Call;
import javax.xml.namespace.QName;
public class HelloServiceClient {
public static void main(String[] args) {
try {
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(new java.net.URL("http://localhost:8080/axis/services/HelloService"));
call.setOperationName(new QName("http://example.com", "sayHello"));
String result = (String) call.invoke(new Object[] {"World"});
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.5 现有 Web 应用改造以支持 Web 服务
对于现有的 Web 应用程序,需要进行一些改造才能支持 Web 服务。以下是改造的主要步骤:
3.5.1 分析需求
确定哪些功能需要以 Web 服务的形式暴露,以及对现有应用的影响。
3.5.2 封装业务逻辑
将需要暴露的业务逻辑封装到独立的类中,确保这些类可以作为 Web 服务的实现。
3.5.3 配置 Axis
在现有项目中集成 Axis 框架,配置服务的部署和发布。
3.5.4 测试和验证
对改造后的应用进行测试,确保 Web 服务的功能正常,并且与现有应用的兼容性良好。
以下是一个 mermaid 流程图,展示现有 Web 应用改造以支持 Web 服务的流程:
graph LR
A[分析需求] --> B[封装业务逻辑]
B --> C[配置 Axis]
C --> D[测试和验证]
D -->|成功| E[上线使用]
D -->|失败| B
3.6 总结与展望
Web 服务作为一种新的技术范式,为企业级应用开发带来了新的机遇和挑战。通过使用 JUnit 进行单元测试、JWebUnit 进行 Web 应用测试,以及 Axis 框架来实现 Web 服务,开发者可以提高代码质量、增强系统的可维护性和互操作性。
在未来的开发中,随着技术的不断发展,Web 服务将更加普及和成熟。开发者需要不断学习和掌握新的技术和工具,以应对日益复杂的开发需求。同时,也需要关注安全、性能和兼容性等方面的问题,确保系统的稳定运行。
以下是一个表格,总结了不同阶段的开发工具和技术的作用:
| 开发阶段 | 工具/技术 | 作用 |
| — | — | — |
| 单元测试 | JUnit | 验证代码的正确性,提高代码质量 |
| Web 应用测试 | JWebUnit | 自动化测试 Web 应用的视觉部分和功能 |
| Web 服务开发 | Axis | 实现 Web 服务的创建、部署和调用 |
通过合理运用这些工具和技术,开发者可以构建出更加健壮、高效和可扩展的 Java 应用程序。
超级会员免费看

被折叠的 条评论
为什么被折叠?



