Web 服务器在执行过程和记录过程中的不同响应
在理想情况是:您会将一组请求记录到 Web 应用程序,运行该 Web 测试,从服务器接收您在记录过程中看到的相同响应。但遗憾的是,Web 应用程序有时在 Web 测试的执行与记录过程中的行为表现完全不同。
出现这种问题的原因有多种,通常导致如下所示的错误:
Request failed: $HIDDEN1.__VIEWSTATE not found in test context.
当 Web 测试尝试在它无法定位的 Web 测试上下文中使用隐藏字段,并从它接收的前一响应页进行提取操作时,会发生该错误。
以下屏幕快照显示该问题由服务器错误引发时的情况。在倒数第二个请求中,服务器错误导致下一请求依赖的隐藏字段不存在于该页上。
服务器之所以在执行过程和记录过程中有不同的响应,有很多原因。以下几节汇总了较为常见的几个原因。在所有情况中,验证规则可以添加到请求以自动验证服务器响应正确的内容。
一次性数据
该问题的一个常见原因是一次性数据,例如,当一个 Web 应用程序创建一个唯一的用户名时。在不添加数据绑定或随机值的情况下运行这种 Web 测试,可能导致 Web 应用程序在该测试尝试创建重复的用户名时显示错误。
JavaScript 重定向
使用 JavaScript 重定向(设置 window.location)的 Web 应用程序可能在执行中和记录中进行不同的响应,因为该 Web 测试引擎不运行脚本代码。要轻松地解决这种问题,可以插入该脚本重定向到的 URL,并从执行重定向的页将所需的提取规则移到新请求。由于记录之后该问题会立即出现在 Web 测试中,因此,唯一可用的提取规则可能是 ExtractHiddenFields。
重定向到错误页
当出现服务器错误时,Web 应用程序可能重定向到错误页,但并不返回一个 HTTP 400 或 500 级别响应代码。这指示或者 Web 应用程序自身有问题,或者 Web 测试发出的请求有问题。有关该问题的详细讨论,请参阅该网络日记发布。
处理视图状态和其他动态参数
甚至在 ASP.NET 1.0 引入 __VIEWSTATE 隐藏窗体字段之前,Web 应用程序就已经使用动态生成的窗体和查询字符串参数在页面间传递信息了。这些动态参数在 Web 测试中需要特殊考虑,因为每次 Web 测试运行时,它们都可能更改。具有硬编码参数值的 Web 测试在记录后不可能长时间工作,甚至根本无法工作。
Web 测试使用提取规则和上下文绑定启用具有动态参数的测试。提取规则位于包含动态值的页面请求中。当提取规则运行时,它使用诸如“myparam”这样的可配置名将动态值提取到 Web 测试上下文中。然后,后续请求包含一个具有值 {{myparam}} 的查询字符串或窗体参数。当 Web 测试运行时,Web 测试上下文中的值替换为 {{myparam}}。
提取规则的事件序列如下所示:
-
Web 测试引擎开始执行 Request1。
-
Request1 发送到目标服务器。
-
从目标服务器接收一个响应。
-
针对 Request1 的提取规则在响应页上运行。
-
提取规则在 Web 测试上下文中放置一个项。
-
Web 测试引擎开始执行 Request2。
-
查询字符串参数、窗体参数和 Request2 上其他任何上下文绑定的值从 Web 测试上下文替换。
-
Request2 发送到目标服务器。
自动隐藏字段跟踪
Web 测试包含用于处理动态隐藏字段(例如,__VIEWSTATE)的特殊支持。当记录一个 Web 测试时,隐藏字段自动与窗体和查询字符串参数匹配。当发现匹配时,ExtractHiddenFields 规则应用于生成隐藏字段源的请求。此时,上下文绑定应用于该请求的参数,从而利用隐藏字段。
ExtractHiddenFields 是一个特殊的提取规则,因为与将值提取到上下文的规则不同,它将页上的每个隐藏字段值提取到 Web 测试上下文。普通的提取规则使用 ContextParameter 属性确定用于上下文参数的名称,但是,ExtractHiddenFields 使用该属性仅用于区分可能同时存在于上下文中的多组隐藏字段。例如,其 ContextParameter 设置为 1 的 ExtractHiddenFields 规则将提取 __VIEWSTATE 作为“$Hidden1.__VIEWSTATE”。
修复 __EVENTTARGET 以及其他由 JavaScript 修改的隐藏窗体字段
当隐藏字段由 Javascript 在 OnClick 事件处理程序中修改时,可能会错误地应用自动隐藏字段绑定。这是 Visual Studio 2005 发布版本中的一个已知的错误。
对于 ASP.NET 站点,该问题通常在 Web 控件调用 __doPostBack() JavaScript 方法设置如上所示的 __EVENTTARGET 隐藏字段时发生。自动隐藏字段绑定让窗体参数具有诸如 {{$HIDDEN1.__EVENTTARGET}} 这样的值,而不是实际值 — btnNext。要更正该问题,必须将参数值设置为要在 Javascript 中设置的值(例如,btnNext)。
在记录中丢失的请求
正如在“了解 Web Test Recorder”部分中讨论的那样,某些请求(例如,AJAX 请求和一些弹出窗口)不可能由 Web Test Recorder 进行记录。庆幸的是,Eric Lawrence 编写了一个称为 Fiddler 的优秀工具,它对此很有帮助。Fiddler 用作代理服务器,可以截获所有 HTTP 通信量(仍不具有 SSL 支持)。下面描述的两个选项使用 Fiddler 来更正一个无法用标准 Web Test Recorder 进行记录的 Web 测试。
从 Fiddler 保存 Web 测试
当 Web Test Recorder 丢失一些 AJAX、ActiveX 或弹出窗口请求时,一个选项用于使用 Fiddler 记录整个测试。Fiddler 可以将一系列捕获的请求保存为可以添加到 Visual Studio 2005 测试项目的 .webtest 文件。
当无法使用 Web 测试记录器记录大量请求时,该选项最为适用。该选项的主要限制是,Fiddler 创建的 Web 测试不使用自动隐藏字段跟踪(例如,针对 __VIEWSTATE),而且不筛选诸如图像、CSS 和 JavaScript 的依赖请求。
从 Fiddler 手动添加请求
对于丢失请求的另一个选项是:使用 Fiddler 确定需要将哪些请求手动添加到 Web 测试。当 Web Test Recorder 丢失少量请求时,该方法最适用,因为您仍然可以受益于诸如自动隐藏字段跟踪和依赖请求筛选这样的功能。
在本例中,最好同时使用 Fiddler 和 Web 测试记录器记录 Web 测试。这使您能够比较这两个记录来发现丢失的请求。如果明显丢失了一个请求(例如,如果您知道发生了一个 AJAX 请求),则在记录过程中插入一个注释也是很有帮助的。该注释作为手动创建请求的占位符。
以下屏幕快照显示如何从 Fiddler 捕获手动创建请求。请记住,需要将任何所需的提取规则、用于参数值的上下文绑定以及 ThinkTime 添加到手动创建的请求。
深入研究进行编码的 Web 测试
普通的 Web 测试旨在处理大量的 Web 测试方案。尽管数据绑定、提取规则、插件和上下文参数提供了对 Web 测试执行的大量控制,但有时甚至需要更多的控制。通过提供可在 Visual C# 或 Visual Basic 中使用的 .NET API,进行编码的 Web 测试能提供最多的控制和可扩展性。通过该 API,您可以使用 Web 测试引擎的全部功能,包括下一节中描述的功能。
生成代码的时机与原因
只有在正常的 Web 测试达到极限后,才能生成进行编码的 Web 测试。在正常的 Web 测试中,最明显的限制是循环(您无法多次运行请求的子集)和分支(您无法有条件地执行一组请求)。生成代码的其他原因包括细粒度的事件处理以及以编程方式设置参数值。
使用图形化的 Web 测试编辑器编辑 Web 测试比直接编辑代码更容易且较少出错。因此,在生成代码之前,您应该尽可能地获得 Web 测试。这包括添加提取和验证规则,设置数据绑定,创建针对请求组的事务,以及调整思考时间。针对 Web 测试所有这些方面而生成的代码,为深入进行自定义编码的 Web 测试奠定了牢固的基础。
以下几节演示需要对 Web 测试进行编码(或通过编码将明显简化)的一些功能和示例。
分支
当 Web 测试必须有条件地发出不同的请求集时,进行 Web 测试的编码。
以下示例对这一点进行演示,其中 Web 测试使用数据绑定的用户凭据登录到 Web 站点,如果用户在该系统中不存在,必须创建一个新用户帐户。
using System; using System.Collections.Generic; using System.Text; using Microsoft.VisualStudio.TestTools.WebTesting; using Microsoft.VisualStudio.TestTools.WebTesting.Rules; [DataSource("DataSource1", "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\"test.mdb\"", DataBindingAccessMethod.Sequential, "Credentials")] [DataBinding("DataSource1", "Credentials", "UserName", "DataSource1.Credentials.UserName")] [DataBinding("DataSource1", "Credentials", "Password", "DataSource1.Credentials.Password")] public class BranchingCoded : WebTest { public BranchingCoded() { } public override IEnumerator GetRequestEnumerator() { // Go to the Web application's home page WebTestRequest request1 = new WebTestRequest("http://testserver/website"); request1.ThinkTime = 4; yield return request1; // Go to the login page WebTestRequest request2 = new WebTestRequest("http://testserver/website/Login.aspx"); request2.ThinkTime = 16; ExtractHiddenFields rule1 = new ExtractHiddenFields(); rule1.ContextParameterName = "1"; request2.ExtractValues += new EventHandler(rule1.Extract); yield return request2; // Attempt to login WebTestRequest request3 = new WebTestRequest("http://testserver/website/Login.aspx"); request3.ThinkTime = 6; request3.Method = "POST"; FormPostHttpBody request3Body = new FormPostHttpBody(); request3Body.FormPostParameters.Add("__VIEWSTATE", this.Context["$HIDDEN1.__VIEWSTATE"].ToString()); request3Body.FormPostParameters.Add("username", this.Context["DataSource1.Credentials.UserName"].ToString()); request3Body.FormPostParameters.Add("password", this.Context["DataSource1.Credentials.Password"].ToString()); request3.Body = request3Body; yield return request3; // If the login failed, create a new user account if (LastResponse.StatusCode != System.Net.HttpStatusCode.OK) { WebTestRequest request4 = new WebTestRequest("http://testserver/website/register.aspx"); request4.ThinkTime = 9; ExtractHiddenFields rule2 = new ExtractHiddenFields(); rule2.ContextParameterName = "1"; request4.ExtractValues += new EventHandler(rule2.Extract); yield return request4; WebTestRequest request5 = new WebTestRequest("http://testserver/website/register.aspx"); request5.ThinkTime = 5; request5.Method = "POST"; FormPostHttpBody request5Body = new FormPostHttpBody(); request5Body.FormPostParameters.Add("__VIEWSTATE", this.Context["$HIDDEN1.__VIEWSTATE"].ToString()); request3Body.FormPostParameters.Add("username", this.Context["DataSource1.Credentials.UserName"].ToString()); request3Body.FormPostParameters.Add("password", this.Context["DataSource1.Credentials.Password"].ToString()); request5Body.FormPostParameters.Add("confirmpassword", this.Context["DataSource1.Credentials.Password"].ToString()); request5.Body = request5Body; yield return request5; } // Go view the user's profile now that the user is logged in WebTestRequest request6 = new WebTestRequest("http://testserver/website/userprofile.aspx"); yield return request6; } }
文章来源于领测软件测试网 https://www.ltesting.net/