阳光男孩

Never give up!

Entries Tagged ‘Ajax’

Ajax开发10条标准守则

0顶一下AJAX 是一种独立于 Web 服务器软件的浏览器技术。AJAX 基于下列 Web 标准:JavaScript XML HTML CSS 在 AJAX 中使用的 Web 标准已被良好定义,并被所有的主流浏览器支持。AJAX 应用程序独立于浏览器和平台。Web 应用程序较桌面应用程序有诸多优势;它们能够涉及广大的用户,它们更易安装及维护,也更易开发。 不过,因特网应用程序并不像传统的桌面应用程序那样完善且友好。 通过AJAX,因...[阅读全文]

0
顶一下

AJAX 是一种独立于 Web 服务器软件的浏览器技术。AJAX 基于下列 Web 标准:JavaScript XML HTML CSS 在 AJAX 中使用的 Web 标准已被良好定义,并被所有的主流浏览器支持。AJAX 应用程序独立于浏览器和平台。Web 应用程序较桌面应用程序有诸多优势;它们能够涉及广大的用户,它们更易安装及维护,也更易开发。

不过,因特网应用程序并不像传统的桌面应用程序那样完善且友好。 通过AJAX,因特网应用程序可以变得更完善,更友好。下面为大家介绍Ajax开发守则。

1.前、端后都要做好安全的把关工作

不能单靠前端做安全验证工作,后端也必须有过滤机制,检验前方传来的资料。由于Ajax也会接收后端传送来的资料,对这些资料都要采不信任态度,必须加以检查。

2.尽量以HTTP POST方法传输资料

使用GET方法容易让有心人士得到资料,虽然POST并非万无一失,但比起GET至少较为安全。

3.不要直接用eval函式唤起JSON物件

JSON是采用JavaScript物件实字的资料格式,从后端传到Ajax程式时,必须采用eval函式将它从字串转成物件,这时必须先行检查资料中是否有非法字元,以免非法程式借机启用。

4.限制使用者可用的HTML语法

许多Ajax网站允许使用者在留言中使用HTML语法,而某些特定语法应该严格限制,例如<plaintext>或是<!- -这类语法,都会让置于之后的程式码失效。

5.禁止可留言的网页页面使用JavaScript语法

虽然乍看之下觉得不可思议,不过的确在一些部落格平台上发生过,这让XSS攻击开了大门。

6.使用Ajax框架,必须注意安全性问题

由于骇客利用JavaScript进行攻击手法日新月异,当网页开发人员利用Ajax框架时,必须注意框架本身是否对某些特定手法具有防治行为。

7.让使用者知道自己身处的状况

当Ajax载入资料量大时,有时会让使用者产生错觉,不确定刚刚按下的按扭是否发生作用。因此开发人员必须设计适当的提示,让使用者了解目前程式的执行状况。

8.保持小量传输

Ajax的灵活性在于动态改变局部资料量,因此小量传输资料才能达到最高效益。如果一大张资料表都要透过DOM的操作来改变,不如就由后端程式与资料库来操作,效益更高。

9.注意易用性与内容的平衡

Ajax虽然为使用者带来浏览时的易用性,不过如果将所有内容都透过Ajax来产生,会造成搜寻引擎无法索引资料的情况。因此与内容相关的部分,需谨慎使用Ajax。

10.执行优雅降级原则

网站如果并非全面性采用Ajax技术,而只是为了局部增加互动性,这时必须考量不支援JavaScript的使用者,设法在不使用这些功能的前提下,让他们还能正常地浏览网站。

Comments (5)

Ajax中文乱码问题及解决方法

0顶一下java认证备考:Ajax中文乱码问题及解决方法,data:{id:1, type:encodeURI(encodeURI(‘商品’))}2.在后台action里要对取得的字符串进行decode。   场景: 使用jQuery的ajax方法提交ajax请求,代码如下: 1$.ajax({ 2    dataType : ‘json’ 3    ,type : ‘POST’ 4    ,url : ‘http://localhost/test/test.do’ 5    ,data : {...[阅读全文]

0
顶一下

java认证备考:Ajax中文乱码问题及解决方法,data:{id:1, type:encodeURI(encodeURI(‘商品’))}2.在后台action里要对取得的字符串进行decode。

 

场景:

使用jQuery的ajax方法提交ajax请求,代码如下:

1$.ajax({

2    dataType : ‘json’

3    ,type : ‘POST’

4    ,url : ‘http://localhost/test/test.do’

5    ,data : {id: 1, type: ‘商品’}

6    ,success : function(data){

7

8    }

9});

问题:

提交后后台action程序时,取到的type是乱码

解决方法:

方法一:提交前采用encodeURI两次编码,记住一定是两次

1.修改以下代码

data:{id:1, type:encodeURI(encodeURI(‘商品’))}2.在后台action里要对取得的字符串进行decode

1String type = request.getParameter(“type”);

2type = URLDecoder.decode(type, “UTF-8″);

方法二:ajax配置contentType属性,加上charset=UTF-8

在ajax方法中加入以下参数

1contentType: “application/x-www-form-urlencoded; charset=UTF-8″使用其它js框架或者xhr都是差不多,设置header中contentType即可,

这里关键是charset=UTF-8,如果没有这个,是不行的,默认jQuery里的contentType是没有的.

还补充一下jQuery里对参数已经进行了一次encodeURIComponent的处理

*方法二在action里不需要进行decode,所以推荐使用此方法

 

Comments (26)

jquery+ajax+xml+php简单留言板

0顶一下话不多说,如果哦看不动的去论坛上发帖 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml”> <head> <title>无刷jquery php xml 留言板版</title> <meta. http-equiv=”Content-Type” content=”text/html; charset=utf-...[阅读全文]

0
顶一下

话不多说,如果哦看不动的去论坛上发帖

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<title>无刷jquery php xml 留言板版</title>
<meta. http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<style>
* { margin:0; padding:0}
ul {list-style.:none}
#msglist{width:800px; vertical-align:text-top; margin:20px auto 0px auto; }

dt{background:url(wtp-m.png);padding:5px}
dl{border:solid 1px #8db2e3}
dd{padding:5px}
#page span{cursor: pointer;}
#commentformbox{width:800px;vertical-align:text-top;margin:20px auto 0px auto; }
#commentformbox TEXTAREA{width:400px;height:100px;vertical-align:text-top;margin:20px auto 0px auto; }
</style>
<SCRIPT. LANGUAGE=”JavaScript” src=”js/jquery-1.2.6.js”></script>
<SCRIPT. LANGUAGE=”JavaScript”>
<!–
$(document).ready(function() {

function load_msg(page)
{
$(‘#state’).html(“当前状态:正在加载数据请稍等….”);
page=$(this).html();
page=page==”undefined”?1:page;

$.post(“comments-ajax.php”,{action:”load”,page:page},function(data){
//alert($(data).find(“span”).text());

$(‘#commentlist dl’).html(data); // 追加留言数据
$(‘#commentlist dl’).find(“span”).bind(“click”,load_msg);
$(‘#state’).html(“当前状态:数据加载完毕”);
})

}

load_msg();
if ($(‘#commentform’).length) {
$(‘#commentform’).submit(function(){
jQuery.ajax({
url: ‘comments-ajax.php’, // 这里要改为 comments-ajax.php 文件的位置
data: $(‘#commentform’).serialize(), // 从表单中获取数据
type: ‘POST’, // 设置请求类型为 ‘POST’,默认为 ‘GET’
beforeSend: function() {
$(‘#state’).html(“当前状态:正在发送数据。。。。”);
},
error: function(request) {
$(‘#state’).html(request.responseText);
},
success: function(data) {
$(‘textarea’).each(function(){
this.value=”;
});

$(‘#state’).html(“当前状态:发布数据成功”);
$(‘#commentlist dl’).prepend(data); // 追加留言数据
$(‘#commentform.:input’).attr(‘disabled’, true);
$(‘#commentformbox’).fadeOut(1000);
$(‘#commentload’).hide();
setTimeout(function() { // 提交留言 15 秒后方可再次提交新留言
$(‘#commentform.:input’).removeAttr(‘disabled’);
$(‘#commentformbox’).fadeIn(15000);
}, 5000);
}
});
return false;
});
}
})

//–>
</SCRIPT>
<body>
<div ID=”state”>当前状态</div>
<div ID=”msglist”>
<div id=”commentlist”>
<dl>

</dl>
</div>
</div>

<div id=commentformbox>
<FORM. METHOD=POST ACTION=”" ID=”commentform”>
<TEXTAREA NAME=”Content” ROWS=”" COLS=”"></TEXTAREA><br><INPUT TYPE=”submit” value=”发布信息”>
</FORM>
</div>
</body>

comments-ajax.php

<?php
//==========================================
// 文件名: comments-ajax.php
// 说   明: 简单的jquery php XML 留言簿
// power by cnjquery.com
//==========================================
if ($_SERVER["REQUEST_METHOD"] != “POST”) {
header(‘Allow: POST’);
header(“HTTP/1.1 405 Method Not Allowed”);
header(“Content-type: text/plain”);
exit;
}

class guestbook extends DOMDocument
{
var $pagenum=10;

public function __construct()
{
parent:: __construct();
if (!file_exists(“guestbook.xml”))
{
$xmlstr = “<?xml version=’1.0′?><msglist></msglist>”;
$this->loadXML($xmlstr);
$this->save(“guestbook.xml”);
}
else
$this->load(“guestbook.xml”);
}
public function loadMessage($page)
{
$page=($page>0)?$page:1;
$num=($page-1)*$this->pagenum;
$roots = $this->getElementsByTagName( “msglist” );
//print_r($roots);
$root=$roots->item(0);
$msg=$root->getElementsByTagName( “msg” );
$total=$msg->length;
// echo $total;
$totalpage=ceil($total/$this->pagenum);
$totalpage=$totalpage?$totalpage:1;
$startid=$total<$num?$total:$total-$num;
$listnum=$startid>$this->pagenum?$startid-$this->pagenum:0;
//echo $startid;
//echo $listnum;
for($i=$startid-1;$i>=$listnum;$i–)
{

echo “<dt> &nbsp;&nbsp;ip:”.$msg->item($i)->getElementsByTagName(“ip”)->item(0)->nodeValue.”&nbsp;&nbsp;time:”.$msg->item($i)->getElementsByTagName(“time”)->item(0)->nodeValue.”</dt><dd>”.trim(nl2br($msg->item($i)->getElementsByTagName(“content”)->item(0)->nodeValue)).”</dd>”;
}
echo “<dt ID=page>”;
for($i=1;$i<=$totalpage;$i++)
{
echo “&nbsp;&nbsp;<span>”.$i.”</span>”;
}
echo “</dt>”;

}
public function saveMessage($clientIP,$clientTime,$postContent)
{
$msglists = $this->getElementsByTagName( “msglist” );
$msglist = $msglists->item(0);
$msg = $this->createElement( “msg” );
$ip = $this->createElement( “ip” );
$ip->appendChild( $this->createTextNode($clientIP)  );
$msg->appendChild($ip);
$time=$this->createElement( “time” );
$time->appendChild( $this->createTextNode($clientTime)  );
$msg->appendChild($time);
$content=$this->createElement( “content” );
$content->appendChild( $this->createCDATASection(nl2br(SafeHtml($_POST["Content"])))  );
$msg->appendChild($content);
$msglist->insertBefore ($msg);
$this->save(“guestbook.xml”);
echo “<dt> &nbsp;&nbsp;ip:”.$clientIP.”&nbsp;&nbsp;time:”.$clientTime.”</dt><dd>”.$postContent.”</dd>”;
}
}
$guestbook = new guestbook;
if($_POST["action"]==”load”)
{
$guestbook->loadMessage(intval($_POST["page"]));
exit;
}
$IP=$_SERVER["SERVER_ADDR"];
$time=date(“Y-m-d H:i:s”);
$content=nl2br(SafeHtml($_POST["Content"]));
$guestbook->saveMessage($IP,$time,$content);
exit;

//http://hellobmw.com/archives/ajax-comments-with-jquery-for-wordpress.html
Function SafeHtml($msg = “”,$clear_script=true)
{
if(empty($msg))
{
return false;
}
$msg = str_replace(‘&amp;’,'&’,$msg);
$msg = str_replace(‘&nbsp;’,’ ‘,$msg);
if(get_magic_quotes_gpc())
{
$msg = str_replace(“\’”,”&#39;”,$msg);
$msg = str_replace(‘\”‘,”&quot;”,$msg);
}
else
{
$msg = str_replace(“‘”,”&#39;”,$msg);
$msg = str_replace(‘”‘,”&quot;”,$msg);
}
$msg = str_replace(“<”,”&lt;”,$msg);
$msg = str_replace(“>”,”&gt;”,$msg);
$msg = str_replace(“\t”,”&nbsp; &nbsp; “,$msg);
$msg = str_replace(“\r”,”",$msg);
$msg = str_replace(“   “,”&nbsp; &nbsp;”,$msg);
if($clear_script)
{
$msg = preg_replace(“/<script(.*?)<\/script>/is”,”",$msg);
$msg = preg_replace(“/<frame(.*?)>/is”,”",$msg);
$msg = preg_replace(“/<iframe(.*?)>/is”,”",$msg);
$msg = preg_replace(“/<\/fram(.*?)>/is”,”",$msg);
}
return $msg;
}

?>

 

Comments (10)

Ajax技术分类与实现

1顶一下1.AJAX简介 AJAX是实现web2.0服务中的核心技术,全称为“异步JavaScript和XML技    术”(Asynchronous JavaScript and XML);Ajax的核心技术理念在于使用XMLHttpRequest对象发送异步请求。最初为XMLHttpRequest对象提供浏览器支持的是微软公司。AJAX技术的出现,可以说是挽救了传统的B/S结构,并赋与web应用新的生命。简单的说,通过AJAX,我们可以用XMLHttpRequest对象来直接与服务...[阅读全文]

1
顶一下

1.AJAX简介
AJAX是实现web2.0服务中的核心技术,全称为“异步JavaScript和XML技    术”(Asynchronous JavaScript and XML);Ajax的核心技术理念在于使用XMLHttpRequest对象发送异步请求。最初为XMLHttpRequest对象提供浏览器支持的是微软公司。AJAX技术的出现,可以说是挽救了传统的B/S结构,并赋与web应用新的生命。简单的说,通过AJAX,我们可以用XMLHttpRequest对象来直接与服务器进行通信,通过这个对象,我们就可以在不重载页面的情况与 Web 服务器交换数据。
2.AJAX的优点
在传统的 JavaScript 编程中,假如您希望从服务器上的文件或数据库中得到任何的信息,或者向服务器发送信息的话,就必须利用一个 HTML 表单向服务器 GET 或 POST 数据。而用户则需要单击“提交”按钮来发送/获取信息,等待服务器的响应,然后一张新的页面会加载结果。
由于每当用户提交输入后服务器都会返回一张新的页面,传统的 web 应用程序变得运行缓慢,且越来越不友好。
通过利用 AJAX,您的 JavaScript 会通过 JavaScript 的 XMLHttpRequest 对象,直接与服务器来通信。
通过使用 HTTP 请求,web 页可向服务器进行请求,并得到来自服务器的响应,而不加载页面。用户可以停留在同一个页面,他或她不会注意到脚本在后台请求过页面,或向服务器发送过数据。
3.AJAX中用到的技术
实际上,Ajax不是一种技术,而是几种技术。每种技术都具有独特之处,合在一起就形成功能强大的新技术。Ajax包括:
javaScript:实现客户端的数据发送和界面更新,是ajax实现的编程语言;
XMLHttpRequest:浏览器内置的用以进行异步数据发送和接收的对象,是AJAX核心对象;
Css+div:对用户而言,AJAX的价值是用户界面更加友好—这当然还是依靠css+div实现;
DOM模型:AJAX常见的技巧就是使用js响应dom组件事件或更新其;
Xml:XML仅是一种传输数据的格式,在ajax应用中常以xml格式在c/s间交换数据;
Html
4.AJAx的实现步骤
实现AJAX的第一步就是要创建XMLHttpRequest对象
而在不同的浏览器中,创建该对象的方法是不同的
IE 浏览器使用 ActiveXObject,而其他的浏览器使用名为 XMLHttpRequest 的 JavaScript 内建对象。
为了是程序更加的兼容,我们可以使用if语句或者是try    catch语句来创建该对象
如:function ajaxFunction()
{
var xmlHttp;
try
{
// Firefox, Opera 8.0+, Safari
xmlHttp=new XMLHttpRequest();
}
catch (e)
{
// Internet Explorer
try
{
xmlHttp=new ActiveXObject(”Msxml2.XMLHTTP”);
}
catch (e)
{
try
{
xmlHttp=new ActiveXObject(”Microsoft.XMLHTTP”);
}
catch (e)
{
alert(”您的浏览器不支持AJAX!”);
return false;
}
}
}
}
</script>
在上面的例子中 ,首先声明一个保存 XMLHttpRequest 对象的 xmlHttp 变量。
然后使用 XMLHttp=new XMLHttpRequest() 来创建此对象。这条语句针对 Firefox、Opera 以及 Safari 浏览器。假如失败,则尝试针对 Internet Explorer 6.0+ 的 xmlHttp=new ActiveXObject(”Msxml2.XMLHTTP”),假如也不成功,则尝试针对 Internet Explorer 5.5+ 的 xmlHttp=new ActiveXObject(”Microsoft.XMLHTTP”)。
第二步是向服务器发送数据和请求
要想把请求发送到服务器,我们就需要使用 open() 方法和 send() 方法。
open() 方法需要三个参数。第一个参数定义发送请求所使用的方法(GET 还是 POST)。第二个参数规定服务器端脚本的 URL。第三个参数规定应当对请求进行异步地处理。
主要形式为:xmlHttp.open(”GET”,url,true);
xmlHttp.send(null);
第三步,也是很重要的一步就是获得服务器的响应后采取相应的操作:如改变WEB页面的某一个元素的值等
在获得服务器的响应前,我们有必要解释一下 XMLHttpRequest 对象的几个重要的属性。
1、onreadystatechange 属性
onreadystatechange 属性存有处理服务器响应的函数
2、responseText 属性
可以通过 responseText 属性来取回由服务器返回的数据。
下面是我用AJAX写的一个小程序,目的是在不加载页面的情况下,当鼠标从用户栏离开时在时间栏里显示当前时间
主要代码如下:
页面的代码是:<html>
<head>
<title>Ajax1.0.html</title>
<meta http-equiv=”Content-type” content=”text/html;charset=UTF-8″>
</head>
<script type=”text/javascript”>
function  AjaxFunction(){
var xmlHttp;
//1、首先是创建XMLHttpRequest对象
try
{
//FireFox,Poera 8.0+,Safari
xmlHttp=new XMLHttpRequest();
}
catch(e){
try{
//IE6.0+
xmlHttp=new ActiveXObject(”Msxml2.XMLHTTP”);
}
catch(e){
try{
//IE5.5+
xmlHttp=new ActiveXObject(”Microsoft.XMLHTTP”);
}
catch(e){
alert(”您的浏览器不支持AJAX!”);
return false;
}
}
//3、获取服务器的响应并显示时间
}
xmlHttp.onreadystatechange=function()
{
if(xmlHttp.readyState==4){
document.myForm.time.value=xmlHttp.responseText;
}
}
//2、发送请求给服务器
xmlHttp.open(”GET”,”time”,true);//time是Servlet的名字,也是其URL地址
xmlHttp.send(null);
}
</script>
<body>
<form  name=”myForm” >
<table>
<tr>          <td>用户</td>
<td><input type=”text”   name=”username” onblur =”AjaxFunction();”/></td>
//onblur =”AjaxFunction();来激活javascript中的代码
</tr>
<tr>      <td>时间</td>
<td><input type=”text”  name=”time”  /></td>
</tr>
</table>
</form><br>
</body>
</html>
Servlet   time的主要代码是:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType(”text/html;charset=UTF-8″);
PrintWriter out = response.getWriter();
Date d=new Date();
int  h=d.getHours();
int m=d.getMinutes();
int s=d.getSeconds();
System.out.println(d);
out.println(h+”:”+m+”:”+s);//这里就将刚创建的时间传递给了html页面
out.flush();
out.close();
}

Comments (73)

高仿google suggest ajax示例

1顶一下前段时间想用google suggest在网上找了很多都不尽人意,于是自己花了些时间写了一个,跟google suggest 基本一样,后面的约多少结果非本程序范围哦 需要修改的地方有 javascript.js var url="ajax.asp";   //后台地址 var time_delayajax=300;   //搜索延迟 var time_delayupdown=100;  //方向键延迟 obj_div.style.top = (xtop + 20) + "px";    //20差不多是输入框...[阅读全文]

1
顶一下

前段时间想用google suggest在网上找了很多都不尽人意,于是自己花了些时间写了一个,跟google suggest 基本一样,后面的约多少结果非本程序范围哦

需要修改的地方有

javascript.js
var url=”ajax.asp”;   //后台地址
var time_delayajax=300;   //搜索延迟
var time_delayupdown=100;  //方向键延迟
obj_div.style.top = (xtop + 20) + ”px”;    //20差不多是输入框的高度,请根据实际情况调整
ajax_xmlhttp.send(“sift_value=”+escape(temp_value)); //提交到后台的值

dd=d+”<li onmouseover=\”overli(“+i+”);\” onmousedown=\”downli(“+i+”)\” onmouseup=\”upli(“+i+”,event)\” onmousemove=\”moveli();\”><span>约”+c[1]+”结果</span>”+c[0]+”</li>”;//****li的显示

后台输出结果格式必需为’文本1,文本2′…..
‘java,2”javascript,11”java示例,22′等

default.css

.ajaxsearch {
 width:300px; //提示层的宽度

首页index.html

<!DOCTYPE html PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN” ”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<link rel=”stylesheet” type=”text/css” href=”default.css” />
<script language=”JavaScript” src=”javascript.js” type=”text/javascript”></script>
<title>Google suggest高仿示例</title>
</head>

<body onResize=”removediv();”>
<div style=”margin:20px 50px”>
<input style=”width:298px;height:18px” type=”text” autocomplete=”off” onBlur=”blurdeal();” onKeyDown=”keydowndeal(event);” onFocus=”focusdeal(event);” />
</div>
</body>
</html>

脚本javascript.js

///////////////////////////////搜索提示框/////////////////////////////////
var obj_div;     //提示层对象
var obj_input;     //输入框对象
var main_delay;     //判断值变化延迟对象
var ajax_delay;     //ajax延迟搜索对象
var updown_delay;    //方向键延迟对象
var ajax_xmlhttp;    //ajax对象
var div_word=null;    //当前提示层对应的搜索值
var li_num=-1;     //伪光标位置,提示层被选中的li序号,从0开始
var li_down=-1;     //鼠标按下提示层的序号
var value_ed=”";    //输入框延迟前的值
var value_ing=”";    //输入框当前的值
var value_unexit=”";   //搜索过没有结果的值开头
var updown_run=false;   //允许方向键上下
var ajax_run=false;    //true为正常进程,false停止ajax
var ajax_run_ing=false;   //true正在运行,false空闲
var input_focus=false;   //文本框焦点
var url=”ajax.asp”;    //后台地址**********************************************************
var time_delayajax=300;   //搜索延迟**********************************************************
var time_delayupdown=100;  //方向键延迟********************************************************

var $=function(Fun_id){
 return document.getElementById(Fun_id);
}
try{
 ajax_xmlhttp= new ActiveXObject(‘Msxml2.XMLHTTP’);
}catch(e){
 try{
  ajax_xmlhttp= new ActiveXObject(‘Microsoft.XMLHTTP’);
 }catch(e){
  try{
   ajax_xmlhttp= new XMLHttpRequest();
  }catch(e){ajax_xmlhttp=null;}
 }
}
////////////////////////创建提示层////////////////////////
function createajaxdiv(){
 var create_div = document.createElement(“div”);
 create_div.type = ”div”;
 var promptdiv = document.body.appendChild(create_div);
 promptdiv.className = ”ajaxsearch”;
 obj_div=promptdiv;
}
////////////////////////设置提示层位置////////////////////////
function removediv(){
 if(!obj_div || !obj_input)return false;
 if(obj_div.style.display==”none”)return false;
 var obj=obj_input;
 var xtop=0;
 var xleft=0;
 while(obj){
  xtop += obj["offsetTop"];
  xleft += obj["offsetLeft"];
  obj = obj.offsetParent;
 }
 obj_div.style.left = xleft + ”px”;
 obj_div.style.top = (xtop + 20) + ”px”;    //20差不多是输入框的高度,请根据实际情况调整************************************************************8
 li_down=-1;
}
////////////////////////隐藏提示层////////////////////////
function hideajaxdiv(){
 obj_div.style.display=”none”;
 li_down=-1;
}
////////////////////////设置被选<li>css样式////////////////////////
function setlistyle(){
 for(var i=0;i<obj_div.firstChild.childNodes.length;i++){
  obj_div.firstChild.childNodes[i].id=”";
 }
 if(li_num!=-1)obj_div.firstChild.childNodes[li_num].id=”liseleted”;
}
////////////////////////鼠标经过提示层////////////////////////
function overli(Fun_seletedlinum){
 if(li_num==-1)value_ing=obj_input.value;
 li_num=Fun_seletedlinum;
 setlistyle();
}
////////////////////////鼠标拖动提示层////////////////////////
function moveli(){
 if(window.getSelection){
  setfocus();
  window.getSelection().removeAllRanges();
 }else{
  document.selection.empty();
  setfocus();
 }
}
////////////////////////鼠标按下提示层////////////////////////
function downli(Fun_seletedlinum){
 if(!obj_input)return false;
 li_down=Fun_seletedlinum;
 input_focus=true;
}
////////////////////////鼠标弹起提示层////////////////////////
function upli(Fun_seletedlinum,Fun_event){
 if(!obj_input)return false;
 if(Fun_event.button==2){li_down=-1;return}
 if(li_down!=Fun_seletedlinum){
  li_down=-1;
  return false;
 }
 clearTimeout(ajax_delay);
 clearTimeout(updown_delay);
 updown_run=true;
 ajax_run=false;
 ajax_run_ing=false;
 li_num=-1;
 div_word=null;

 value_ed=obj_div.firstChild.childNodes[Fun_seletedlinum].childNodes[1].nodeValue;
 obj_input.value=value_ed;
 value_ing=value_ed;
 hideajaxdiv();
 obj_div.innerHTML=”";
}
////////////////////////设置文本框获取焦点///////////////////////
function setfocus(){
 if(window.event){
  var r = obj_input.createTextRange();
  r.moveStart(‘character’,obj_input.value.length);
  r.collapse(true);
  r.select();
 }else{
  obj_input.selectionStart=obj_input.value.length;
  obj_input.focus();
 }
}
////////////////////////文本框失去焦点////////////////////////
function blurdeal(){
 if(input_focus==true){
  setfocus();
  setTimeout(“setfocus()”);
  return false;
 }
 updown_run=false;
 ajax_run=false;
 ajax_run_ing=false;
 clearInterval(main_delay);
 clearTimeout(ajax_delay);
 clearTimeout(updown_delay);
 hideajaxdiv();
 if(value_ed!=obj_input.value)obj_div.innerHTML=”";
}
////////////////////////文本框获取焦点////////////////////////
function focusdeal(Fun_event){
 if(!obj_div)createajaxdiv();
 if(input_focus==true){
  input_focus=false;
  return false;
 }
 var obj=((window.event)?Fun_event.srcElement:Fun_event.target);
 if(obj.type!=”text”)return false;
 updown_run=true;
 ajax_run=true;
 ajax_run_ing=false;
 if(obj_input==obj && value_ed==obj.value && obj_div.innerHTML!=”"){
  obj_div.style.display=”block”;
  removediv();
 }else{
  obj_input=obj;
  value_ed=obj.value;
  value_ing=obj.value;
  value_unexit=”";
  li_num=-1;
  li_down=-1;
  div_word=null;
  obj_div.innerHTML=”";
  removediv();
 }
 main_delay=setInterval(“mainajax()”,10);
}
////////////////////////主函数////////////////////////
function mainajax(){
 if(value_ed==obj_input.value)return false;
 if(value_unexit!=”" && (obj_input.value).indexOf(value_unexit)==0){hideajaxdiv();obj_div.innerHTML=”";return false;}
 if(value_ed!=obj_input.value && ajax_run_ing==false){
  ajax_run=true;
  value_ed=obj_input.value;
  clearTimeout(ajax_delay);
  if(obj_input.value!=”"){
   ajax_delay=setTimeout(“getsearch();”,time_delayajax);
  }else{
   hideajaxdiv();
   obj_div.innerHTML=”";
   ajax_run=false;
   return false;

  }
 }
}
////////////////////////获取搜索内容////////////////////////
function getsearch(){
 var temp_value=obj_input.value;
 if(ajax_xmlhttp==null){
  return false;
 }else if(ajax_xmlhttp.readyState!=0){
  ajax_xmlhttp.abort();
  ajax_run_ing=false;
 }
 ajax_xmlhttp.onreadystatechange=function(){
  if(ajax_run==false){ajax_xmlhttp.abort();ajax_run_ing=false;return false;}
  if(ajax_xmlhttp.readyState==4){
   obj_div.innerHTML=”";
   if(ajax_xmlhttp.status==200 || ajax_xmlhttp.status==304){
    var contant=ajax_xmlhttp.responseText;
    if(contant!=”" && ajax_run==true){
     div_word=temp_value;
     obj_div.innerHTML=resetcontant(contant);
     obj_div.style.display=”block”;
     removediv();removediv();
    }else{
     hideajaxdiv();
    }
    updown_run=true;
    ajax_run_ing=false;
    li_num=-1;
    if(contant==”")value_unexit=temp_value;
   }
  }
 }
 ajax_xmlhttp.open(“post”,url,true);
 ajax_xmlhttp.setRequestHeader(‘Content-type’,'application/x-www-form-urlencoded’);
 ajax_run_ing=true;
 ajax_xmlhttp.send(“sift_value=”+escape(temp_value)); //提交到后台的值*****************************************
}
////////////////////////内容重组///////////////////////
function resetcontant(Fun_contant){
 if(Fun_contant==null || Fun_contant==”")return ”";
 var a=Fun_contant.substring(1,Fun_contant.length-1);
 if(Fun_contant==null || Fun_contant==”")return ”";
 var b=a.split(“””);
 var c;
 var d;
 d=”<ul>”;
 for(var i in b){
  c=b[i].split(“,”);
  //***************************************************************
  d=d+”<li onmouseover=\”overli(“+i+”);\” onmousedown=\”downli(“+i+”)\” onmouseup=\”upli(“+i+”,event)\” onmousemove=\”moveli();\”><span>约”+c[1]+”结果</span>”+c[0]+”</li>”;
  //***************************************************************
 }
 d=d+”<li onmousedown=\”input_focus=true;li_down=-1;\” onmouseup=\”li_down=-1\” onmousemove=\”moveli();\”><span><a class=\”shutajaxdiv\” onclick=\”hideajaxdiv();\”>关闭</a></span></li>”
 d=d+”</ul>”;
 return d;
}
////////////////////////键盘事件////////////////////////
function keydowndeal(Fun_event){
 var keyc=((window.event)?Fun_event.keyCode:Fun_event.which);
 if(keyc==13){hideajaxdiv();return false;}
 if(keyc==27){
  if(obj_div.style.display==”block” && li_num>-1)value_ed=obj_input.value=value_ing;
  hideajaxdiv();
  return false;
 }
 if(keyc==40 || keyc==38){
  if(div_word==obj_input.value && obj_div.style.display==”none” && obj_div.innerHTML!=”"){
   obj_div.style.display=”block”;
   removediv();
   return false;
  }
  if(li_num==-1){
   if(div_word!=obj_input.value)return false;
  }else{
   if(div_word!=value_ing)return false;
  }
  if(updown_run==false || ajax_run_ing==true || obj_div.style.display==”none”)return false;
  updown_delay=setTimeout(“updownli(“+keyc+”)”,time_delayupdown);
  updown_run=false;
 }
}
////////////////////////方向键移动li////////////////////////
function updownli(Fun_key){
 if(!obj_div){return false;}
 updown_run=true;
 if(li_num==-1){
  if(div_word!=obj_input.value){hideajaxdiv();obj_div.innerHTML=”";li_num=-1;return false;}
 }else{
  if(div_word!=value_ing){hideajaxdiv();obj_div.innerHTML=”";li_num=-1;return false;}
 }
 if(updown_run==false)return false;
 if(li_num==-1)value_ing=value_ed;
 if(Fun_key==40){
  if(li_num<obj_div.firstChild.childNodes.length-2){
   li_num++;
  }else{
   li_num=-1;
  }
 }
 if(Fun_key==38){
  if(li_num>=0){
   li_num–;
  }else{
   li_num=obj_div.firstChild.childNodes.length-2;
  }
 }
 if(li_num!=-1){
  value_ed=obj_input.value=obj_div.firstChild.childNodes[li_num].childNodes[1].nodeValue;
 }else{
  value_ed=obj_input.value=value_ing;
 }
 setlistyle();
}

后台ajax.asp

<%@LANGUAGE=”VBSCRIPT” CODEPAGE=”65001″ %>
<%option explicit%>
<%Response.CodePage=”65001″%>
<%Response.Charset=”utf-8″ %>
<!–#include file=”conn.asp”–>
<%
dim Sift_value
dim Sql,Rs,I,Num
dim Contant
Contant=”"
Num=10
Sift_value=replace(unescape(request.form(“sift_value”)),”"”",”"”"”")

Sql=”select top ”&Num&” keyword,matchnum from search where keyword like ”"”&Sift_value&”%”" order by id”

set Rs=server.CreateObject(“adodb.recordset”)
Rs.open Sql,Conn,1,1
if not (Rs.eof and Rs.bof) then
 for I=0 to Num-1
  Contant=Contant&”‘”&rs(0)&”,”&rs(1)&”‘”
  Rs.movenext
  if Rs.eof then exit for
 next
end if
response.Write(Contant)
Rs.close
set Rs=nothing
%>

Comments (8)

编写高质量Java代码:Ajax单元测试实践

2顶一下 您可能从编写 Ajax 应用程序中获得了极大乐趣,但是对它们执行单元测试却着实让人头痛。   在本文中,Andrew Glover 着手解决 Ajax 的弱点(其中之一),即应对异步 Web 应用程序执行单元测试的固有挑战。   幸运的是,他发现在 Google Web Toolkit 的帮助下,解决这个特殊的代码质量问题要比预想的容易。   Ajax 在近期无疑是 Web 开发界最时髦的字眼之一 ??   与 Ajax 相关的...[阅读全文]

2
顶一下

您可能从编写 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 应用程序的每个部分都能像与之对应的同步应用程序一样进行测试。

  改进代码质量

  别错过 Andrew Glover 的 代码质量讨论论坛,里面有关于代码语法、测试框架以及如何编写专注于质量的代码的帮助。

  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 表单

  输入一个有效单词,单击组件的 Submit 按钮,将向服务器发送消息请求该单词的定义。

  该定义通过回调异步返回,相应地插入到 Web 页面,如图 2 所示:

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

  功能性和集成测试

  图 2 所示的交互测试可用于多个不同场景,

  但是其中两种场景最为常见。从功能性观点考虑,您或许希望编写一个测试:填入表单值,单击 Submit 按钮,然后验证表单是否显示定义。

  另外一个选择是集成测试,使您能够验证客户端代码的异步功能。GWT 的 GWTTestCase 正是

  被设计用来执行此类测试。

  需要牢记的是:在 GWTTestCase 测试用例环境下不可以进行用户界面测试。

  在设计和构建 GWT 应用程序时,您必须清楚不要依赖用户界面 测试代码。

  这种思路需要把交互代码从业务逻辑中分离出来,正如您已经了解的,这是最佳的入门实践!

  举例而言,重新查看图 1 和图 2 所示的 Ajax 应用程序。

  该应用程序由四个逻辑部分构成:TextBox 用于输入目标单词,Button 用于执行单击,还有两个 Label(一个用于 TextBox,另一个显示定义)。

  实际 GWT 模块的初始方法如清单 1 所示,但是您该如何测试这段代码呢?

  清单 1. 一个有效的 GWT 应用程序,但是如何测试它?
1 public class DefaultModule implements EntryPoint {
2
3 public void onModuleLoad() {
4 Button button = new Button(“Submit”);
5 TextBox box = new TextBox();
6 Label output = new Label();
7 Label label = new Label(“Word: “);
8
9 HorizontalPanel inputPanel = new HorizontalPanel();
10 inputPanel.setStyleName(“input-panel”);
11 inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
12 inputPanel.add(label);
13 inputPanel.add(box);
14
15 button.addClickListener(new ClickListener() {
16 public void onclick(Widget sender) {
17 String word = box.getText();
18 WordServiceAsync instance = WordService.Util.getInstance();
19 try {
20 instance.getDefinition(word, new AsyncCallback() {
21
22 public void onFailure(Throwable error) {
23 Window.alert(“Error occurred:” + error.toString());
24 }
25
26 public void onSuccess(Object retValue) {
27 output.setText(retValue.toString());
28 }
29 });
30 }catch(Exception e) {
31 e.printStackTrace();
32 }
33 }
34 });
35
36 inputPanel.add(button);
37 inputPanel.setCellVerticalAlignment(button,
38 HasVerticalAlignment.ALIGN_BOTTOM);
39
40 RootPanel.get(“slot1″).add(inputPanel);
41 RootPanel.get(“slot2″).add(output);
42 }
43 }

  清单 1 的代码在运行时发生了严重的错误:它无法按照 JUnit 和 GWT 的 GWTTestCase 进行测试。

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

  如果我想以白盒方式验证这段代码,就必须分离业务逻辑和特定于用户界面的代码,这就需要进行重构。这本质上意味着把清单 1 中的代码分离到一个便于测试的独立方法中。

  但是这并非听上去那么简单。很明显组件挂钩是通过 onModuleLoad() 方法实现,但是如果我想强制其行为,可能 必须操纵某些用户界面(UI)组件。

  分解业务逻辑和 UI 代码

  第一步是为每个 UI 组件创建访问器方法,

  如清单 2 所示。按照该方式,我可以在需要时获取它们。

  清单 2. 向 UI 组件添加访问器方法使其可用
1 public class WordModule implements EntryPoint {
2
3 private Label label;
4 private Button button;
5 private TextBox textBox;
6 private Label outputLabel;
7
8 protected Button getButton() {
9 if (this.button == null) {
10 this.button = new Button(“Submit”);
11 }
12 return this.button;
13 }
14
15 protected Label getLabel() {
16 if (this.label == null) {
17 this.label = new Label(“Word: “);
18 }
19 return this.label;
20 }
21
22 protected Label getOutputLabel() {
23 if (this.outputLabel == null) {
24 this.outputLabel = new Label();
25 }
26 return this.outputLabel;
27 }
28
29 protected TextBox getTextBox() {
30 if (this.textBox == null) {
31 this.textBox = new TextBox();
32 this.textBox.setVisibleLength(20);
33 }
34 return this.textBox;
35 }
36 }

  现在我实现了对所有与 UI 相关的组件的编程式访问(假设所有需要进行访问的类都在同一个包内)。以后我可能需要使用其中一种访问进行验证。我现在希望限制 使用访问器,如我已经指出的,这是因为

  GWT 并非设计用来进行交互测试。所以,我不是真的要试图测试某个按钮实例是否被单击,

  而是要测试 GWT 模块是否会对给定的单词调用服务器端代码,并且服务器端会返回一个有效定义。方法为将 onModuleLoad() 方法的定义获取逻辑推入(不是故意用双关语!)一个可测试方法中,如清单 3 所示:

  清单 3. 重构的 onModuleLoad 方法委托给更易于测试的方法
1 public void onModuleLoad() {
2 HorizontalPanel inputPanel = new HorizontalPanel();
3 inputPanel.setStyleName(“disco-input-panel”);
4 inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
5
6 Label lbl = this.getLabel();
7 inputPanel.add(lbl);
8
9 TextBox txBox = this.getTextBox();
10 inputPanel.add(txBox);
11
12 Button btn = this.getButton();
13
14 btn.addClickListener(new ClickListener() {
15 public void onClick(Widget sender) {
16 submitWord();
17 }
18 });
19
20 inputPanel.add(btn);
21 inputPanel.setCellVerticalAlignment(btn,
22 HasVerticalAlignment.ALIGN_BOTTOM);
23
24 if(RootPanel.get(“input-container”) != null) {
25 RootPanel.get(“input-container”).add(inputPanel);
26 }
27
28 Label output = this.getOutputLabel();
29 if(RootPanel.get(“output-container”) != null) {
30 RootPanel.get(“output-container”).add(output);
31 }
32 }

  如清单 3 所示,我已经把 onModuleLoad() 的定义获取逻辑委托给 submitWord 方法,

  如清单 4 定义:

  清单 4. 我的 Ajax 应用程序的实质!
1 protected void submitWord() {
2 String word = this.getTextBox().getText().trim();
3 this.getDefinition(word);
4 }
5
6 protected void getDefinition(String word) {
7 WordServiceAsync instance = WordService.Util.getInstance();
8 try {
9 instance.getDefinition(word, new AsyncCallback() {
10
11 public void onFailure(Throwable error) {
12 Window.alert(“Error occurred:” + error.toString());
13 }
14
15 public void onSuccess(Object retValue) {
16 getOutputLabel().setText(retValue.toString());
17 }
18 });
19 }catch(Exception e) {
20 e.printStackTrace();
21 }
22 }

  submitWord() 方法又委托给 getDefinition() 方法,我可以用 JUnit 测试它。getDefinition() 方法从逻辑上独立于特定于 UI 的代码(对于绝大部分而言),并且可以在没有单击按钮的情况下得到调用。另一方面,与异步应用程序有关的状态问题和 Java 语言的语义规则也规定了我不能在测试中完全 避免与 UI 相关的交互。仔细查看清单 4 中的代码,您能够发现激活异步回调的 getDefinition() 方法操纵了某些 UI 组件 ??

  一个错误警告窗口以及一个 Label 实例。

  我还可以通过获得输出 Label 实例的句柄,断言其文本是否是给定单词的定义,从而验证应用程序的功能。

  在用 GWTTestCase 测试时,最好不要 尝试手工强制改变组件状态,而应该让 GWT 完成这些工作。举例而言,在清单 4 中,我想验证对某个给定单词返回了其正确定义并放入一个输出 Label 中。无需操作 UI 组件来设置这个单词;我只要直接调用 getDefinition 方法,然后断言 Label 具有对应定义。

  既然我已经编写好了计划进行测试的 GWT 应用程序,我需要实际编写测试,这意味着设置 GWT 的 GWTTestCase。

  设置 GWTTestCase

  若想从 GWTTestCase 的测试魔力中获益,需要遵守一些规则。

  幸运的是,规则很简单:

  所有用于实现测试的类和待测 GWT 模块必须位于同一个包内。

  运行测试时,您必须至少传递一个 VM 参数,指明在哪种 GWT 模式(托管或 Web)下运行测试。

  您必须实现 getModuleName() 方法,它返回一个 String,表示您的 XML 模块文件。

  最后,因为与服务器端实体通信的 Ajax 应用程序在本质上是异步的,GWT 还提供了 Timer 类,以便延迟 JUnit,使异步行为在进行相关断言之前全部完成。实现 getModuleName 和 Timer 类

  我已经指出,我的测试集中于 getDefinition() 方法(如 清单 4 所示)。

  您可以从代码看到,测试逻辑非常简单:传入一个单词(比如 pugnacious),然后验证相应的 Label 文本是否得到正确定义。很简单,对吗?但是不要忘记,getDefinition() 方法在 AsyncCallback 对象中具有某种相关的异步性。

  GWTTestCase 类是一个抽象 类,因为它的 getModuleName() 方法就是这么声明的;因此,当您扩展该类时,您需要实现 getModuleName()(除非您是在为框架创建自己的基抽象类)。模块名实际上就是您的 GWT XML 文件所在的包结构的名称去掉文件扩展名。举个例子,在本例中,我有一个名为 WordModule.gwt.xml 的 XML 文件,它位于

  一个目录结构如:com/acme/gwt。相应的,模块的逻辑名称为 com.acme.gwt.WordModule,这会让您想到 Java 平台的普通包模式。

  我已经得到一个模块名,可以开始定义测试用例了,如清单 5 所示:

  清单 5. 您必须实现 getModuleName 方法并提供一个有效的名字
1 import com.google.gwt.junit.client.GWTTestCase;
2 import com.google.gwt.user.client.Timer;
3
4 public class WordModuleTest extends GWTTestCase {
5
6 public String getModuleName() {
7 return “com.acme.gwt.WordModule”;
8 }
9 }

  到目前为止一切良好,但是我还没有执行任何测试!由于我的 Ajax 应用程序使用 AsyncCallback 对象,在通过测试用例调用 getDefinition() 方法时,我必须强迫 JUnit 延迟运行;否则测试将由于没有任何响应而失败。这就要用到 GWT 的 Timer 类。Timer 使我能够重写 getDefinition() 的 run 方法,在 Timer 内完成测试用例逻辑。(测试用例以独立线程运行,有效地阻塞 JUnit 完成整个测试用例)。

  以我的测试为例,我将首先调用 getDefinition() 方法,然后提供一个 Timer 的 run() 方法的实现。run() 方法得到输出 Label 实例的文本并验证是否是正确定义。

  定义了 Timer 实例后,我就需要确定其何时运行,同时强制 JUnit 挂起直至 Timer 实例完成。也许听起来有点复杂,不必担心,因为实践起来非常简易。实际上,清单 6 展示了整个过程:

  清单 6. 使用 GWT 轻松测试
1 public void testDefinitionValue() throws Exception {
2 WordModule module = new WordModule();
3 module.getDefinition(“pugnacious”);
4 Timer timer = new Timer() {
5 public void run() {
6 String value = module.getOutputLabel().getText();
7 String control = “inclined to quarrel or fight readily;…”;
8 assertEquals(“should be ” + control, control, value);
9 finishTest();
10 }
11 };
12 timer.schedule(200);
13 delayTestFinish(500);
14 }

  正如您所见,Timer 的 run() 方法是我真正验证 Ajax 应用程序功能及其应用远程过程调用的地方。

  请注意 run 方法的最后一步是调用 finishTest() 方法,它意味着一切如预期运行,JUnit 可以不受阻塞正常运行。在实践中,您可能会发现需要根据异步行为完成所需的时间调整延迟时间。

  但用 JUnit 测试 GWT 应用程序的要点在于:您能够在无需 部署完整功能的 Web 应用程序的情况下测试它。因此,您能够更早地 并且更 频繁地 测试您的 GWT 应用程序。

  运行 GWT 测试

  使用 GWT 进行功能测试

  像本文演示的这类简单 Ajax 应用程序可以 从功能角度进行验证,使用包括 Selenium 在内的框架,它会驱动浏览器模拟实际用户行为。不过,要想用 Selenium 运行功能测试,您必须部署完整功能的 Web 应用程序。

  前面我曾提到,如果您想实际运行您的 GWT JUnit 测试,您必须执行大量琐碎的工作来配置运行环境。比如说,要想通过 Ant 的 junit 任务运行我的测试,我就必须确保某些文件位于类路径中并向低层 JVM 提供一个参数。特别是,在调用 junit 任务时,我还要确保托管源文件(以及测试)的目录(或多个目录)位于类路径中,还要告诉 GWT 以何种模式运行。

  我倾向于使用 hosted 模式,这意味着要使用 www-test 标志,如清单 7 所示:

  清单 7. 用 Ant 运行 GWT 测试
1
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

  运行 GWT 测试现在转变成调用问题了。还需注意的是 GWT 测试属于轻量级测试,所以我可以频繁运行测试,甚至是连续运行,就像我在一个持续集成环境(Continuous Integration)中一样。

  结束语

  在本文所示的 GWT 测试用例中,您已经看到用于验证 Ajax 应用程序所需的基本步骤。

  您可以继续测试我的示例 GWT 应用程序,比如测试一些边界用例,但是我认为重点在于:如果使用包含测试特性的框架编写 Ajax 应用程序,测试要比想象中容易。

  要对 GWT 应用程序进行良好测试(对绝大多数应用程序也适用),关键在于设计应用程序时要把测试一并考虑。还要注意 GWTTestCase 不是被用来进行交互测试的。

  您不能使用 GWTTestCase 直接模拟用户。不过您能够以一种间接的方式用它来验证用户交互,正如本文中演示的那样。

原文链接:http://www.88doc.com/node/967

Comments (12)