AJAX 测试自动化

http://msdn.microsoft.com/zh-cn/magazine/cc163479

 

 

 

AJAX 测试自动化
James McCaffrey 博士


代码下载位置: TestRun2007_02.exe (574 KB)
Browse the Code Online

过 去一年中,使用 AJAX (Asynchronous JavaScript And XML) 技术的 Web 应用程序越来越受到用户的推崇。与非 AJAX Web 应用程序相比,正确编写的 AJAX Web 应用程序可显著提升性能和改善用户体验。但由于 AJAX Web 应用程序采用的是异步工作模式,因而传统同步测试自动化技术在此处通常便无用武之地。在本月的专栏中,我将介绍一项技术,利用该技术您便可编写小型测试自 动化软件,以此来验证 AJAX Web 应用程序的功能。
就我而言,向您传达本专栏中所述观点的最佳方法是借助一些屏幕快照。 图 1 是一个使用了 AJAX 技术的简单但极具代表性的 ASP.NET Web 应用程序。该应用程序模拟映射工具,如 Microsoft ® Windows Live™ Local。当用户单击某定向按钮控件时,该应用程序会从 Web 服务器中提取适当的映射图像,然后在中心区域中显示新映射。不太容易从屏幕快照中看出的是 Web 应用程序使用 Microsoft ASP.NET AJAX 以异步方式发送和检索映射图像。当然,您的 Web 应用程序会更为复杂,但我将向您介绍的技术适用于任何复杂的应用程序,并可用于任何启用 AJAX 的应用程序,不论实施何种技术。
手动测试 图 1 中的应用程序是一件既费时低效又枯燥乏味的差事。更好的测试方法是编写小型测试自动化软件,如 图 2 所示。这种测试工具是由两个框架构成的简单的 HTML 页面。右侧框架是 Web 应用程序的宿主,用户未进行任何修改或检测。左侧框架是含有 JavaScript 代码的单个 HTML 页面的宿主,JavaScript 代码可使用 Internet Explorer ® 文档对象模式 (DOM) 在另一个框架中处理 Web 应用程序。
虽 然该测试技术是专用于处理使用异步操作的应用程序,但此技术也可用于使用传统同步 HTTP 请求/响应机制的 Web 应用程序。在本专栏中,我将简要介绍一下待测试的 Web 应用程序,从而使您了解传统测试自动化技术对 AJAX 应用程序无效的原因。然后,我将详细说明生成 图 2 中的图像的测试工具,并说明如何才能修改和扩展此处介绍的技术以满足您自己的需求。最后,我相信您会发现这项技术是对开发人员和测试人员技能要求的一个有用补充。
图 1  待测试的 AJAX Web 应用程序  (单击该图像获得较大视图)

 

待测试的 Web 应用程序
我使用 ASP.NET AJAX 代码库(极大地简化了 AJAX 应用程序的创建过程)创建了如 图 1 所示的 AJAX 映射应用程序。我没有使用真正的映射图像,而仅使用了从数字 1 到数字 9 的九个虚拟图像,图像名称依次为 1.jpg 到 9.jpg。(有关使用 Microsoft TerraServer 来获取真正的映射图像的示例,请参阅本期中 Jeff Prosise 关于“超酷代码”专栏。)缩图区域由 3 行 3 列的网格构成,第一行为 1、2 和 3,第二行为 4、5 和 6,第三行为 7、8 和 9。5 是该区域的中心,并且如果用户从 5 单击“北”控制按钮,则应用程序应显示 2。“北”按钮的逻辑如 图 3 所示。
protected void Button1_Click(object sender, EventArgs e)
{
  string imageUrl = Image1.ImageUrl.ToString();
  if (!(imageUrl == "~/1.JPG" || imageUrl == "~/2.JPG" ||
        imageUrl == "~/3.JPG"))
 {
    string oldNum = imageUrl.Substring(2, 1);
    int oldNumAsInt = int.Parse(oldNum);
    int newNumAsInt = oldNumAsInt - 3;
    string newNum = newNumAsInt.ToString();
    string newImageURL = imageUrl.Replace(oldNum, newNum);
    Image1.ImageUrl = newImageURL;
  }
}

图 2  测试运行示例  (单击该图像获得较大视图)
如 果当前映射位于该区域的首行(1、2、3),则单击“北”后不会产生任何影响。其他情况下,我将得到当前映射位置的数字,这个数字减去 3 后即可确定相应的新 JPEG 图像,并更新该图像控件的 ImageUrl 属性。“南”、“东”和“西”按钮控件的处理程序与之类似。
需 要强调的是,在此我并没有为了保持应用程序代码的简短易读而故意不采用更好的编码方法。在实际的 Web 应用程序中,代码逻辑很可能需要访问后台 SQL 或 XML 存储库并从中提取数据,然后使用这些数据更新应用程序状态。由于本专栏中的技术通过 Internet Explorer DOM 测试 Web 应用程序,因而如何确定应用程序状态并不重要 - 任何用户输入都可能导致应用程序状态的改变,用户输入将反映在应用程序的 UI 中,用户可通过 Internet Explorer DOM 访问这些 UI。
传 统的非 AJAX 方法向 Web 服务器发布 HTTP 请求,并以“窗体”对象或查询字符串的形式传递请求信息。映射应用程序不需使用 AJAX 技术便能运行,但这种方法存在两个缺点。第一,由于 HTTP 请求/响应机制是同步的,因此,尽管请求由 Web 服务器进行处理,但用户在该响应返回客户端之前几乎无法与 Web 应用程序进行交互。第二,多数情况下 HTTP 请求会导致创建整个响应页面,因此当由 Internet Explorer 收到响应时,将重新绘制整个页面。如果请求/响应相对较快(或更糟),这将导致出现恼人的页面闪烁效果;如果操作较慢,则将导致页面变为空白。
AJAX 可解决上述两个问题。AJAX 通过发送 XMLHTTP 请求而非 HTTP 请求进行工作。XMLHTTP 请求是异步的。因此,在后台处理 XMLHTTP 请求的同时,用户可继续与 Web 应用程序进行交互。并且当收到 XMLHTTP 响应时,系统可使用 Internet Explorer DOM 仅重新绘制存放新数据的网页区域,而不必重新绘制整个页面。
我 不是通过编写“原始”JavaScript 来启用 ASP.NET 映射应用程序上的 AJAX,而是使用 Microsoft ASP.NET AJAX 框架来启用它。Microsoft ASP.NET AJAX 框架的使用方法相当简单,因为它在安装时提供了一个 Visual Studio ® AJAX 网站模板。选择该模板便可将所需的程序集引用添加到 Web 应用程序项目中。为了在映射应用程序中启用 AJAX 功能,我向源文件添加了下列标记:
<asp:ScriptManager ID="sm" runat="server" />
然后,我将图像控件(我想在异步请求-响应后更新该控件)封装在 ASP.NET AJAX UpdatePanel 控件中,如下所示:
<asp:UpdatePanel ID="up1" runat="server">
  <ContentTemplate>
    <asp:Image ID="Image1" runat="server" ImageUrl="~/5.JPG" 
               (other attributes omitted) />
  </ContentTemplate>
  <Triggers>
    <asp:AsyncPostbackTrigger ControlID="Button1" EventName="Click" />
    <asp:AsyncPostbackTrigger ControlID="Button2" EventName="Click" />
  </Triggers>
</asp:UpdatePanel>
这就是对 Microsoft ASP.NET AJAX 框架所做的全部工作。非常好!该框架将处理所有杂乱的细节,如创建 XMLHTTP 对象、侦听异步响应、错误处理、跨浏览器差异等。
请 注意,仅当存在对 Button1(“北”)控件和 Button2(“南”)控件的单击事件时(而非对所有四个定向控件的单击事件),我才会启用 AJAX 异步请求/响应。我这样做的目的是为了说明本月专栏中介绍的测试技术既可用于异步请求也可用于同步请求。如果您使用本文附带的代码下载内容尝试运行该应用 程序,则会发现“北”请求和“南”请求 (AJAX) 与“东”请求和“西”请求(非 AJAX)之间的区别。
大 多数传统测试自动化技术都对 AJAX Web 应用程序不起作用。测试 Web 应用程序功能的最基本方法是以编程方式发送 HTTP 请求(该请求对应于某用户向 Web 服务器输入信息后所生成的请求)、提取 HTTP 响应,然后检查此响应以确定“pass/fail”(通过/失败)结果。这种方法不适用于 AJAX 应用程序,因为这些应用程序使用特殊的 XMLHTTP 请求。
另一种传统方法是使用 JavaScript 操纵 Internet Explorer DOM 以向 Web 服务器发送请求,等到 onload 事件触发(发出客户端已收到并已加载响应的信号)后,然后使用 JavaScript 和 Internet Explorer DOM 来检查网页的新状态以确定“pass/fail”(通过/失败)结果。使用此方法产生的问题是,由于 AJAX 采用的是异步工作模式,因而您无法使用 onload 事件来确定客户端收到响应的时间。但您可对这种方法稍作修改,以创建适用于 AJAX 应用程序的小型测试自动化软件。收到响应时,您不必使用 onload 事件来发出信号,而是可通过编写代码来监视应用程序,使应用程序状态按照要求发生某些变化,然后将控制权转至回调函数。

 

测试自动化
图 2 屏幕快照中所示的 AJAX 应用程序测试工具系统由三个文件组成。 图 4 显示了该工具整体结构的框图。工具本身(即 TestHarness.aspx)是包含两个框架的简单的网页。右侧框架包含待测试的 AJAX Web 应用程序。左侧框架包含测试方案页面,其中列出了一些最小 UI 和所有 JavaScript 代码,用于处理和检查待测试的 Web 应用程序。为保持一致,我打算将上述三个页面编写为 .aspx 文件,尽管这会使我无法将基本 HTML 用于测试工具和测试方案页面。下面是 TestHarness.aspx 页面的完整源代码:
<html>
<head>
  <title>Test Harness for AJAX Web Apps</title>
</head>
  <frameset cols="45%,*">
    <frame src="http://localhost/AjaxTest/TestScenario001.aspx"
      name="leftFrame">
    <frame src="http://localhost/AjaxApplication/Default.aspx"
      name="rightFrame">
  </frameset>
</html>
图 4  测试工具的结构  (单击该图像获得较大视图)
如 您所见,主要的测试工具页面实际上就是一个容器文件。通过调用程序定义函数 asyncCall 开始运行测试方案,该函数会使待测试的应用程序向 Web 服务器发送异步 XMLHTTP 请求,同时调用程序定义的 delay 函数。该 delay 函数在 Web 服务器处理请求时进行循环,返回异步响应,并不时检查 Web 应用程序的状态。客户端收到响应且应用程序状态被更新后,delay 函数会发现网页的状态已发生改变,然后便可进行新的异步调用。

 

asyncCall 内部
AJAX 测试自动化技术的核心是一对程序定义函数,即 asyncCall 和 delay,用户应搭配使用这两个函数。asyncCall 方法如下:
function asyncCall(action, checkFunc, arg, callback, pollTime)
{
  numTries = 0;
  action();
  window.setTimeout("delay(" + checkFunc + ", " + "'" + arg +
    "'" + ", " + callback + ", " + pollTime + ")", pollTime);
}
我传入了五个参数。第一个参 数 action 是指向某例程的函数指针,该例程使用 Internet Explorer DOM 来触发操作,从而启动异步 XMLHTTP 请求。第二个参数 checkFunc 是指向某例程的函数指针,该例程在 Web 应用程序的状态表明异步响应已完成时返回 true。第三个参数 arg 是传递给 checkFunc 函数的参数。第四个参数 callback 是指向某例程的函数指针,系统会在异步响应已完成时调用该例程。最后一个参数 pollTime 用于指定合作伙伴 delay 函数中两次调用之间的等待时间(以毫秒为单位)。
在 asyncCall 函数内部,我首先将全局 numTries 计数器设置为 0。我使用该变量来跟踪进入 delay 函数的次数,以便在查找可表明异步响应已发生的尝试达到指定次数后能够退出。接下来,我调用 action 参数,该参数进而触发异步请求。请注意,附加到函数名的括号是用来调用函数的语法机制。
下 面才是真正的技巧所在:我调用内部 window.setTimeout 函数。如您所见,setTimeout 接受两个参数。第一个参数是仅执行一次的 JavaScript 语句。第二个参数是在执行第一个参数前延迟的时间(以毫秒为单位)。如果检查 asyncCall 中的代码,您便会发现我传递给 setTimeout 的第一个参数变成了如下形式:
delay(checkFunc, 'arg', callback, pollTime)
第二个 arg 就是 pollTime。简言之,在 pollTime 毫秒的延迟后,我调用了程序定义的 delay 函数。
对 asyncCall 函数的调用如下所示:
asyncCall(clickNorth, imgIsTwo, "2", clickSouth, 200);
该调用可解释为“调用名为 clickNorth 的函数,然后进入 delay 循环,每隔 200 毫秒调用一次带有参数‘2’的函数 imgIsTwo,并在 imgIsTwo 最终返回 true 时,将控制权转至名为 clickSouth 的函数。”
在 asyncCall 方法的定义中,arg 周围的单引号字符非常重要。如果 asyncCall 定义中没有单引号,则参数将按引用进行传递,如下所示:
asyncCall(doThis, findX, "X", doThat, 200);
此类调用会生成错误,指出变量 X 未被定义。如果使用了单引号,则参数将按值进行传递。在本例中,您希望参数按值传递。
还有另一种有趣的方法可用来调用 asyncCall 内部的 window.setTimeout 函数。通常,您需要编写类似下列内容的代码:
window.setTimeout("delay(" + checkFunc + ", " + "'" + arg +
    "'" + ", " + callback + ", " + pollTime + ")", pollTime);
此外,您还可使用 JavaScript 的匿名函数功能,并编写下列代码:
window.setTimeout(
    function(){delay(checkFunc, arg, callback, pollTime);}, 
    pollTime);
由于删除了单引号字符和字符 串连接,因而此类代码比非匿名代码要略显简洁一些。另外,它的效率比非匿名代码略胜一筹,因为非匿名代码要求浏览器创建新的脚本环境来处理该脚本。但如果 没有使用单引号字符来分隔传递给 checkFunc 的 arg 参数,则无法明显表示 arg 是按值进行传递的(尽管简单的注释可避免发生任何可能的误解)。

 

Delay 内部
下面介绍 asyncCall 的合作伙伴 delay 函数:
function delay(checkFunc, arg, callback, pollTime)
{
  ++numTries;

  if (numTries > maxTries) finish();
  else if (checkFunc(arg)) callback();
  else window.setTimeout("delay(" + checkFunc + ", " + "'" + arg +
      "'" + ", " + callback + ")", pollTime);
}
delay 函数接受四个参数,这些参数与进行传递以调用 asyncCall 函数的最后四个参数完全对应。
在 delay 内部,我首先递增全局 numTries 计数器。我们知道,在上文中我使用该计数器来跟踪进入 delay 函数的次数,或换言之,我检查 Web 应用程序状态的次数,以判断异步响应是否已完成。如果全局计数器的读数大于全局常量 maxTries,则或是服务器端的 Web 应用程序超时,或是 XMLHTTP 响应不正确,因此,我将控制权转至 finish 函数,以确定具体情况。
如果 delay 未超时,我便会调用 checkFunc 函数来测试用来指示正确响应的条件是否为 true。如果 checkFunc 返回 true,我便可调用 callback 函数以继续执行测试方案。但如果 checkFunc 函数返回 false,我便需继续等待,因此我将再次调用内部 setTimeout 函数,该函数将在等待 pollTime 毫秒后再次调用 delay。
delay 函数不是完全递归函数,因为它无法直接调用它自己,但它是自引用函数,因为它通过 setTimeout 函数间接调用它自己。实际效果是产生一个等待循环,该循环或将控制权转至 finish 函数(如果超过了 delay 循环中的最大次数),或将控制权转至 callback 函数(如果某 Web 应用程序状态变为 true)。这种方法会使人产生一种山重水复疑无路,柳暗花明又一村的感觉。当然,其他方法也能解决上述问题,但实践证明该方法的确简单高效。
如同在 asyncCall 函数中一样,您不必使用指定的函数参数来调用 window.setTimeout 函数,而是可以使用匿名函数功能,如下所示:
window.setTimeout(
    function(){delay(checkFunc, arg, callback);}, 
    pollTime);

 

构建测试页
处理了 asyncCall 函数和 delay 函数后,现在我便可开始构建测试方案页面了。 图 5 介绍了测试方案的整体结构。
<html>
<head>
  <script type="text/javascript">
    var maxTries = 10;
    var numTries;
    var polling = 200; // 毫秒
    
    function asyncCall(action, checkFunc, arg, callback, pollTime)
    {
      // 在此处编写代码
    }

    function delay(checkFunc, arg, callback, pollTime)
    {
      // 在此处编写代码
    }

    // 在此处添加其他函数
  </script>
</head>
<body bgcolor="#22EE66">
  <h3>此为测试方案脚本页面</h3>
  <p>动作:</p><p><textarea id="comments" rows="16"
                      cols="34"></textarea></p>
  <input type="button" value="运行测试" οnclick="runTest();" /> 
</body>
</html>

测 试方案页面 <正文> 部分仅包含一个标题、一个要显示注释且具有 ID“comments”的文本区,以及一个用于启动自动化的按钮控件。<标题> 部分包括我的所有 JavaScript 代码。我声明并初始化全局常量 maxTries 以指定要进入 delay 函数的最大次数,并检查可表明异步响应已完成的条件。全局变量 numTries 跟踪进入 delay 函数的次数。全局常量 polling 设置对 delay 函数进行两次连续调用之间的延迟时间。
根 据待测试的 AJAX Web 应用程序的复杂度,您可能需要修改 maxTries 和 polling 的值。在这里,10 次调用(将 200 毫秒作为两次调用之间的延迟时间)仅用时 2 秒,Web 服务器可能无法在如此短的时间内处理 XMLHTTP 请求并将响应返回至客户端。
测试方案首先调用 runTest 函数:
function runTest()
{
  try
  {
    logRemark("测试方案 001");
    logRemark("开始测试运行\n");
    step1(); // 从 5 开始,将 N 转至 2
  }
  catch(ex) { logRemark("致命错误: " + ex); }
  }
}
我所做的是使用程序定义的 logRemark 函数在工具正文的 <文本区> 中显示两条消息,然后将控制权转至 step1 函数。我将代码封装到简单的 try/catch 块中,以便在测试运行期间出现任何例外时都能激起并捕获这些代码。我的 logRemark 函数非常简单:
function logRemark(comment)
{
  var currComment = document.all["comments"].value;
  var newComment = currComment + "\n" + comment;
  document.all["comments"].value = newComment;
}
我检索具有 ID“comments”的 <文本区> 元素的当前内容,将换行字符和新注释文本附加到当前内容中,然后使用更新的注释集替换 <文本区> 内容。这不是一种高效的方法,但在小型测试自动化软件中,简洁往往要比效率更重要。
step1 函数开始处理待测试的 AJAX Web 应用程序:
function step1()
{
  logRemark("单击“北”,等待“2”");
  asyncCall(clickNorth, checkImageSrc, "2", step2, polling);
}
记录注释后,我调用 asyncCall 函数,该函数是测试自动化的关键所在。该调用在这里表示:调用函数 clickNorth,然后进行 delay 循环直到 checkImgSrc("2") 返回 true 为止,在两次检查之间暂停 200 毫秒,然后将控制权转至函数 step2。如果调用 delay 循环的次数超过 10 (maxTries) 次,则会将控制权转至 finish 函数。
clickNorth 函数非常简单:
function clickNorth()
{
  var btnNorth = parent.rightFrame.document.all["Button1"];
  if (!btnNorth) throw "未找到 btnNorth";
  btnNorth.click();
}
我使用 Internet Explorer DOM 来获得对 Button1 控件的引用,然后调用该控件的 click 方法。请注意,由于自动化软件位于一个框架中,而 AJAX 应用程序位于另一个框架中,为了从测试脚本访问应用程序上的控件,因而我必须使用 parent 关键字来“上升”一个级别,然后使用应用程序容器的框架 ID。我喜欢使用 document.all 集合来获得对网页控件的引用,而我的一些同事则更喜欢使用 getElementById 方法,如下所示:
var btnNorth = parent.rightFrame.document.getElementById("Button1");
checkImageSrc 函数可告知自动化软件有关异步响应更新待测试 AJAX 应用程序上的 Image1 控件的时间:
function checkImageSrc(target)
{
  try
  {
    var s = parent.rightFrame.document.all["Image1"].src;
    return s.indexOf(target) >= 0;
  }
    // 尚未装载 Image1 的陷阱案例
    // logRemark("CheckImageSrc() 中存在错误: " + ex);
  catch(ex) { return false; }
}
通常,我只检查 Image1 控件的 src 属性,以此来确定该属性是否包含目标字符串。例如,如果图像的 src 属性包含“2”(如在 map 2 的文件名“~/2.jpg”中),则当调用 checkImageSrc("2") 时将返回 true。在编写您自己的 AJAX 自动化软件时,您必须创建适当的 check 函数。例如,假设 Web 应用程序要更新具有 ID“TextBox1”的 TextBox 控件。一个可能的 check 函数为:
function checkTextBoxValue(target)
{
  try
  {
    var s = parent.rightFrame.document.all["TextBox1"].value;
    return s.indexOf(target) >= 0;
  }
    // 尚未装载 TextBox1 的陷阱案例
    // logRemark("checkTextBoxValue() 中存在错误: " + ex);
  catch(ex) { return false; }
}
请注意 checkImageSrc 函数中的 try/catch 块。与大多数情况下将 try/catch 用来捕获异常错误条件不同,在这里我将 try/catch 用作标准函数逻辑的一部分。其间的思路是在重新绘制网页期间,用户无法获得对 Image1 控件的引用,而任何尝试获得对 Image1 控件的引用的操作都将引发异常。这并不一定意味着 Web 应用程序的状态不正确,而仅表明该应用程序处于未完成状态,因而我希望系统捕获异常并返回 false。通过取消对 logRemark 调用的注释并将轮询时间设置得非常短(如 10 毫秒)可观察该行为。
顺便提一下,人们通常认为将 try/catch 用作标准函数逻辑的一部分是很糟糕的编码样式,但在本例中,由于将 try/catch 用于标准逻辑流程可简化代码,因而我认为这是可以接受的。
我 的测试方案脚本定义了一些与 step1 函数极其类似的函数 step2、step3、step4 和 step5。step2 函数可调用函数 clickEast,等到映射显示区域中出现“3”后,便紧接着调用 step3。step3 函数可调用函数 clickSouth,并等到“6”出现后,便紧接着调用 step4,依此类推。step5 函数可调用暂作过渡之用的 finish 函数 step6:
function step6()
{
  finish();
}
在这里,我只需将控制权转至真正的 finish 函数(如 图 6 所示)。我们知道,finish 函数还可处理对 delay 函数的调用次数超过了最大允许次数的情况。
function finish()
{
  if (numTries > maxTries)
  {
    logRemark("\nnumTries 已超出 maxTries");
    logRemark("\n*失败*");
  }
  else
  {
    logRemark("检查最终状态");
    var is = parent.rightFrame.document.all["Image1"].src;
    if (is.indexOf("8") >= 0) logRemark("\n*通过*");
    else logRemark("\n*失败*");
  }
  logRemark("\n已全部完成");
}

首 先,我将处理全局计数器 numTries 的读数超过了全局常量 maxTries 的情况。在下列两种情况中都可能发生这种情况:第一种情况是 AJAX 应用程序的逻辑正确,但应用程序更新页面状态超时;第二种情况是应用程序的逻辑不正确,并且 check 函数无法找到正确的状态。在这里,我随意选择了一种导致测试方案失败的简单解决方案。
finish 中的第二个逻辑分支会检查应用程序的最终状态以确定“pass/fail”(通过/失败)结果。在本例中,如果我从“5”开始逐个单击“北”、“东”、“南”和“西”这几个控件,便可检查最终映射图像是否为“8”(正常情况下应为“8”)。

 

扩展测试工具
我在本文中介绍的测试自动化系统规模很小,专为便于您进行修改而设计。对于杂志文章,我通常会删除其中大多数错误检查以保持主要观点的鲜明性。但您可能希望随意添加一些错误陷阱 - 但测试自动化软件机敏睿智,并且错误是常事而非例外。
请注意,测试工具并不是完全自动化的:您必须手动单击“进行测试”按钮来启动自动化软件。您可通过各种方法使该工具实现完全自动化。一种简单的方法就是将 onload 处理程序添加到测试工具框架中:
<frame src="http://localhost/AjaxApplication/Default.aspx"
  name="rightFrame"
  οnlοad="leftFrame.launch();" >
在本例中,测试方案代码中的 launch 函数定义如下:
var started = false;
function launch()
{
  if (!started) runTest();
}
此外,还需编辑本专栏中介绍的 runTest 函数,使其以下列语句开头:
started = true;
当加载包含待测试的 AJAX Web 应用程序的右侧框架时,系统会调用 launch 函数。首次执行 launch 时,处于启用状态的全局变量为 false,并且将控制权转至 runTest 函数,该函数将处于启用状态的变量设置为 true,然后执行自动化软件。加载(发生于定期 HTTP 请求/响应操作时)Web 应用程序的后续页面时,系统将调用 launch 函数,但由于处于启用状态的全局变量为 true,因而不会再次将控制权转至 runTest。借助完全自动化版本的测试工具,您便可创建多个工具页面和方案页面,然后通过将下列语句添加到 BAT 文件中并紧接着使用 Windows Task Scheduler 来启动自动化软件,以执行多个方案:
iexplore http://localhost/AjaxTest/TestHarness01.aspx
iexplore http://localhost/AjaxTest/TestHarness02.aspx
请注意,小型系统的缺点是您 无法方便地对该系统进行参数化。尽管您可轻松地将测试方案页面中的公共 JavaScript 代码添加到独立的单个 .js 文件中,但要协调单个测试工具页面中的多个测试方案的执行却无简便方法可循。这并意味着您无法实现该操作,我只是要告诉您这并非易事。
AJAX 测试自动化软件的另一个扩展是使保存测试方案结果的过程实现自动化。本文中介绍的系统不会记录结果,但您可通过下列几种方法来实现该操作。一种方法是将 “窗体”元素添加到测试方案页面中,在该窗体内部放置一个用来存放测试方案结果的文本字段,然后将结果 POST 到服务器,如下所示:
<form name="resultForm" method="Post" action="saveResults.aspx">
  <p>Result: <input type="text" name="result"></p>
  <p><input type="submit" name="saver" value="Save Results"></p>
</form>
您可根据下列代码行向 finish 函数内部的结果字段提供“pass/fail”(通过/失败)值:
if (is.indexOf("8") >= 0)
{
  logRemark("\n*通过*");
  theForm.result.value = "Pass";
}
else
{
  logRemark("\n*失败*");
  theForm.result.value = "Fail";
}
现在,您可通过单击“保存结果”按钮来手动保存方案结果,或者您也可通过将下列代码添加到方案代码中来自动进行保存:
document.all["theForm"].submit();

 

结束语
我 在本文中介绍的测试工具只是可用来测试 AJAX Web 应用程序的众多方法中的一种。阅读本专栏后,您应当能够使用我介绍的小型方法和更为复杂的工具来测试 AJAX Web 应用程序。随着 ASP.NET AJAX 框架的发布,AJAX Web 应用程序也会随之很快普及。编写小型测试自动化软件的能力会变得越来越重要,它是对您个人技能集的重要补充。

 

将您想向 James 询问的问题和提出的意见发送至 testrun@microsoft.com .

 

James McCaffrey 博士 供 职于 Volt Information Sciences, Inc.,在那里他负责 Microsoft 的软件工程师的技术培训。他已为多种 Microsoft 产品效过力,包括 Internet Explorer 和 MSN Search。James 是《.NET Test Automation Recipes》(.NET 测试自动化菜单)(Apress,2006)的作者。James 的联系方式如下: jmccaffrey@volt.comv-jammc@microsoft.com 感谢 Doug Walter、Shawn Burke 和 Scott Guthrie(全部供职于 Microsoft),他们的意见使本专栏的内容更为丰富精彩。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值