在 Rational Performance Tester 中使用自定义代码获取数据库动态数据的方法
 

2010-02-22 作者:张雪莹,姜世锋,陈元 来源:ibm

 
本文内容包括:
本文针对 Rational Performance Tester(RPT)中对动态数据无法通过数据池解决,带来了很大额外工作量的问题,提供一种在 RPT 中通过自定义代码来获取数据库中动态数据的方法。

Rational Performance Tester 模拟动态数据测试

在性能测试中,为了更好地模拟用户的真实使用场景,往往需要针对不同的测试脚本匹配相应的模拟数据。这些模拟数据通常可以分为两大类:一类是静态数据,这些数据在整个测试的过程中不会发生变化(如一个稳定系统中的用户名和用户角色);一类是动态数据,这些数据会随着脚本的执行而发生变化(如销售总额,库存数量这样的数据)。

在使用 Rational Performance Tester(以下简称 RPT)进行测试时,对于静态数据,可以通过创建数据池,并与脚本中数据作关联,以满足不同虚拟用户对数据的调用;对于动态数据,有些在脚本运行中先引用然后改变的数据虽然可以通过每次脚本运行前修改数据池解决,但是带来了很大的额外工作量,而那些在脚本运行过程中先改变然后被引用的数据通常无法用数据池的方法解决。在很多应用中,这些动态数据往往都存储在数据库里。本文提供一种在 RPT 中通过自定义代码来获取数据库中动态数据的方法。

应用示例

应用举例:一个应用系统中,在工作流的若干节点对数据记录项进行处理,根据不同的条件,流转至相应分支的下一个节点。所有的数据记录项内容及属性保存在数据库中,并以数据记录项的记录号作为主键。具体数据记录处理过程参见图 1。

图 1. 数据处理过程示例
数据处理过程示例

在 RPT 脚本中,对于不同节点的操作,同样以数据记录项的记录号作为过滤条件,执行业务逻辑。由于数据记录项在工作流中的流转性,每当脚本执行完之后,各个节点中的数据记录项都将发生变化。如果对数据记录项的记录号创建数据池,在每一次脚本执行之后,都要执行数据库查询,获取到各个节点当前的数据记录项记录号,然后对数据池文件进行逐个更新。这样加大了额外的工作量,同时在更新多个数据池文件的工作中也增大了出错的可能性。所以,通过如下步骤以自定义代码来获取数据库中各个节点的记录号(以一个 RPT 脚本为例)。

操作步骤

文中涉及的数据库以 DB2 为例,其他类型的数据库在更换相应的 jdbc 驱动后同样适用。

  1. 将 db2jcc.jar 和 db2jcc_license_cu.jar 加入到 Java 构建路径的库文件中(参见图 2)。这两个文件可以在数据库安装目录的 /sqllib/java 文件夹下找到。
图 2. 构建路径的库文件界面
构建路径的库文件界面
  1. 从 RPT 自定义代码中获取数据库数据的基本方法
    通过以下代码片段,RPT 脚本的一个虚拟用户可以从数据库中取得相应 SQL 语句所返回的数据。
public String exec(ITestExecutionServices tes, String args[]) {
	String item;
	ITestLogManager testlog = tes.getTestLogManager();
	testlog.reportMessage("Try to get DB2 item");
	try {
Class.forName("com.ibm.db2.jcc.DB2Driver").newInstance();

连接到 DB2 数据库

		String url = "jdbc:db2://ip:port/dbname"; 
		String user = "db2user"; 
		String password = "password"; 
		Connection conn = DriverManager.getConnection(url,user,password);

执行 SQL 语句,取得返回结果集的第一项(此处也可根据需求对返回结果集进行其他操作)

PreparedStatement ps = conn.prepareStatement( "SQL"); 
		ResultSet rs = ps.executeQuery(); 
		if (rs.next()) { 
			item = rs.getString("attribute");
		}
		conn.close();
	} catch(Exception e){
testlog.reportMessage(e.getMessage());
	}

	testlog.reportMessage("Got DB2 item: " + item); 	
return item;
}
  1. 当使用 RPT 进行性能测试时,对于多个执行同一脚本的虚拟用户来说,每个用户都会执行该脚本中的自定义代码。在某些情况下,数据库中的数据在脚本运行中会随之变化,因此每个虚拟用户执行上述代码并重新从数据库中取得数据是合理的。但在某些情况下,数据在测试进行过程中不会变化,因此实际上只要访问数据库一次就能取得本次脚本运行的所有虚拟用户所需要的数据集。当不同的虚拟用户在该数据集中需要选取不同的项时,还需要引入一种机制来保证这种唯一性。此外,在并发用户数比较大时,需要避免对同一数据集的争用。基于以上考虑,对这一类情况可以作以下改进:
     
    • 增加静态成员变量 isResultGot,用于标识数据集是否已被取得
    • 增加静态成员变量 itemList,用于存放数据集
    • 增加静态成员变量 pointer,用于标识当前所取的数据项的位置
    • 使用 synchronized 方法解决并发用户对 itemList 的争用

改进后的代码示例如下:

package test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import com.ibm.rational.test.lt.kernel.services.ITestExecutionServices;
import com.ibm.rational.test.lt.kernel.services.ITestLogManager;

public class GetItem implements
		com.ibm.rational.test.lt.kernel.custom.ICustomCode2 {
private static ArrayList itemList = new ArrayList(); // 用于存放数据集
private static boolean isResultGot = false; // 标识是否已取得数据集
private static int pointer = 0; // 数据集上的指针

	public GetItem() {
	}

	/**
	 * For javadoc of ICustomCode2 and ITestExecutionServices interfaces, 
	 * select 'Help Contents' in the Help menu and select 
	 * 'IBM Rational Performance Tester TES'.
	 */
	public String exec(ITestExecutionServices tes, String args[]) {
		ITestLogManager testlog = tes.getTestLogManager();

synchronized(itemList) { // 解决并发用户对 itemList 的争用
    if (isResultGot == false) { // 若数据集还未取得,则连接数据库并获取
		testlog.reportMessage("Try to get items");
				try
				{

 // 此处省略连接数据库并执行 SQL 语句的相同代码

…
// 将结果集写入 itemList
					while (rs.next())
					{ 
itemList.add(rs.getString( "attribute"));
					}
					conn.close();
					testlog.reportMessage("Items Got");
}catch(Exception e)
				{
testlog.reportMessage(e.getMessage());
				}

isResultGot = true; // 数据集已取得
			}
// 针对当前虚拟用户,从数据集中取下一项(根据需求可进行相应操作)

testlog.reportMessage("Get item at position: " + (pointer + 1));
String item = (String) itemList.get(pointer);

pointer=pointer++; // 对 pointer 的控制

			testlog.reportMessage("Got DB2 item: " + item); 
			return item;
		}
	}

}
  1. 最后,将脚本中需要作数据替换的地方与此段自定义代码作关联。

选择脚本中需要做数据替换的数据段,然后在鼠标右键菜单中选择“Substitute From”,在其子菜单中可以看到前文中所创建自定义代码的名称,点击即完成关联。

图 3. 与自定义代码关联
与自定义代码关联

通过如上的步骤,即可完成脚本中对数据记录项记录号的自动获取。在不需要人工干预的情况下,为每个虚拟用户分配相应的记录号数据,同时由于采用了 synchronized 的机制,消除了并发用户对数据项列表的争用,保证了每个虚拟用户所获取记录号的唯一性。考虑到执行自定义代码中的 SQL 对系统 CPU 资源的影响,这段自定义代码放在每个脚本的登录页之前,避免影响到脚本中其他页面的执行,从而不会对最后的性能测试结果产生影响。在实际项目中,使用此方法,避免了每次运行脚本前修改 24 个数据池文件,节省了近两个小时,提高了效率和准确率。

总结

通过自定义代码,可以有效地获取到数据库中的指定数据,这为动态数据在 RPT 测试中的应用提供了一种有效的方法,也使得性能测试的结果更加接近真实用户的实际使用场景。同时,也在一定程度上提高了性能测试的效率和准确率。

参考资料

学习
  • 访问 developerWorks 中国网站的 Jazz 技术空间,这里汇集了丰富的 Jazz 平台中文技术资源。 您可以通过这里了解更多关于 Jazz 平台和 Jazz 技术发展趋势的最新信息。
  • 访问 UML 资源中心,获得关于统一建模语言(Unified Modeling Language,UML)的入门知识、技术资源和最佳实践。
  • 访问 IBM developerWorks 中国网站 Rational 专区,获得关于 IBM Rational 软件交付平台(Rational Software Delivery Platform)产品的技术资源和最佳实践。
  • 阅读 Rational Edge 中文版,获取软件开发领域的最佳实践。
  • 订阅 IBM developerWorks 时事通讯,一份关于 developerWorks 指南、文章、下载、社区活动、网络广播和技术讲座的电子周刊。
  • 学习 Hello World 系列教程,这是学习 IBM 软件工具的快速通道。在每一篇教程中,都会有快速入门产品演示动画。您可以通过其中的动画演示快速浏览如何使用 IBM 软件完成开发任务。
获得产品和技术 讨论

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织