Visual Studio是可以用于性能测试的工具之一。Visual Studio Test版或Visual Studio 2010旗舰版为自动化测试提供了支持。本文介绍Visual Studio的Web测试功能。
介绍
对于一个多用户的应用程序,性能是非常重要的。性能不仅是执行的速度,它包括负载和并发方面。性能测试是对以下问题的回答:
- 我们如何确保我们的新的应用程序将支持预期的用户负载?
- 如何避免在实际负载条件下会出现的问题?
- 如何确定响应时间?
- 如何规划服务器的容量?
Visual Studio是可以用于性能测试的工具之一。Visual Studio Test版或Visual Studio 2010旗舰版为自动化测试提供了支持。
这将是第一部分的一系列文章。
-
Part I: Web测试和配置
-
Part II:负载测试,RIG和负载测试术语。
-
Part III:性能计数器,负载测试结果分析。
VS 2010测试类型
Visual Studio支持多种可用于执行自动化测试的测试类型。图1显示了VSTS 2010中的主要测试类型。
Basic Unit Test,Unit Test和Unit Test Wizard帮助创建新的单元测试的类文件。这将有助于开发人员和测试人员执行单元测试。
Coded UI test用于从手工测试中录制UI活动。
Database Unit Test用于测试存储过程和函数。
Generic Test用于把一个可执行文件封装成一个测试方法。您可以把一个可执行文件封装成一个测试方法并把它包含在自动化测试中。
Ordered test是在一个特定的顺序中执行多个测试脚本。
Web Performance Test用于录制URL并生成用于性能测试的代码。
Web测试
Web Performance Test或者Web test,可以执行Web应用程序测试。我们将通过选择从测试项目模板创建一个测试项目。
一旦你创建好测试项目,右键单击该项目,并添加新的项。选择Web Performance Test,这将在Internet Explorer中打开录制器,并开始录制URL和相关参数。
录制
下图显示了Web测试录制器,录制在google上的操作。
如何向现有 Web 性能测试中添加 Web 录制,参见 https://docs.microsoft.com/zh-cn/previous-versions/ff406969(v=vs.110)
在这个例子中,我们记录到谷歌的搜索功能。一旦录制结束,在Web测试录制器中使用“停止”按钮停止录制。这将把录制的URL生成一个Web测试,并添加动态的相关性。
相关性(Correlation)
什么是相关性,简而言之就是上一次Web请求的响应和下次请求之间的关联。
例如,当你登录网站时,它会生成一个用于跟踪会话的SID。登录后,这SID被传递给了客户端。随着下一个请求,存储的SID将被发送到服务器。当您使用Web测试录制这一操作时,它会记录这个值并硬编码保存。但是,下一次运行时SID会有所不同。
为了避免这样的情况,需要把登录请求的响应中的SID值关联为下次请求时的参数。 首先,我们将提取的SID值,并储存为一个上下文参数,它将被作为参数传递给下一个请求。
上下文参数(Context Parameter)
上下文参数,它就像全局变量一样。如果你想在所有的URL中使用同一个参数,那么把它声明为上下文参数。
例如,我们需要在多种环境中运行该脚本。不需要为每种环境单独录制一遍,而是定义一个名为’WebServer’的上下文参数,并在每个需要用到的URL中使用它。上下文参数需要在两层大括号中引用,如{{Webserver}}。把所有的URL修改为使用相同的上下文参数。当你想在另一个环境中运行该脚本,修改上下文参数的值,所有的URL将自动更新。
Query String作为URL的一部分被记录下来。
在测试环境中,Query String输入的值会有所不同,这取决于测试类型:正向测试,反向测试,边界测试等。为了传递多个不同值的参数,我们需要做参数化。
参数化(Parameterization)
为了参数化参数(arguments),需要添加数据源用于选取参数值。右键单击Web test - >选择“Add Data Source”选项。
选择数据源的类型。数据源可以是数据库,如SQL Server,Oracle和Excel数据,也可以是CSV或XML文件。
选择数据文件或数据库,表中包含输入数据。数据预览将显示在向导中。
点击Query String来查看Query String的属性。更改该值使用数据源映射如下。
数据源绑定将作为Query String的值显示
提取规则
提取规则被用于从一个请求的响应中提取的数据。我们有以下提取选项:提取表单字段,提取HTTP头等,提取的值可以用来作为下一个Web请求的一部分,或可用于任何商业决策。
在下面的例子中,我使用”提取文本“选项,提取从服务器传递来的一个id。通过分析HTML输出,我们在规则属性的“Starts With”和“Ends With”中填入对应的匹配值。这个HTML响应将作为最终窗口的一部分显示(不久将讨论)。
验证规则
验证规则用于确保你处于正确的页面中。例如登录后,你可能会期待页面中包含”SignOut”文本。以下验证规则,验证当前的响应中是否包含“SignOut”文本。
我们可以使用以下验证规则属性中的任何选项形成验证规则。Visual Studio 2010和2008会为录制的测试自动添加响应URL验证。
事务(Transaction)
事务是执行一个特定操作所必需的,一组操作或来回应答的集合。例如,购买一本书的过程中包括的步骤:选择一本书,加入购物车,检查和付款。
定义事务将有助于分析结果。通常情况下,响应时间,响应字节等信息,将分别为每个URL显示。一旦你定义了事务,响应时间和其他衡量数据,将在事务级别显示。
运行测试
当你运行测试,你将看到如下屏幕,在这里你可以看到每个URL的状态,结果在web浏览器中是如何显示的,哪些参数作为请求的一部分传递,HTML响应,上下文参数和其他一些细节。
这是单次运行的情况。如果你想使用参数化来运行多次测试,请单击屏幕顶部的“Edit run Settings“选项。
在这里,您可以指定需要运行测试的次数。每次运行都会从参数数据源中选择一个记录来运行测试。运行次数的同时,我们可以指定浏览器的类型,这样就可以模拟网站在不同的浏览器中的展现。
设置运行设置后,在屏幕顶部选择”Click here to run again“选项。
生成代码
生成代码选项允许您根据脚本创建对应的代码。
这将生成一份和 web test对应的C#代码,同时为它创建一个单独的测试。我们可以修改代码,而不会影响原有的web test,反之亦然。
我们可以使用C#的功能,来自定义这种编码的Web测试。可以使用循环迭代一个操作或用ADO.NET连接到数据库为请求提取一些数据,强制执行思考时间(在PartII中讨论)和处理WCF Services。
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:4.0.30128.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace GoogleTestProject { using System; using System.Collections.Generic; using System.Text; using Microsoft.VisualStudio.TestTools.WebTesting; using Microsoft.VisualStudio.TestTools.WebTesting.Rules; public class WebTest1Coded : WebTest { public WebTest1Coded() { this.Context.Add("WebServer", "www.google.co.in"); this.PreAuthenticate = true; } public override IEnumerator<WebTestRequest> GetRequestEnumerator() { // Initialize validation rules that apply to all requests in the WebTest if ((this.Context.ValidationLevel >= Microsoft.VisualStudio.TestTools.WebTesting.ValidationLevel.Low)) { ValidateResponseUrl validationRule1 = new ValidateResponseUrl(); this.ValidateResponse += new EventHandler<ValidationEventArgs>(validationRule1.Validate); } if ((this.Context.ValidationLevel >= Microsoft.VisualStudio.TestTools.WebTesting.ValidationLevel.Low)) { ValidationRuleResponseTimeGoal validationRule2 = new ValidationRuleResponseTimeGoal(); validationRule2.Tolerance = 0D; this.ValidateResponseOnPageComplete += new EventHandler<ValidationEventArgs>(validationRule2.Validate); } WebTestRequest request1 = new WebTestRequest("http://www.google.com/"); request1.ThinkTime = 1; request1.ExpectedResponseUrl = "http://www.google.co.in/"; ExtractHiddenFields extractionRule1 = new ExtractHiddenFields(); extractionRule1.Required = true; extractionRule1.HtmlDecode = true; extractionRule1.ContextParameterName = "1"; request1.ExtractValues += new EventHandler<ExtractionEventArgs>(extractionRule1.Extract); yield return request1; request1 = null; WebTestRequest request2 = new WebTestRequest("http://clients1.google.co.in/generate_204"); yield return request2; request2 = null; WebTestRequest request3 = new WebTestRequest("http://www.google.co.in/csi"); request3.QueryStringParameters.Add("v", "3", false, false); request3.QueryStringParameters.Add("s", "webhp", false, false); request3.QueryStringParameters.Add("action", "", false, false); request3.QueryStringParameters.Add("e", "17259,18168,23730,24808", false, false); request3.QueryStringParameters.Add("ei", "gV_yS7vIKYqC7QPFmZznCw", false, false); request3.QueryStringParameters.Add("expi", "17259,18168,23730,24808", false, false); request3.QueryStringParameters.Add("imc", "1", false, false); request3.QueryStringParameters.Add("imn", "1", false, false); request3.QueryStringParameters.Add("imp", "1", false, false); request3.QueryStringParameters.Add("rt", "prt.77,xjsls.102,ol.1085,iml.353,xjses.1817,xjs.1849", false, false); yield return request3; request3 = null; WebTestRequest request4 = new WebTestRequest("http://clients1.google.co.in/complete/search"); request4.QueryStringParameters.Add("hl", this.Context["$HIDDEN1.hl"].ToString(), false, false); request4.QueryStringParameters.Add("client", "hp", false, false); request4.QueryStringParameters.Add("expIds", "17259,18168,23730,24808", false, false); request4.QueryStringParameters.Add("q", "as", false, false); request4.QueryStringParameters.Add("cp", "2", false, false); yield return request4; request4 = null; WebTestRequest request5 = new WebTestRequest("http://clients1.google.co.in/complete/search"); request5.QueryStringParameters.Add("hl", this.Context["$HIDDEN1.hl"].ToString(), false, false); request5.QueryStringParameters.Add("client", "hp", false, false); request5.QueryStringParameters.Add("expIds", "17259,18168,23730,24808", false, false); request5.QueryStringParameters.Add("q", "asp", false, false); request5.QueryStringParameters.Add("cp", "3", false, false); yield return request5; request5 = null; WebTestRequest request6 = new WebTestRequest("http://clients1.google.co.in/complete/search"); request6.QueryStringParameters.Add("hl", this.Context["$HIDDEN1.hl"].ToString(), false, false); request6.QueryStringParameters.Add("client", "hp", false, false); request6.QueryStringParameters.Add("expIds", "17259,18168,23730,24808", false, false); request6.QueryStringParameters.Add("q", "asp.", false, false); request6.QueryStringParameters.Add("cp", "4", false, false); yield return request6; request6 = null; WebTestRequest request7 = new WebTestRequest("http://clients1.google.co.in/complete/search"); request7.QueryStringParameters.Add("hl", this.Context["$HIDDEN1.hl"].ToString(), false, false); request7.QueryStringParameters.Add("client", "hp", false, false); request7.QueryStringParameters.Add("expIds", "17259,18168,23730,24808", false, false); request7.QueryStringParameters.Add("q", "asp.n", false, false); request7.QueryStringParameters.Add("cp", "5", false, false); yield return request7; request7 = null; WebTestRequest request8 = new WebTestRequest(("http://" + (this.Context["WebServer"].ToString() + "/"))); request8.ExpectedResponseUrl = "http://www.google.co.in/#hl=en&source=hp&q=asp.net&rlz=1R2ADFA_enIN375&aq=f&aqi=&" + "aql=&oq=&gs_rfai=&fp=4a7b17d2fd7e8e7"; yield return request8; request8 = null; WebTestRequest request9 = new WebTestRequest("http://clients1.google.co.in/complete/search"); request9.QueryStringParameters.Add("hl", this.Context["$HIDDEN1.hl"].ToString(), false, false); request9.QueryStringParameters.Add("client", "hp", false, false); request9.QueryStringParameters.Add("expIds", "17259,18168,23730,24808", false, false); request9.QueryStringParameters.Add("q", "asp.net", false, false); request9.QueryStringParameters.Add("cp", "7", false, false); yield return request9; request9 = null; WebTestRequest request10 = new WebTestRequest("http://www.google.co.in/search"); request10.ThinkTime = 1; request10.QueryStringParameters.Add("hl", this.Context["$HIDDEN1.hl"].ToString(), false, false); request10.QueryStringParameters.Add("source", this.Context["$HIDDEN1.source"].ToString(), false, false); request10.QueryStringParameters.Add("q", "asp.net", false, false); request10.QueryStringParameters.Add("rlz", "1R2ADFA_enIN375", false, false); request10.QueryStringParameters.Add("aq", "f", false, false); request10.QueryStringParameters.Add("aqi", "", false, false); request10.QueryStringParameters.Add("aql", "", false, false); request10.QueryStringParameters.Add("oq", "", false, false); request10.QueryStringParameters.Add("gs_rfai", "", false, false); request10.QueryStringParameters.Add("fp", "4a7b17d2fd7e8e7", false, false); yield return request10; request10 = null; WebTestRequest request11 = new WebTestRequest("http://www.google.co.in/csi"); request11.QueryStringParameters.Add("v", "3", false, false); request11.QueryStringParameters.Add("s", "web", false, false); request11.QueryStringParameters.Add("action", "", false, false); request11.QueryStringParameters.Add("ei", "hl_yS7TeHYTHrAfgy7jKDQ", false, false); request11.QueryStringParameters.Add("e", "17259,18168,23730,24808", false, false); request11.QueryStringParameters.Add("cp", "false", false, false); request11.QueryStringParameters.Add("imp", "0", false, false); request11.QueryStringParameters.Add("imn", "1", false, false); request11.QueryStringParameters.Add("rt", "prt.586,pprt.599,ol.599,jsrt.565,iml.599", false, false); yield return request11; request11 = null; WebTestRequest request12 = new WebTestRequest("http://clients1.google.co.in/generate_204"); yield return request12; request12 = null; } } }
总结
Web Performance Test可以记录网站链接跳转并生成代码。我们可以使用Web测试脚本或从其生成的代码作为性能测试的输入。自定义的测试可以使我们做到:
参数化 - 使用户输入动态化
关联性 - 提取服务器产生的数据,用于后续请求。
PartII中,我们将探讨负载测试,PartIII是对性能测试结果进行分析和理解的主要性能计数器。