使用 Apache JMeter 测试基于云的应用程序

学习使用 JMeter 进行 RESTful API 测试的有效技术和最佳实践

JMeter 是一个强大的测试工具,可用于测试在云中运行的应用程序。但是,如果您不知道如何有效地使用 JMeter,那么管理和维护 JMeter 脚本可能极富挑战性。本文演示的技术可以帮助您在基于云的应用程序测试中实现精心设计的自动化 JMeter 任务。

Shalini Gilra, 自动化负责人, IBM

作者照片Shalini 是位于印度 Pune 的 IBM 的一名自动化主管和部署工程师。她在功能测试、自动化和 IBM WebSphere Portal 的 2 级支持方面具有丰富的经验。 她也持有多个认证。



Prem Prakash, 软件测试专家, IBM

premprakashPrem Prakash 是一名软件测试专员,在位于 Pune 的 IBM 印度软件实验室工作。他拥有约 12 年的 IT 工作经验,擅长服务器端测试、shell 和 Perl 脚本测试,以及数据库测试。



Tom Tuohy, 自动化开发人员, IBM

tuohyTom Tuohy 是一名 SmartCloud 平台的自动化开发人员,在 IBM Littleton, Massachusetts Labs 工作。在 IBM,他负责设计、构建并为 Lotus Notes/Domino、Lotus Quickplace/Quickr、Lotus Connections 和 Sterling Commerce 的自动化框架做出了许多贡献。他拥有超过 20 年的从业经验,擅长对用 C、C++ 和 Java 编写的 API、Web 服务和企业应用程序进行后端测试。



2013 年 12 月 19 日

基于云的应用程序通常涉及几个组件,它们通过 API 进行交互,以 XML 或 JavaScript Object Notation (JSON) 格式交换数据。本文介绍的技术使用了 Apache JMeter(一个基于 GUI 的开源测试应用程序)执行支持云的应用程序的功能、性能和可靠性测试,这些应用程序使用 RESTful Web API 和 JSON。

RESTful Web API

RESTful web API 使用了 HTTP 和 Representational State Transfer (REST) 原则,其中包括:

  • API 的基本统一资源标识符(Uniform Resource Identifier,URI)
  • API 支持的数据,往往是 JSON 格式
  • API通过使用 HTTP GETPUTPOSTDELETE 方法支持的操作集

了解 JMeter 作为 RESTful API 测试工具的功能,以及它与测试基于云的应用程序的相关性。因为多租户云是一个重要的云特性,所以本文将会介绍以编程方式整合数据分离和数据检索的技术,以确保数据的完整性。本文展示了如何编写有助于维护、可重用性和模块化的有效的 JMeter 测试脚本(也被称为测试计划 或 JMX 文件)。还了解了如何使用配置和属性文件,以确保可以在多种环境中运行相同的脚本。

作者假设您熟悉 JMeter 的用户界面,并拥有大量使用 JMeter 的经验。

在哪里设置实现最优脚本重用的属性

由于支持云的应用程序通常可以轻松、快速地进行复制和部署,所以可以在多种环境中对其进行测试。如果您需要在多个环境中测试和运行自动化脚本,那么可以在 JMeter 中使用一个独立的属性文件为连接资源(如,应用服务器和数据库)定义数据(包括登录凭据),这样做很有好处。

在 JMETER_HOME/bin 目录下的三个文件中定义 JMeter 的属性和变量。在启动 JMeter 时,它会按以下顺序加载这些文件:

  1. jmeter.properties
  2. 一个可选的用户定义的属性文件
  3. system.properties

当 JMeter 正在运行的时候,如果要添加任何新属性或改变现有的属性,则必须先关闭 JMeter,然后重新启动它,使更改生效。

jmeter.properties 文件存储与 JMeter 应用程序本身有关的属性。这个文件中 保留了特定于 JMeter 程序的属性或特定于框架的属性。创建一个单独的文件(文件名由您选择),用该文件来存储测试环境特定的属性和变量,它们对于和接受测试的应用程序有关联的所有脚本来说是全局的属性和变量 — 例如,管理员的用户名/密码。在 jmeter.properties 文件中,取消对 user.properties 设置的注释,并将 user.properties 的值设置为所创建的文件的名称。清单 1 中的示例将该值设置为 myuser.properties

清单 1. 在 jmeter.properties 文件中指定一个用户属性文件
# Should JMeter automatically load additional JMeter properties?
# File name to look for (comment to disable)
user.properties=myuser.properties

清单 2 中的示例用户属性文件显示了在用户属性文件中定义变量所用的格式。(在该定义中,等号左边的任何地方都不允许有空格;必要时,属性值可以包含空格)。

清单 2. 示例用户属性文件
#----------------------------------------------------------------
# FVT API Test Environment parameters
#----------------------------------------------------------------
#
# --- Login Credentials
USER_LOGIN=admin@in.ibm.com
USER_PASSWORD=password
#
# --- Application Server
APP_SERVER=localhost
APP_PORT=80
APP_CONTEXT=ctx
PROTOCOL=http
#
# --- Database Server${DB_NAME}
DB_HOST=localhost
DB_PORT=50000
DB_NAME=dbname
DB_USER=dbadmin
DB_PASSWORD=dbpassword

应保留 JMeter 的第三个属性文件 system.properties,以便必须为所有脚本定义的全系统属性能够使用它。例如,如果您的所有脚本都使用某个特定的数据库服务器,那么您可以在 system.propterties 文件中指定相关的属性。

JMeter 的 User Defined Variables 控制面板(如图 1 所示)显示了 JMeter 脚本如何读取在用户属性文件中定义的属性。

图 1. JMeter 脚本如何读取在用户属性文件中定义的配置数据
JMeter User Defined Variables 控制面板的屏幕截图,显示了 JMeter 脚本如何读取已在用户属性文件中定义的属性

控制面板的 Value 列中的每个项目的格式为:

${__property(VARIABLE_NAME,VARIABLE_NAME)}

例如,来自用户属性文件的 USER_LOGIN 变量被读取为脚本中的 ${__property(USER_LOGIN, USER_LOGIN)} 函数。括号中的第一个 USER_LOGIN 是在属性文件中定义的变量的名称(并且已在控制面板的 Name 列中列出)。如果属性文件中没有定义变量,那么第二个实例是默认值或回退值。

何时在属性文件中定义一个变量,何时将它定义为 JMeter 脚本里面的一个变量,这些并没有严格的规定。但有两个准则可以帮助您在多个 JMeter 脚本中实现一致性,并减少不必要的重复变量定义:

  • 如果在几个脚本一致地使用相同的值,那么可以在用户属性文件或 system.properties 文件中定义数据。这方面的示例包括系统变量(例如,数据库名称和服务器名称),以及执行范围的变量(例如,日志级别)。
  • 如果一些脚本使用一个在各脚本中可能会变化的值,那么可以将它定义为一个脚本变量,或在外部数据文件中定义它,例如,逗号分隔值(CSV)文件。

使用 JSON 模板文件实现有效负载分离

许多云 API 要求使用 JSON 有效负载作为输入。JSON 定义了一组结构化的元素,可以将它们嵌套在其他元素中。每一个元素定义一个或多个名称/值对。功能测试包括以指定格式反复提供数据。例如,在一个典型的 REST API 调用中,JSON 有效负载在 REST HTTP 请求的主体中传递,并且通常包含硬编码的数据。硬编码的数据通常会在多个测试中重复,并分散在整个脚本中。

这种方法的一个常见问题是,如果 JSON 结构(或数据)发现变化(也许是因为 API 参数的更改),那么您必须进入 JMeter 测试,找到 HTTP 请求的主体,并修改 JSON 结构(和数据),以满足新的要求。如果在使用此 JSON 结构的多个 JMeter 测试用例中有数千个 HTTP 请求,那么必须执行许多重复的编辑。

更好的方法是创建一个 JSON 结构模板,并定义数据目的地的替换字符串。JSON 模板不包含任何硬编码的数据值,而是定义在脚本运行时随实际数据一起加载的引用变量。然后,模板被读入 JMeter 脚本中的某个变量,并在 HTTP 请求主体中被替换。

清单 3 显示了定义一个 JSON 有效负载的传统方式:

清单 3. JMeter 测试计划中的静态 JSON 定义
{
   "Customer":{
      "CustomerType":"DIRECT",
      "CustomerNumber":"1234567890",
      "Organization":{
         "OrgName":"IBM",
         "Phone":"999-999-9999",
         "AddressSet":[
            {
               "AddressLine1":"Tech Park One",
               "AddressLine2":"",
               "AddressType":"MAILING",
               "City":"Pune",
               "Country":"India",
               "State":"Maharashtra",
               "StateCode":"MH",
               "PostalCode":"411006"
            }
         ],
         "Contact":{
            "FamilyName":"Gilra",
            "GivenName":"Shalini",
            "EmailAddress":"shagilra@in.ibm.com",
            "NamePrefix":"Miss",
            "LanguagePreference":"EN_US",
            "WorkPhone":"999-9999"
         }
      }
   }
}

清单 4 显示了在模板中定义 JSON 的动态方式:

清单 4. 在外部 JSON 模板文件中的动态 JSON 定义
{ 
   "Customer":{ 
      "CustomerType":"${__eval(${STR_CUSTOMERTYPE})}", 
      "CustomerNumber":"${__eval(${STR_CUSTOMERNUMBER})}", 
      "Organization":{ 
         "OrgName":"${__eval(${STR_ORGNAME})}", 
         "Phone":"${__eval(${STR_PHONE})}", 
         "AddressSet":[ 
            { 
               "AddressLine1":"${__eval(${STR_ADDRESSLINE1})}", 
               "AddressLine2":"${__eval(${STR_ADDRESSLINE2})}", 
               "AddressType":"${__eval(${STR_ADDRESSTYPE})}", 
               "City":"${__eval(${STR_CITY})}", 
               "Country":"${__eval(${STR_COUNTRY})}", 
               "State":"${__eval(${STR_STATE})}", 
               "StateCode":"${__eval(${STR_STATECODE})}", 
               "PostalCode":"${__eval(${STR_POSTALCODE})}", 
            } 
         ], 
         "Contact":{ 
            "FamilyName":"${__eval(${STR_FAMILYNAME})}", 
            "GivenName":"${__eval(${STR_GIVENNAME})}", 
            "EmailAddress":"${__eval(${STR_EMAILADDRESS})}", 
            "NamePrefix":"${__eval(${STR_NAMEPREFIX})}", 
            "LanguagePreference":"${__eval(${STR_LANGUAGEPREFERENCE})}", 
            "WorkPhone":"${__eval(${STR_WORKPHONE})}", 
         } 
      } 
   } 
}

清单 3 中的 JSON 实体只包含硬编码的数据。相反,清单 4 中的模板只包含引用变量的名称,所以,任何 JMeter 测试计划都可以使用模板。实际数据被单独存储在 CSV 数据文件中,我们将在 下一节 讨论它。

请注意,清单 4 中的 JSON 为每个已定义的替代变量调用了 JMeter __eval() 函数。增加的这一步骤使得在运行时执行 JMeter 脚本的时候可以对变量进行计算。

图 2 和图 3 显示了如何在 JMeter 测试脚本中指定 JSON 实体模板文件。图 2 显示了 HTTP Request 控制面板:

图 2. 在测试中使用一个模板文件的内容
JMeter HTTP Request 控制面板的屏幕截图,配置该面板,以便在测试中使用某个模板文件的内容

在图 2 的示例中,CUSTOMER_JSON 变量表示整个JSON Customer 元素。该变量被封闭在 _eval() 函数中,显示为 HTTP 请求的主体(在 Parameters 选项卡上的 Send Parameters With the Request 标题下)。然后,在图 2 中,请求主体是 ${_eval(${CUSTOMER_JSON})}

在 User Parameters 控制面板中定义 CUSTOMER_JSON 变量,如图 3 所示:

图 3. 在 JMeter 脚本中读取 JSON 模板文件
用于 JMeter 测试的 User Parameters 控制面板的屏幕截图,显示了该脚本如何读取一个 JSON 模板文件

在图 3 中,CUSTOMER_JSON 变量被设置为 FileToString(),并使用指向 JSON 模板文件的路径作为参数。JSON 实体模板文件的所有内容都被读入 CUSTOMER_JSON 变量。因此,JSON 实体模板文件的内容是在运行时计算的,所定义的所有替代字符串都被翻译成为它们定义的数据。(下一节 将说明替代变量如何与实际数据相关联)。

因为 JMeter 的 JSON 实体模板文件对于 JMeter 测试脚本是外部文件,所以您可以将它们存储在一个单独的目录,比如 JMETER_HOME/tests/jsontemplates。当您访问 JMeter 测试计划中的某个 JSON 实体模板时,可指定相对于 JMeter BIN 目录的名称,例如:

../tests/jsontemplates/customer_template.json

您也可以将模板存储在 JMETER_HOME 目录以外的地方,在这种情况下,您必须提供绝对路径。


使用 CSV Data Set Config 元素的数据抽象

虽然将测试数据与 JMeter 测试计划分开在最初看起来像是执行了额外的工作,但是更简洁的测试中的分离结果也更容易管理。在需要修改测试时,就可以快速实现一些好处。某些数据可能仍然位于本地,存在于每个测试计划中,但大多数的测试数据抽象到外部文件中 — 在属性文件(正如我们前面讨论过的)或一个 CSV 数据配置文件中。所产生的测试套件更易于维护,而且在大多数情况下,不需要编辑 JMeter 测试计划就可以修改任何数据值。

JMeter 使​​用 CSV 文件存储以逗号分隔的数据行。JMeter 框架的 CSV Data Set Config 功能提供了一种在运行时动态读取 CSV 文件测试数据的方法。将测试数据存储为 CSV 另外一个好处是,您可以存储多行代表多个数据对象/变量的数据,或循环处理的多次迭代的数据。JMeter 2.3.4 及更高版本还支持提供一个 CSV 标题 作为文件的第一行。然后,使用标题的分隔字符串名称存储所定义的数据值。通常情况下,随后每一行的数据都代表循环的一次迭代。从这个意义上讲,该脚本主要是 “数据驱动的”,测试人员可以修改 CSV 文件中的数据,无需对 JMX 脚本进行任何更改。

测试人员还可以跨多个测试线程共享数据,并在多个 CSV 文件中存储测试数据。例如,如果有一个测试用一组用户凭据登录,然后为该用户创建一个订单,那么您可以创建两个 CSV 文件:一个用于保存用户凭据,另一个用于保存订单信息。有时候,可能需要创建多个 JMX 脚本来访问 CSV 文件。在这种情况下,您必须将 CSV 文件放在多个脚本都可以访问的位置。

在一个 CSV 文件内定义的迭代数据通常与变量名称有关联。您可以在标题中定义这些变量名称,用逗号分隔,并且它们一对一地与数据值的数量相匹配。当 JMeter 处理文件的每一行时,它会将适当的数据值分配给与该位置有关联的变量名称。清单 5 显示了样例 CSV 文件的内容:

清单 5. 样例 CSV 文件 (customer.csv)
"STR_TESTCASE","STR_CUSTOMERTYPE","STR_CUSTOMERNUMBER","STR_ORGNAME","STR_PHONE",
"STR_ADDRESSLINE1","STR_ADDRESSLINE2","STR_ADDRESSTYPE","STR_CITY","STR_COUNTRY",
"STR_STATE","STR_STATECODE","STR_POSTALCODE","STR_FAMILYNAME","STR_GIVENNAME",
"STR_EMAILADDRESS","STR_NAMEPREFIX","STR_LANGUAGEPREFERENCE","STR_WORKPHONE", 

"Testcase1","DIRECT","1234567890","IBM","999-999-9999","Tech Park One","","MAILING",
"Pune","India","Maharashtra","MH","411006","Gilra","Shalini","shagilra@in.ibm.com","Miss",
"EN_US","999-9999", 

"Testcase2","DIRECT","1234567891","IBM","999-999-9999","Tech Park One","","MAILING",
"Pune","India","Maharashtra","MH","411006","Prakash","Prem","premprakash@in.ibm.com",
"Mr","EN_US","999-9999", 

"Testcase3","DIRECT","1234567892","IBM","999-999-9999","550, Kings Street","","MAILING",
"Littleton","United States","MA","MA","1460-1250","Tuohy","Tom","tuohy@us.ibm.com","Mr",
"EN_US","999-9999",

下载转换脚本

将 JSON 有效负载转换成 J​​SON 模板文件,将数据转换成一个 CSV 文件,这些实现细节可能很繁琐。您需要为每个 “属性-值” 对定义一个引用变量,并将相应的数据值映射到 CSV 文件。为了简化这种耗时的任务,本文提供了一个样例 Windows 批处理文件,它为一个指定的 JSON 有效负载执行这些任务。请参阅 下载,获得该批处理脚本及其使用说明。

在 JMeter 处理 清单 5 中的 CSV 文件时,对于第一次迭代,会将 Pune 分配给 STR_CITY 变量,将 India 分配给 STR_COUNTRY 变量,等等。然后,对接下来的两行/迭代执行同样的操作,但使用了在那些行上指定的值。当脚本运行的时候,所指定的变量加载并包含它们的值,直到它们被覆盖或脚本结束。为了避免无意中覆盖了变量,请小心地命名变量。此外,当您调试 JMeter 脚本时,知道变量的起源在哪里非常重要,因为脚本本身没有定义该位置。在 CSV 文件中使用一致的变量名称命名惯例很有帮助。

图 4 显示了如何在 CSV Data Set Config 控制面板中指定 CSV 文件:

图 4. 在 JMeter 中指定和读取一个 CSV 文件
JMeter CSV Data Set Config 控制面板的屏幕截图,该面板用于指定和读取某个 CSV 数据文件

在图 4 中,Filename 字段被设置为将被读取的 CSV 文件的相对路径。“Allow quoted data?” 值被设置为 true,因为 CSV 文件中的数据值用引号括了起来。“Recycle on EOF?” 选项被设置为 false,“Stop thread on EOF?” 选项被设置为 true,这将导致线程在到达文件结束(EOF)时停止。


使用 While Controllers 实现循环

在将测试数据分离成单独的 CSV Data Set Config 文件后,可以更容易地使用数据集在一组相似但独立的操作中进行迭代。JMeter 提供了 While Controller 等循环构造函数来帮助实现迭代。通过在循环构造函数中组织测试操作,您可以在一个 JMeter 测试计划中执行所需的全部数据驱动的测试。

这种方法对于云测试很有用,因为它解决了测试从多个访问角色或不同数据集(正、负、边界值,等等)访问 API 的需求。利用循环,可以将一个 API 测试目标的所有测试排列都放在一个测试计划中,这使得编写测试用例的速度至少快了 20 倍。

图 5 显示了如何使用 While Controller 控制面板来告诉 JMeter 循环遍历所有 CSV 文件数据:

图 5. 循环遍历 CSV 文件中的所有数据
JMeter While Controller 控制面板的屏幕截图,该面板告诉 JMeter 循环遍历 CSV 文件中的所有数据

在图 5 中,Condition(函数或变量)字段被设置为 jexl() 函数。此函数比较了来自 CSV 数据文件的变量与 EOF,当 CSV 文件中没有其他数据行时,它会告诉 JMeter 退出循环。条件可以是最终计算结果为 false 的任何变量或函数。


使用 BeanShell 编写脚本

通过使用 BeanShell 脚本语言(请参阅 参考资料),您可以在 JMeter 脚本中进行 Java 编程。例如,在脚本必须操纵用 JMeter 的 Regular Expression Extractor 捕获的变量的情况下,BeanShell 脚本很有用。BeanShell 脚本执行传递给它的程序,并在运行时返回结果。

在某些云应用程序中,使用了从 API 命令返回的响应数据(可能是 JSON 格式的)作为对另一个 API 命令的请求数据,其中进行了一些修改。使用未知的动态数据可能非常棘手和困难,除非您可以在脚本中使用编程。利用 BeanShell 脚本,通过使用 JSONObject 类库(请参阅 参考资料),您可以在运行时操纵 JSON 有效负载或读取 JSON 有效负载的属性值。为了在 JMeter 脚本中使用 JSONObject,通过将 JAR 复制到 JMETER_HOME/lib 文件夹,可以在 JMeter 的类路径中包含 java json.jar(请参阅 参考资料,以获得下载链接)。

您可以针对不同目的,通过多种方式定义和使用 BeanShell 脚本。利用 BeanShell PreProcessor 或 PostProcessor,可以在采样器执行之前或之后应用 JMeter BeanShell Sampler 中的一段代码。利用 BeanShell Assertion,可以进行条件测试,比如 JMeter 变量是否保存了一个预期值。

定义并使用 BeanShell 脚本的一般步骤是:

  1. 在 JMeter 中,创建一个 BeanShell Listener、一个 BeanShell PreProcessor、一个 BeanShell PostProcessor,或 BeanShell Sampler。
  2. 在 BeanShell 脚本中使用 vars.get("variable") 获得变量。
  3. 使用 Java 编程语言来处理 BeanShell 脚本。
  4. 在 BeanShell 脚本中,使用 vars.put("variable") 将处理后的变量放回指定的 JMeter 变量(这可以是一个现有变量或一个新变量)。

清单 6 中的样例 BeanShell 脚本修改了嵌套在 清单 3 中的 JSON 的第三层的 GivenNameWorkPhone 值:

清单 6. 使用 BeanShell 脚本修改两个 JSON 值
custPayload= vars.get("testResp");
org.json.JSONObject custJSON= new org.json.JSONObject(custPayload);

if (custJSON.has("Customer") && custJSON.get("Customer")!= null) {
   org.json.JSONObject contactJSON = custJSON.getJSONObject("Customer").getJSONObject(
   "Organization").getJSONObject("Contact");
   contactJSON.put("GivenName", "Shalini");
   contactJSON.put("MobilePhone", "9923406159");
}
vars.put("updatedCustPayload", custJSON.toString());

现在,请求可以在 API UPDATE 命令中使用 ${updatedCustPayload} 变量。

可以通过其他许多方式使用 BeanShell 脚本来操纵 JMeter 变量或 JSON 数据 — 比如执行算术运算、提取变量的值,或以一个特定变量的值替换另一个变量的值。总体而言,BeanShell 可用于执行 JMeter 不直接支持的任务。


使用 Module Controller 模块化可重用的片段

一个复杂的测试计划中包括许多变量和函数,这很常见。通常情况下,这些片段在其他测试计划中也被使用超过一次或两次。在这种情况下,可以将这些片段拆分为可以在其他地方重用的若干个子模块,以便减少维护工作量。然后,如果一个可重用的功能在未来需要进行修改,您只需要在某个地方修改它。

JMeter Module Controller 是一种机制,可以在运行时在目前的测试计划中替换测试计划片段。片段可以位于任何线程组或在 WorkBench 上。Module Controller 使用的任何片段都必须具有惟一的名称,因为该名称被用于在重新加载一个测试计划时找到目标控制器。

图 6 的示例显示了放置 Module Controller 的位置,以及在脚本中调用的模块:

图 6. Module Controller 的放置和它指向的模块
JMeter 测试计划的屏幕截图,在该计划中,Module Controller 是在 Customer Service 模块的 Login 和 Logout 例程之间创建的,并且控制器所指向的 Register Customer 模块是在 WorkBench 中创建的

在图 6 所示的测试计划中,Register Customer 被定义为一个单独的模块,被放置在 WorkBench 部分中。Module Controller (Module Controller - Register Customer) 被放置在 Login User 例程的后面和 Logout 的前面,指向 Register Customer 模块。在运行脚本时,在 Module Controller 的所在位置上,Module Controller 替代了测试计划中的 Register Customer 模块。

图 7 显示了如何在脚本中定义一个 Module Controller:

图 7. Module Controller 指向在 WorkBench 中定义的简单控制器
指向 WorkBench 中定义的简单控制器的 JMeter Module Controller 的屏幕截图

在图 7 中,从 Module To Run 字段的下拉列表选中 Register Customer 模块,该列表列出了所有可用模块。

当可重用的组件在同一个脚本(JMX 文件)中被重用时,可以使用 Module Controller。如果在其他脚本中也将使用可重用的片段,那么可以将片段移到一个单独的 JMX 文件中,并使用 Include Controller 调用它,在下一节中将会详细叙述该操作。


使用 Include Controller 包括可重用的 JMeter 文件

JMeter 的 Include Controller 提供了一个占位符,其中一个 JMX 文件( 脚本)可以调用另一个 JMeter 脚本( 脚本)。通过将脚本拆分为较小的脚本或模块/例程,并使用这些子例程构建立一个测试套件,可以在脚本中实现模块化,以增强可理解性和可重用性。使用 Include Controller 分离可重用的代码片段或先决条件(如 Login User 和 Register User),帮助更好地管理和维护脚本。

要使用 Include Controller,首先需要创建一个子 JMX 文件,其中包含一个可重用的例程(如 Login Admin User)。图 8 显示了一个示例子脚本,通过 Include Controller 可以将它包含在父脚本中:

图 8. 子 JMX 文件 (LoginUser.jmx)
子脚本 (LoginUser.jmx) 的屏幕截图

图 8 中的子脚本定义了一个测试计划,其中包含一个采样程序和一个 Login User 的 HTTP 请求。在 HTTP 请求中,协议、服务器名称和其他设置所使用的变量是在父脚本中定义的。

下一步是在父脚本中想要调用子脚本的地方添加一个 Include Controller,并将 Include Controller 指向子 JMX 文件的路径。图 9 显示了一个在父脚本中定义的 Include Controller:

图 9. 在父 JMX 文件中添加一个 Include Controller
父脚本的 Include Controller 的屏幕截图

在图 9 所示的 Include Controller 控制面板中,Filename 字段存储了子 JMX 文件(在本例中是 Login_User.jmx)的相对路径。

子 JMX 文件可以访问在 Include Controller 中可以访问的父 JMX 文件中定义的任何变量,并且父 JMX 文件可以使用在子 JMX 文件中定义的变量。


使用正则表达式

当 JSON 是 REST 操作的请求负载时,响应要么是 JSON 格式的,要么是字符串表示形式。在完成操作后,必须提取响应字符串、错误代码和错误消息字符串,以验证特性是否如预期般运作。您可以使用 JMeter 的正则表达式特性来提取变量中的响应数据。

图 10 显示了如何在 JMeter 的 Regular Expression Extractor 控制面板中创建一个简单的正则表达式提取器:

图 10. 简单的正则表达式提取器
JMeter 中的简单的正则表达式提取器的屏幕截图

在图 10 中,Reference Name (在本例中是 User)存储使用正则表达式语法提取的值。在 Regular Expression 字段中,您为针对响应运行的正则表达式提供了语法(在 图 11 中,该语法是 \"LoginName\":\"(.*?)\"),以提取特定数据。JSON 响应数据的常用正则表达式语法示例包括:

  • \s*(.+)\s* 用于提取整个响应字符串
  • \"LoginName\":\"(.*?)\" 用于提取 "LoginName":"abc@testmail.com"
  • \"CustomerId\":(\d+) 用于提取 "CustomerId":2000006

图 10 的 Template 字段中,$1$ 表示参考变量的分组。Default Value 字段用于在正则表达式不匹配的情况下为调试提供默认值。根据实践,只有在脚本阶段添加和测试正则表达式模式的时候才声明默认值。


扩展功能 JMeter 脚本以执行性能测试

理想情况下,任何应用程序的性能测试都包括两个场景:用户数量尖峰和系统负载的增加。如果现有的功能测试场景涵盖了基本的 REST 函数,那么很容易扩展和更新它,从而测试托管在云基础架构上的 REST 服务的性能。您可以修改应该被发送到每个服务器的请求的数量,对应用程序进行加载测试。您可以通过配置使用适当循环迭代的上升期 来控制请求的数量。

例如,假设您有一个测试计划,基于以下算法执行和验证一组简单的 REST 原则:

  1. 执行一个 HTTP POST 方法,以创建一个客户对象。
  2. 执行一个 HTTP GET 方法,以验证客户对象的创建。
  3. 执行一个 HTTP PUT 方法,以验证客户对象的修改。
  4. 执行一个 HTTP DELETE 方法,以验证客户对象已被删除。

您可以将简单的功能测试计划(最初为 RESTful API 功能测试场景而设计)调整为一个以云环境中部署的服务器为目标的性能测试脚本。

  1. 导航到测试计划的 Thread Group 控制面板,如图 11 所示:
    图 11. 图 11. 性能仿真设置示例
    JMeter Thread Group 控制面板的屏幕截图,显示了性能仿真设置
  2. 在 Number of Threads (users) 字段中输入 ${Thread}Thread 变量的值(将在步骤 5 中配置它)设置了可用线程的数量,以反映有多少虚拟用户在访问服务器。
  3. 在 Ramp-Up Period (in seconds) 字段中输入 ${RampUp}RampUp 变量的值(将在步骤 5 中配置它)确定了分发请求的速度(这基于在步骤 1 中定义的线程数量)。例如,如果有 10 个线程,加速上升时间为 100 秒,那么每个线程在前一个线程启动后 10 秒开始,有 100 秒的总时间可以让测试加快速度。
  4. 在 Loop Count 字段中输入 ${Loop}Loop 变量的值(将在步骤 5 中配置它)确定了测试计划执行的次数。
  5. 导航到 User Defined Variables 控制面板,并将 ThreadRampUpLoop 定义为全局变量,如图 12 所示。为每个要模拟的负载分配适当的变量值。
    图 12. 性能测试模拟变量
    User Defined Variables 控制面板的屏幕截图,显示了样例性能测试仿真变量

    在图 12 中,Loop 值被设置为 5RampUp 值被设置为 50Loop 值被设置为 2

图 13 显示了基于图 12 中已配置的变量值,对在云应用程序中部署的其中一个节点服务器执行 JMeter 的结果:

图 13. 性能测试结果
屏幕截图显示了对已在云应用程序中部署的某个节点服务器运行 JMeter 测试的结果,基于图 12 中定义的配置变量

图 13 显示了发送到服务器的并发请求的沉重负载,以模拟性能测试条件。


扩展功能 JMeter 脚本,以执行可靠性测试

可靠性测试 是通过在多组特定条件下连续运行一组脚本,确保整体系统稳定性的一种方式。可靠性测试的结果应该与压力测试、功能测试和网络特性测试的结果一起查看。

与性能测试一样,您可以将执行和验证一组简单的 REST 原则的测试计划转换为一组合适的可靠性测试脚本,以确定云实例上运行的任何产品或特性的稳定性。

可靠性测试的一个重要方面是,确定在连续多日运行一个脚本的过程中是否发生故障。选中 Thread Group 控制面板的 Forever 复选框,如图 14 所示,让线程可以一直运行,直到测试失败或脚本被强行停止:

图 14. JMeter 脚本的可靠性设置
JMeter Thread Group 控制面板的屏幕截图,包含已为可靠性测试选中的 Forever 复选框

在配置了可靠性测试设置后,运行测试几天,通过 JMeter 结果图或通过 JMeter 提供的推断选项连续监测结果。


结束语

本文向您介绍了使用 JMeter 来有效地测试基于云的应用程序的方法。文章并没有进行详尽的讨论,我们鼓励您尝试使用其他技术来改进 JMeter 中的自动化任务实现。JMeter Wiki(参阅 参考资料)是继续您的探索的一个好地方。


下载

描述名字大小
转换 JSON 有效负载的批处理文件Conversion_Script.zip2KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Cloud computing, Java technology
ArticleID=957456
ArticleTitle=使用 Apache JMeter 测试基于云的应用程序
publish-date=12192013