对 Ajax 应用程序进行单元测试

发表于:2008-04-03来源:作者:点击数: 标签:单元测试AjaxAJAXajax
您可能从编写 Ajax 应用程序中获得了极大乐趣,但是对它们执行 单元测试 却着实让人头痛。 在本文中,Andrew Glover 着手解决 Ajax 的弱点(其中之一),即应对异步 Web 应用程序执行单元测试的固有挑战。幸运的是,他发现在 Google Web Toolkit 的帮助下,
您可能从编写 Ajax 应用程序中获得了极大乐趣,但是对它们执行单元测试却着实让人头痛。 在本文中,Andrew Glover 着手解决 Ajax 的弱点(其中之一),即应对异步 Web 应用程序执行单元测试的固有挑战。幸运的是,他发现在 Google Web Toolkit 的帮助下,解决这个特殊的代码质量问题要比预想的容易。

Ajax 在近期无疑是 Web 开发界最时髦的字眼之一 —— 与 Ajax 相关的工具、框架、书籍以及 Web 站点的剧增就是该技术流行的最好证明。此外,Ajax 应用程序也相当灵巧,不是吗?不过,像任何一个开发过 Ajax 应用程序的人证实的一样,对 Ajax 执行测试真的很不方便。事实上,Ajax 的出现已经从根本上使得许多测试框架和工具失效,因为它们并没有针对异步 Web 应用程序测试进行设计!

有趣的是,某个支持 Ajax 的框架的开发人员注意到了这个限制,并为此做了一些非常新颖的设计:内置的可测试性。除此之外,由于该框架简化了使用 Java™ 代码(而不是 JavaScript)创建 Ajax 应用程序,它的起点甚高,并且充分利用了 Java 平台上无可置疑的标准测试框架:JUnit。

我所论及的框架当然是非常流行的 Google Web Toolkit,也就是 GWT。在本文中,我将向您展示 GWT 如何实际地利用 Java 兼容性,使 Ajax 应用程序的每个部分都能像与之对应的同步应用程序一样进行测试。

JUnit 和 GWTTestCase

因为与 GWT 有关的 Ajax 应用程序采用 Java 代码编写,所以非常适合开发人员使用 JUnit 进行测试。事实上,GWT 开发小组还为此创建了一个帮助器类 GWTTestCase,扩展自 JUnit 的 3.8.1 TestCase。该基类添加了一些功能,可测试 GWT 代码并处理某些基础实现从而启动并运行 GWT 组件。

Google Web Toolkit
Google Web Toolkit 在 Java Web 开发社区的发布声势浩大,同时也获得了与之相称的巨大轰动。GWT 为利用 Java 代码进行设计、构建和部署支持 Ajax 的 Web 应用程序提供了一种新颖的方式。Java Web 开发人员不再需要学习 JavaScript 并花费数个小时解决特定于浏览器的问题,他们可以直接进行与 Ajax 有关的富含信息的动态 Web 应用程序设计。

需要提醒的是:GWTTestCase 并非用来测试与 UI 相关的代码 —— 它是为了便于测试那些由 UI 交互触发 的异步问题。对 GWTTestCase 用途的误解使许多刚接触 GWT 的开发人员备受挫折,因为他们期望能够用它方便地模拟用户界面,但最终发现这是徒劳的。

Ajax 组件有两个基本组成:体验和功能,这些都被设计成异步方式。图 1 演示了一个模拟 Web 表单的简单 Ajax 组件。由于该组件支持 Ajax,表单的提交是异步执行的(即:无需重新载入与传统表单提交关联的页面)。


图 1. 一个支持 Ajax 的简单 Web 表单
一个支持 Ajax 的简单 Web 表单。

输入一个有效单词,单击组件的 Submit 按钮,将向服务器发送消息请求该单词的定义。该定义通过回调异步返回,相应地插入到 Web 页面,如图 2 所示:


图 2. 单击 Submit 按钮后显示响应
单击 Submit 按钮后显示响应。

功能性和集成测试

图 2 所示的交互测试可用于多个不同场景,但是其中两种场景最为常见。从功能性观点考虑,您或许希望编写一个测试:填入表单值,单击 Submit 按钮,然后验证表单是否显示定义。另外一个选择是集成测试,使您能够验证客户端代码的异步功能。GWT 的 GWTTestCase 正是被设计用来执行此类测试。

需要牢记的是:在 GWTTestCase 测试用例环境下不可以进行用户界面测试。在设计和构建 GWT 应用程序时,您必须清楚不要依赖用户界面 测试代码。这种思路需要把交互代码从业务逻辑中分离出来,正如您已经了解的,这是最佳的入门实践!

举例而言,重新查看图 1 和图 2 所示的 Ajax 应用程序。该应用程序由四个逻辑部分构成:TextBox 用于输入目标单词,Button 用于执行单击,还有两个 Label(一个用于 TextBox,另一个显示定义)。实际 GWT 模块的初始方法如清单 1 所示,但是您该如何测试这段代码呢?


清单 1. 一个有效的 GWT 应用程序,但是如何测试它?
                public class DefaultModule implements EntryPoint { public void onModuleLoad() {   Button button = new Button("Submit");   TextBox box = new TextBox();   Label output = new Label();   Label label = new Label("Word: ");   HorizontalPanel inputPanel = new HorizontalPanel();   inputPanel.setStyleName("input-panel");   inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);   inputPanel.add(label);   inputPanel.add(box);   button.addClickListener(new ClickListener() {    public void onclick(Widget sender) {      String word = box.getText();      WordServiceAsync instance = WordService.Util.getInstance();       try {         instance.getDefinition(word, new AsyncCallback() {                      public void onFailure(Throwable error) {             Window.alert("Error oclearcase/" target="_blank" >ccurred:" + error.toString());           }           public void onSuccess(Object retValue) {             output.setText(retValue.toString());           }		  });        }catch(Exception e) {          e.printStackTrace();		}	}   });   inputPanel.add(button);   inputPanel.setCellVerticalAlignment(button,     HasVerticalAlignment.ALIGN_BOTTOM);   RootPanel.get("slot1").add(inputPanel);   RootPanel.get("slot2").add(output);   }}

清单 1 的代码在运行时发生了严重的错误:它无法按照 JUnit 和 GWT 的 GWTTestCase 进行测试。事实上,如果我试着为这段代码编写测试,从技术方面来说它可以运行,但是无法按照逻辑工作。考虑一下:您如何对这段代码进行验证?惟一可用于测试的 public 方法返回的是 void, 那么,您怎么能够验证其功能的正确性呢?

如果我想以白盒方式验证这段代码,就必须分离业务逻辑和特定于用户界面的代码,这就需要进行重构。这本质上意味着把清单 1 中的代码分离到一个便于测试的独立方法中。但是这并非听上去那么简单。很明显组件挂钩是通过 onModuleLoad() 方法实现,但是如果我想强制其行为,可能 必须操纵某些用户界面(UI)组件。

原文转自:http://www.ltesting.net