Microsoft Visual Studio 2005 Team Edition for Software Testers 引入了一个全新的用于 Web 和负载测试的强大工具集。Web 应用程序的负载测试可跨多个计算机进行并模拟成千上万个用户,但其核心则是一个 Web 测试集合。本文针对的读者是这样一些测试和开发人员,他们想学习更多有关创建有效的 Web 测试并对它们进行调试,以确保按预期方式运行的技术。
了解 Web Test Recorder
Web Test Recorder 挂钩到 Internet Explorer 对象模型,侦听不同的导航事件。该类记录的主要优点是,可以记录安全套接字层 (SSL) 和已验证身份的 Web 站点,而没有任何特殊的配置要求。
该记录方法的另一方面是,Web 测试中不记录诸如图像、级联样式表(cascading style sheet,CSS)文件和 JavaScript 文件这样的依赖请求。相反,这些依赖请求在 Web 测试执行过程中从 HTML 页中被分析出来并进行自动请求。该功能有助于 Web 测试能更好地还原装饰性的 Web 站点更改,使测试更关注于 Web 应用程序的实际使用。如果需要,也可以禁用该功能,方法是将请求的 ParseDependentRequests 属性设置为 false。
该记录方法的一个不足是,它可能无法记录由 JavaScript(例如,在 AJAX 站点上)、ActiveX 控件和某些类型的弹出窗口发出的请求,因为 Internet Explorer 无法始终引发所需的事件。在多数情况下,可手动地将丢失的请求添加回 Web 测试来解决这些问题,稍后将阐述这一点。
插入注释
在记录过程中插入注释有助于创建有效的 Web 测试,特别是当 Web 测试包含很多请求时。您应该使用注释来说明在 Web 测试的不同点应该采用什么逻辑操作,例如,“登录”、“将项 X 添加到购物车”等。当您稍后在 Web 测试编辑器中修改 Web 测试时,这些注释是非常有用的。
您也可以使用注释来说明需要添加什么样的验证规则以确保 Web 测试成功。较之于在 Web 测试编辑器中查看 HTTP 请求列表而言,在记录并查看页面时决定需要对每个请求进行哪些验证更容易进行。
谨记要记录 Think Time
Web 测试请求的 ThinkTime 属性是指用户在发出下一个请求之前花在当前页的“思考”时间。Think time 延迟用于在负载测试过程中估算实际用户的行为。由于 Think time 对 Web 测试可以生成的负载量会产生巨大的影响,因此在负载测试中可全面禁用它,以便将更大的负载应用到目标服务器。禁用 Think time 使您能够尽快发出对服务器的请求,而不会产生请求间的延迟。
Web 测试记录器在针对 Web 应用程序的请求进行记录时自动记录 Think time。在记录过程中,尝试估算用户通常在每个页面上将花费的时间。当记录完成后,查看针对每个请求记录的思考时间尤为重要。无意中造成的较长思考时间会极大地影响 Web 测试生成请求的速率。默认情况下,Think time 在 Web Test Viewer 中是关闭的。因此,较长的思考时间不可能立即体现出来。当 Think time 在 Web Test Viewer 中打开时,您将看到 HTTP 状态栏中显示“Thinking…[n]”,直到下一请求开始。默认情况下,Think time 在负载测试中打开。当暂停记录并输入注释时,Think time 计数器停止。
了解 Web 测试引擎
有关 Web 测试引擎需要了解的一个关键概念是,Web 测试在 HTTP 层工作。Web 测试包含一个 HTTP 请求列表;这些请求主要都是由查询字符串参数、窗体参数和针对 Web 服务器的 URL 组成的。Web 测试引擎执行这些 HTTP 请求,从服务器(一个或多个)检索响应,以及收集计时数据。
由于 Web 测试引擎在 HTTP 层起作用,因此它并不直接模拟客户端脚本,例如,JavaScript 或 ActiveX 控件。Web 测试关注在服务器上生成负载。因此,只影响 Web 页外观的客户端脚本对 Web 测试而言并不重要。那些在附加 HTTP 请求(例如,AJAX)中设置参数值或结果的客户端脚本影响服务器上的负载,而且可能需要手动修改 Web 测试来模拟该脚本。本文稍后将描述这些修改类型。
一个常见的误解是,由于记录发生在 Internet Explorer 中,因此认为 Web 测试必须使用 Internet Explorer 执行。事实并非如此。所有请求直接使用 Web 测试引擎执行;不发生与 Internet Explorer 或其他任何浏览器的交互。Web 测试引擎使用标准 HTTP 请求/响应消息直接与目标 Web 服务器通讯。
同样,在 Web Test Viewer 的 Web Browser 选项卡上嵌入的 Internet Explorer 控件只显示由 Web 测试引擎接收的响应页。Web 测试引擎将响应写入磁盘的临时位置,然后将临时文件加载到 Web Test Viewer 中的 Internet Explorer 控件。如果用于其他浏览器的页面没有在 Web Browser 选项卡上中正确显示,则使用 Response 选项卡对其进行验证。
另一个容易令人混淆的方面是,运行 Web 负载测试时可以选择不同的浏览器模板。这些浏览器模板只影响随每个请求发送的默认 HTTP 标头集。Web 服务器用于确定浏览器类型的关键标头是 UserAgent 标头。Web 测试引擎直接使用标准 HTTP 协议发出请求,而无论选择哪个浏览器模板。
使用 Web Test Viewer 验证 Web 测试
在将 Web 测试添加到一个负载测试并进行长期运行之前,重要的是确保该测试按预期方式正常工作。这就是引入 Web Test Viewer 的原因。Web Test Viewer 使您能够在运行 Web 测试时对其进行监视,并查看前一测试运行的所有方面。
要验证一个新建的 Web 测试是否过载,只需查看该测试运行的结果并查明它是否通过。例如,对于一个不具有验证规则的 Web 测试而言,通过只意味着没有引发异常,没有规则失败,而且没有出现 HTTP 错误。对于验证而言,除了确保无错误执行外,还包括确保 Web 测试在目标 Web 应用程序上表现出正确的行为。重要的是查看每个请求的响应以确保它是正确的。
下表列出验证 Web 测试时需要查看的项,以及有关每种问题类型的附加信息。
表 1 要检查的问题 |
附加信息/解决方案 |
---|---|
HTTP 请求错误 |
HTTP 错误由 400-599 范围内的一个响应状态代码表示。通常,一个 HTTP 错误包含一个指示该问题原因的响应体。 |
未找到依赖请求 |
顶级请求可能由于它其中的一个依赖请求未被发现而导致失败。这可能指示 Web 应用程序的 HTML 有问题。可以通过禁用针对该请求的 ParseDependentRequests 属性避免这些错误。这样就防止依赖请求(例如,图像、CSS 和 javascript)被自动地分析为 HTML 并进行请求。 |
提取和验证规则失败 |
提取和验证规则失败在 Details 选项卡中显示。造成失败的原因通常是 Web 服务器返回了包含意外内容的页,例如,重定向到一个登录页或错误的页。 |
测试级异常 |
在 Web Test Viewer 中,测试级异常显示为最后一次成功请求后的节点。测试级异常包括 WebTestPlugins、PreWebTest 和 PostWebTest 事件处理程序中的异常,以及非特定于编码 Web 测试中具体请求的异常。 |
请求级异常 |
请求级异常导致单个请求失败,但允许 Web 测试继续。这些异常包括从 WebTestRequestPlugins、PreRequest 和 PostRequest 事件处理程序,以及提取和验证规则引发的异常。异常消息以及堆栈跟踪(如果可用)在 Web Test Viewer 中 Details 选项卡的底部显示。 |
服务器返回的错误页内容 |
要验证 Web 服务器返回正确的内容,通常在 Web Test Viewer 的 Web Browser 和 Response 选项卡中以可视化方式检查每个页面。 |
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 测试执行的大量控制,但有时甚至需要更多的控制。通过提供可在 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; } }
循环
当 Web 测试必须循环以发出在测试中动态确定的大量请求时,进行 Web 测试的编码。
以下示例对这一点进行演示,其中 Web 测试执行搜索,然后在搜索结果中连接每个链接。
using System; using System.Collections.Generic; using System.Text; using Microsoft.VisualStudio.TestTools.WebTesting; public class LoopingCoded : WebTest { public LoopingCoded() { } public override IEnumerator GetRequestEnumerator() { // Issue a search for the term "Microsoft" WebTestRequest request7 = new WebTestRequest("http://testserver/website/Search.aspx"); request7.ThinkTime = 20; request7.Method = "POST"; FormPostHttpBody request7Body = new FormPostHttpBody(); request7Body.FormPostParameters.Add("txtSearch", "Microsoft"); request7.Body = request7Body; yield return request7; // Loop through each anchor tag in the search result and issue a request to each tag's target url (href) foreach (HtmlTag tag in this.LastResponse.HtmlDocument.GetFilteredHtmlTags("a")) { WebTestRequest loopRequest = new WebTestRequest(tag.GetAttributeValueAsString("href")); yield return loopRequest; } } }
细粒度的事件处理
当只想将 PreRequest 和 PostRequest 事件处理程序应用于特定请求时,进行 Web 测试的编码。正常的 Web 测试将 WebTestRequestPlugins(处理这两种事件)应用于 Web 测试中的所有请求。
以下示例对这一点进行演示,其中 Web 测试将两个请求的响应体记录到磁盘,以用于调试和基准调整。
using System; using System.Collections.Generic; using System.IO; using System.Text; using Microsoft.VisualStudio.TestTools.WebTesting; public class EventHandlingCoded : WebTest { public EventHandlingCoded() { } public override IEnumerator GetRequestEnumerator() { WebTestRequest request1 = new WebTestRequest("http://testserver/website"); request1.ThinkTime = 8; yield return request1; // Log this response out to a file WebTestRequest request2 = new WebTestRequest("http://testserver/website/products.aspx"); request2.ThinkTime = 2; request2.QueryStringParameters.Add("CategoryID", "14", false, false); request2.PostRequest += new EventHandler(request2_PostRequest); yield return request2; WebTestRequest request3 = new WebTestRequest("http://testserver/website/products.aspx"); request3.ThinkTime = 2; request3.QueryStringParameters.Add("CategoryID", "15", false, false); yield return request3; // Log this response out to a file, too WebTestRequest request4 = new WebTestRequest("http://testserver/website/products.aspx"); request4.ThinkTime = 1; request4.QueryStringParameters.Add("CategoryID", "20", false, false); request4.PostRequest += new EventHandler(request4_PostRequest); yield return request4; } void request2_PostRequest(object sender, PostRequestEventArgs e) { File.WriteAllBytes("c:\\request2.html", e.Response.BodyBytes); } void request4_PostRequest(object sender, PostRequestEventArgs e) { File.WriteAllBytes("c:\\request4.html", e.Response.BodyBytes); } }
使用编码的 Web 测试模拟 Web JavaScript
如前所述,Web 测试引擎工作在 HTTP 层并且不运行 JavaScript。以影响 HTTP 层的方式依赖于 JavaScript 的 Web 站点,可使用编码的 Web 测试模拟通常由 JavaScript 执行的逻辑。
以下代码是一个简单的 Web 页,它收集电话号码,并在窗体提交时使用 JavaScript 除去一些非数字字符。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html > <head> <title>Untitled Page</title> <script type="text/javascript"> //get rid of some common non-digit characters // in the phone number string function FixPhoneNumber() { var phoneNumberElement = document.getElementById('phoneNumber'); var number = phoneNumberElement.value; number = number.replace('-', ''); number = number.replace('(', ''); number = number.replace(')', ''); number = number.replace(' ', ''); phoneNumberElement.value = number; return true; } </script> </head> <body> <form action="http://testserver/testwebsite/showparameters.aspx" method="post"> <div> <input name="phoneNumber" id="phoneNumber" type="text"/> <input name="submit" type="submit" value="Submit" onclick="FixPhoneNumber()" /> </div> </form> </body> </html>
当使用具有 phoneNumber 字段的数据绑定测试这种页时,可能会产生问题。如果数据源以不正确的格式包含电话号码,则非预期的数据将提交到服务器,从而导致 Web 测试失败。理想情况是:服务器在禁用 JavaScript 的情况下验证并修复数据,但是可能仍然需要 Web 测试来模拟客户端脚本运行。
在多数情况下,编码的 Web 测试可以轻松模拟影响 HTTP 请求的 JavaScript。以下示例演示如何模拟上面的 JavaScript。
public override IEnumerator GetRequestEnumerator() { WebTestRequest request1 = new WebTestRequest("http://testserver/testwebsite/default.aspx"); yield return request1; WebTestRequest request2 = new WebTestRequest("http://testserver/testwebsite/showparameters.aspx"); request2.Method = "POST"; FormPostHttpBody request2Body = new FormPostHttpBody(); //get the databound phone number from the context and // strip out certain characters to simulate JavaScript string phoneNumber = this.Context["PhoneNumber"].ToString(); phoneNumber = phoneNumber.Replace("-", ""); phoneNumber = phoneNumber.Replace("(", ""); phoneNumber = phoneNumber.Replace(")", ""); phoneNumber = phoneNumber.Replace(" ", ""); request2Body.FormPostParameters.Add("phoneNumber", phoneNumber); request2Body.FormPostParameters.Add("submit", "Submit"); request2.Body = request2Body; yield return request2; }
运行并调试编码的 Web 测试
要运行编码的 Web 测试,请在 Test View 或 Test Manager 窗口中选择该测试,并单击 Run 工具栏按钮。当编码的 Web 测试开始运行之后,可双击 Test Results 窗口以显示 Web Test Viewer。同样的功能可用于验证:编码的 Web 测试对于正常的 Web 测试而言是正确的。
由于 Web 测试与 Visual Studio 集成,因此可通过选择 Debug instead of Run 调试一个编码的 Web 测试,如以下屏幕快照所示。可在编码的 Web 测试中的任何位置设置断点,并且所有的标准调试机制均可用。