基于云的应用程序通常涉及几个组件,这些组件通过API进行交互,该API交换XML或JavaScript Object Notation(JSON)格式的数据。 本文介绍了使用Apache JMeter(基于GUI的开源测试应用程序)对使用RESTful Web API和JSON的支持云的应用程序执行功能,性能和可靠性测试的技术。
RESTful Web API使用HTTP和表示状态传输(REST)原理,该原理包括:
API的基本统一资源标识符(URI) API支持的数据,通常为JSON格式 API使用HTTP GET , PUT , POST和DELETE方法支持的一组操作了解JMeter作为RESTful API的测试工具的功能及其与测试基于云的应用程序的相关性。 由于多租户是重要的云功能,因此本文介绍了以编程方式合并数据分离和数据检索以确保数据完整性的技术。 它显示了如何编写有效的JMeter测试脚本(也称为测试计划或JMX文件),以促进维护,可重用性和模块化。 了解如何使用配置和属性文件来确保相同的脚本可以在多个环境中运行。
作者假设您熟悉JMeter UI并具有使用JMeter的丰富经验。
由于启用云的应用程序通常易于复制和部署,因此可以在多个环境中对其进行测试。 如果您需要在多个环境中测试和运行自动化脚本,则在JMeter中使用单独的属性文件来定义用于连接到资源(例如,应用程序服务器和数据库)的数据(包括登录凭证)是有益的。
JMeter属性和变量在JMETER_HOME / bin目录的三个文件中定义。 JMeter启动时,它将按以下顺序加载这些文件:
jmeter.properties 可选的用户定义属性文件 系统属性如果在JMeter运行时添加任何新属性或更改现有属性,则必须关闭JMeter并重新启动它以使更改生效。
jmeter.properties文件存储与JMeter应用程序本身有关的属性。 在此文件中仅保留JMeter特定于程序或框架的属性。 创建一个单独的文件(使用您选择的文件名)来存储特定于测试环境的属性和变量,这些属性和与被测应用程序关联的所有脚本都是全局变量-例如,管理员用户名/密码。 在jmeter.properties文件中,取消注释user.properties设置,并将user.properties值设置为您创建的文件的名称。 清单1中的示例将值设置为myuser.properties :
清单2中的示例用户属性文件显示了用于在用户属性文件中定义变量的格式。 (定义中等号左侧的任何位置都不允许有空格;必要时属性值可以包含空格。)
JMeter的第三个属性文件system.properties应该保留给必须为所有脚本定义的系统范围的属性。 例如,如果所有脚本都使用特定的数据库服务器,则可以在system.propterties文件中指定相关属性。
图1所示的JMeter用户定义变量控制面板显示了JMeter脚本如何读取用户属性文件中定义的属性。
控制面板的“值”列中的每个项目均采用以下格式:
${__property(VARIABLE_NAME,VARIABLE_NAME)}例如,用户属性文件中的USER_LOGIN变量在脚本中被读取为${__property(USER_LOGIN, USER_LOGIN)}函数。 括号中的第一个USER_LOGIN是在属性文件中定义的变量的名称(并在控制面板的“名称”列中列出)。 如果在属性文件中未定义变量,则第二个实例是默认值或后备值。
对于何时在属性文件中定义变量以及何时在JMeter脚本中将其定义为变量没有严格的规定。 但是有两个准则可以帮助您强加一致性,并减少跨多个JMeter脚本的变量定义的不必要重复:
如果您在多个脚本中始终使用相同的值,请在用户属性文件或system.properties文件中定义数据。 示例包括系统变量(例如数据库名称和服务器名称)以及执行范围的变量(例如日志记录级别)。 如果您的多个脚本使用的值可能会随脚本的不同而变化,请将其定义为脚本变量,或者在外部数据文件(例如,逗号分隔值(CSV)文件)中定义它。许多云API都需要JSON负载作为输入。 JSON定义了一组结构化的元素,可以嵌套在其他元素中。 每个元素定义一个或多个名称/值对。 功能测试包括重复提供指定格式的数据。 例如,在典型的REST API调用中,JSON有效负载在REST HTTP请求的主体中传递,并且通常包含硬编码数据。 硬编码的数据通常会在多个测试中重复进行,并分散到整个脚本中。
这种方法的一个普遍问题是,如果JSON结构(或数据)发生了变化(可能是因为API参数发生了变化),则必须进入JMeter测试,找到HTTP请求正文,并更改JSON结构(和数据)以使其匹配新的要求。 如果您在使用此JSON结构的多个JMeter测试用例中有成千上万个HTTP请求,则必须进行大量重复的编辑。
更好的方法是创建JSON结构的模板,并定义替换字符串以将数据存储在何处。 JSON模板不包含任何硬编码的数据值,而是定义在脚本运行时随实际数据加载的参考变量。 然后将该模板读入JMeter脚本中的变量中,并替换为HTTP请求正文中的变量。
清单3显示了定义JSON有效负载的传统方式:
清单4显示了在模板中定义JSON的动态方式:
清单3中的JSON实体仅包含硬编码数据。 相反,清单4中的模板仅包含引用变量名称,因此任何 JMeter测试计划都可以使用该模板。 实际数据分别存储在CSV数据文件中,这将在下一部分中讨论。
请注意, 清单4中的JSON为每个定义的替换变量调用JMeter __eval()函数。 此添加使JMeter脚本执行时可以在运行时评估变量。
图2和图3显示了如何在JMeter测试脚本中指定JSON实体模板文件。 图2显示了HTTP Request控制面板:
在图2的示例中, CUSTOMER_JSON变量表示整个JSON Customer元素。 包含在_eval()函数中的变量显示为HTTP请求正文(在“参数”选项卡上的“带有请求的发送参数”标题下。然后,在图2中,请求正文为${_eval(${CUSTOMER_JSON})} 。
CUSTOMER_JSON变量在“用户参数”控制面板中定义,如图3所示:
在图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目录之外,在这种情况下,您必须提供绝对路径。
尽管从JMeter测试计划中分离测试数据可能看起来像是额外的工作,但分离会产生更干净的测试,并且更易于管理。 当您需要对测试进行更改时,您会很快意识到好处。 每个测试计划中可能仍存在一些本地数据,但是大多数测试数据都被抽象到外部文件中-要么在属性文件中(如我们前面所述),要么在CSV数据配置文件中。 结果是测试套件更易于维护,并且在大多数情况下不需要编辑JMeter测试计划即可更改任何数据值。
JMeter使用CSV文件存储以逗号分隔的数据行。 JMeter框架的CSV数据集配置功能提供了一种在运行时动态读取CSV文件测试数据的方法。 将测试数据存储为CSV的另一个好处是,您可以存储多行数据,这些数据代表多个数据对象/变量或用于循环处理多次迭代的数据。 JMeter 2.3.4和更高版本还支持提供CSV 标头作为文件的第一行。 然后使用标头的定界字符串名称存储定义的数据值。 通常,每个后续行上的数据表示循环的迭代。 从这个意义上讲,脚本主要是“数据驱动的”,测试人员可以更改CSV文件中的数据,而无需更改JMX脚本。
数据也可以在多个测试线程之间共享,并且您可以将测试数据存储在多个CSV文件中。 例如,如果您的测试使用一组用户凭据登录,然后为该用户创建订单,则可以创建两个CSV文件-一个用于保存用户凭据,另一个用于保存订单信息。 有时,可能不止一个JMX脚本需要访问CSV文件。 在这种情况下,您必须将CSV文件放置在多个脚本可以访问的位置。
CSV文件中定义的迭代数据通常与变量名关联。 您在标头中定义这些变量名,用逗号分隔,它们与数据值的数量一对一匹配。 当JMeter处理文件的每一行时,它会将相应的数据值分配给该位置的关联变量名。 清单5显示了示例CSV文件内容:
将JSON有效负载转换为JSON模板文件并将数据转换为CSV文件的实现细节可能很繁琐。 您需要为每个属性值对定义一个参考变量,并使用相应的数据值映射到CSV文件。 为了减轻此耗时的任务,本文提供了一个示例Windows批处理文件,该文件针对指定的JSON有效负载执行这些任务。 请参阅下载以获取批处理脚本及其使用说明。
当JMeter处理清单5中的CSV文件时,对于第一次迭代,它将Pune分配给STR_CITY变量,将India分配给STR_COUNTRY变量,依此类推。 然后,它对接下来的两行/迭代执行相同的操作,但是使用在这两行/迭代中指定的值。 脚本运行时,指定的变量将加载并包含其值,直到它们被覆盖或脚本结束。 为避免意外覆盖变量,请仔细命名变量。 另外,调试JMeter脚本时,重要的是要知道变量的起源,因为脚本本身未定义位置。 在CSV文件中为变量名使用一致的命名约定将有所帮助。
图4显示了如何在“ CSV数据集配置”控制面板中指定CSV文件:
在图4中,“文件名”字段设置为要读取的相对CSV文件路径。 允许引用数据? value设置为true ,因为CSV文件中的数据值用引号引起来。 回收EOF? 选项设置为false ,并在EOF上停止线程? 设置为true ,这将导致线程在到达文件末尾(EOF)时停止。
将测试数据分为单独的CSV数据集配置文件后,使用数据集通过一系列相似但独立的操作进行迭代要容易得多。 JMeter包括诸如While Controller之类的循环结构,以帮助进行迭代。 通过在循环结构下组织测试动作,您可以从一个JMeter测试计划中执行所有必需的数据驱动的测试。
这种方法对云测试很有用,因为它满足了测试从多个访问角色或不同数据集(正值,负值,边界值等)对API的访问的需求。 通过循环,您可以将单个API测试目标的所有测试排列放入单个测试计划中,这使测试用例的编写速度至少快20倍。
图5显示了如何使用While Controller控制面板告诉JMeter遍历CSV文件数据:
在图5中,“条件(函数或变量)”字段设置为jexl()函数。 此函数将CSV数据文件中的变量与EOF进行比较,并在CSV文件中没有数据行时告诉JMeter退出循环。 条件可以是最终求值为false任何变量或函数。
您可以使用BeanShell脚本语言将Java编程包含在JMeter脚本中(请参阅参考资料 )。 BeanShell脚本很有用,例如,当脚本必须处理使用JMeter的正则表达式提取器捕获的变量时。 BeanShell脚本执行传递给它的程序,并在运行时返回结果。
在某些云应用程序中,将从API命令返回的响应数据(可能是JSON格式)用作对另一API命令的请求数据,并进行一些修改。 除非您可以在脚本中使用编程,否则处理未知的动态数据可能会非常棘手且困难。 随着BeanShell的脚本,你可以操纵JSON有效载荷或通过利用的在运行时读取一个JSON有效载荷的属性值JSONObject类库(参见相关主题 )。 要使用JSONObject在你的JMeter的脚本,包括Java的json.jar(见相关主题的下载链接)在JMeter的classpath中通过复制JAR到JMETER_HOME / lib文件夹。
您可以通过多种方式针对不同目的定义和使用BeanShell脚本。 使用BeanShell PreProcessor或PostProcessor,可以在执行采样器之前或之后在JMeter BeanShell采样器中应用一段代码。 使用BeanShell断言,您可以测试条件,例如JMeter变量是否持有期望值。
定义和使用BeanShell脚本的一般步骤是:
在JMeter中,创建BeanShell侦听器,BeanShell预处理器,BeanShell后处理器或BeanShell采样器。 通过在BeanShell脚本中使用vars.get(" variable ")获取变量。 使用Java编程语言来处理BeanShell脚本。 通过在BeanShell脚本中使用vars.put(" variable ") ,将处理后的变量放回指定的JMeter变量(可以是现有变量,也可以是新vars.put(" variable ") 。清单6中的示例BeanShell脚本修改了清单3中嵌套在JSON的第三层的GivenName和WorkPhone值:
现在,该请求可以在API UPDATE命令中使用${updatedCustPayload}变量。
BeanShell脚本可以通过许多其他方式来操纵JMeter变量或JSON数据-例如执行算术运算,获取变量的值或将特定变量的值替换为另一个。 总体而言,BeanShell对于执行JMeter不直接支持的任务很有用。
一个复杂的测试计划中包含许多变量和函数并不罕见。 通常,这些段在其他测试计划中也使用了不止一次或两次。 在这种情况下,您可以通过将这些段分成可在其他地方重用的子模块来减少维护工作。 然后,如果该可重用功能在将来需要进行任何更改,则只需在一个地方进行修改。
JMeter模块控制器是一种在运行时将测试计划片段替换为当前测试计划的机制。 该片段可以位于任何线程组中或在工作台上。 模块控制器使用的任何片段都必须具有唯一的名称,因为重新加载测试计划时,该名称用于查找目标控制器。
图6显示了在脚本中放置模块控制器和被调用模块的位置的示例:
在图6所示的测试计划中,Register Customer被定义为一个单独的模块,并放置在WorkBench部分中。 在“登录用户”例程之后和“注销”例程之前放置的模块控制器(模块控制器-注册客户)指向“注册客户”模块。 当脚本运行时,模块控制器将在模块计划所在位置的测试计划中替换“注册客户”模块。
图7显示了如何在脚本中定义模块控制器:
在图7中,从“要运行的模块”字段的下拉列表中选择“注册客户”模块,该列表列出了所有可用模块。
当可重用组件在同一脚本(JMX文件)中重用时,请使用模块控制器。 如果可重用片段也将在其他脚本中使用,请将片段移动到单独的JMX文件中,然后使用“包含控制器”对其进行调用,如下一节所述。
JMeter的Include Controller提供了一个占位符,JMX文件( 父脚本)可以在其中调用另一个JMeter脚本( 子脚本)。 通过将脚本分解为小的脚本或模块/例程,并使用这些子例程构建测试套件,可以在脚本中实现模块化,从而增强可理解性和可重用性。 使用“包含控制器”可以分隔可重复使用的代码段或前提条件,例如“登录用户”和“注册用户”,以帮助更好地管理和维护脚本。
要使用“包含控制器”,请首先创建一个包含可重用例程(例如Login Admin User)的子JMX文件。 图8显示了一个示例子脚本,可以通过Include Controller将其包含在父脚本中:
图8中的子脚本定义了一个测试计划,其中包含一个带有对登录用户的HTTP请求的采样器。 在HTTP请求中,用于协议,服务器名称和其他设置的变量在父脚本中定义。
下一步是在要调用子脚本的位置,在父脚本中添加一个“包含控制器”,并将“包含控制器”指向子JMX文件的路径。 图9显示了在父脚本中定义的一个Include Controller:
在图9所示的“包括控制器”控制面板中,“文件名”字段存储子JMX文件(在此示例中为Login_User.jmx)的相对路径。
子JMX文件可以访问可由Include Controller访问的父项中定义的任何变量,并且父JMX文件可以使用子JMX文件中定义的变量。
当JSON是REST操作的请求有效负载时,响应可以是JSON格式,也可以是字符串表示形式。 操作完成后,必须提取响应字符串,错误代码和错误消息字符串,以验证功能是否按预期工作。 您可以使用JMeter的正则表达式功能提取变量中的响应数据。
图10显示了如何在JMeter的Regular Expression Extractor控制面板中创建一个简单的正则表达式提取器:
在图10中,引用名称(在此示例中为User )存储使用正则表达式语法提取的值。 在“正则表达式”字段中,提供正则表达式的语法( 图11中的 \"LoginName\":\"(.*?)\" ),以响应以提取特定数据。 JSON响应数据的常用正则表达式语法示例包括:
\s*(.+)\s*提取整个响应字符串 \"LoginName\":\"(.*?)\"提取“ LoginName": "abc@testmail.com" \"CustomerId\":(\d+)提取"CustomerId": 2000006在图10的Template字段中, $1$表示参考变量的分组。 如果正则表达式不匹配,则默认值字段用于提供默认值,用于调试目的。 请遵循在添加和测试正则表达式模式时仅在脚本编写阶段声明默认值的做法。
理想情况下,针对任何应用程序的性能测试都涉及两种情况:用户数量激增和系统负载增加。 如果您有一个涵盖基本REST功能的现有功能测试方案,则可以轻松地对其进行扩展和更新以测试云基础架构上托管的REST服务的性能。 您可以通过修改应发送到各个服务器的请求数来对应用程序进行负载测试。 而且,您可以通过配置使用适当循环迭代的加速周期来控制请求数。
例如,假设您有一个测试计划,该计划基于以下算法执行并验证一组简单的REST原理:
执行HTTP POST方法以创建客户对象。 执行HTTP GET方法以验证客户对象的创建。 执行HTTP PUT方法以验证对客户对象的修改。 执行HTTP DELETE方法以验证对象已删除。您可以将简单的功能测试计划(最初是为RESTful API功能测试方案设计的)调整为性能测试脚本,该脚本可以访问部署在云环境中的服务器。
导航到测试计划的“线程组”控制面板,如图11所示:在图12中, Loop值设置为5 , RampUp值设置为50 , Loop值设置为2 。
图13显示了基于图12中配置的变量值对部署在云应用程序中的节点服务器之一执行JMeter的结果:
图13显示了大量并发请求,这些并发请求发送到服务器以模拟性能测试条件。
可靠性测试是通过在特定条件下连续运行一组脚本来确保整体系统稳定性的一种方法。 可靠性测试的结果应与压力测试,功能测试和网络功能测试的结果一起查看。
与性能测试一样,您可以将执行并验证一组简单的REST原理的测试计划转换为一组合适的可靠性测试脚本,这些脚本确定在云实例上运行的任何产品或功能的稳定性。
可靠性测试的重要方面是确定连续运行脚本许多天时是否发生故障。 选中线程组控制面板的Forever复选框,如图14所示,以使线程能够运行直到测试失败或脚本被强制停止:
配置可靠性测试设置后,运行测试几天,并通过JMeter结果图或JMeter提供的断言选项连续监视结果。
本文向您展示了如何使用JMeter有效地测试基于云的应用程序。 本文决不是详尽的讨论,我们鼓励您研究其他技术来改进JMeter中自动化任务的实现。 JMeter Wiki(请参阅参考资料 )是继续您的探索的好地方。
翻译自: https://www.ibm.com/developerworks/cloud/library/cl-jmeter-restful/index.html