TestListResourceBundle”,打印出“Home”。
● PropertyResourceBundle
继承ResourceBundle类,它不是抽象类,也不需要创建它的子类。与ListResourceBundle相同的是它也实现了ResourceBundle类的抽象函数getKeys()和handleGetObject(String key);不同的是,它是从属性文件(.properties)中读入属性对的。例如,
定义如下一组properties文件,并加入到classpath中: MResources.properties: s1=Home
MResources_zh_CN.properties s1=主页
下面是一个java类根据不同的locale从相应的Properties文件中取数据来显示: package oracle.cdc.sgt.unicode; import java.util.Locale;
import java.util.ResourceBundle;
public class TestPropertyResourceBundle { public static void main(String[] args) { ResourceBundle messages; Locale curloc;
if (args.length != 2) {
curloc=Locale.getDefault(); } else {
curloc = new Locale(args[0],args[1]); }
messages = ResourceBundle.getBundle(
\ curloc);
System.out.println(messages.getString(\ } }
运行的方式和结果同TestListResourceBundle一样。
留意一下TestListResourceBunlde和TestPropertyResourceBundle唯一不同的地方就是在调用getBundle函数的那个语句,按照我们上面所说的,完全可以统一写成:“oracle.cdc.sgt.unicode.MResources”,因为getBundle会缺省先找基础名称为MRseources的类,失败后再找基础名称为MResources的属性文件,在查找属性文件前它会自动把“.”转换为“/”。当然,如果通过存在基础名称为MResources的类和属性文件时,也可以通过直接使用“oracle/cdc.sgt/unicode/MResources”来略过查找基础名称为MResources的类。
当然,java程序的国际化设计并不只是这么简单,当涉及日期和时间显示等问题时,还可以利用java.text包以及java.util包中的TimeZone、SimpleTimeZone和Calendar等类进行辅助处理。我们就不在这里详细叙述了,您只需要记住一个
6
ResourceBundle的概念就可以了,本文的后续部分都是围绕着这个概念展开的。
2. WinRunner调研
WinRunner适合于测试那些有图形操作界面的组件。目前,我们手头可用的版本WinRunner7.5,启用Web和Java插件(plugin)。 让我们先从WinRunner的技术特点说起吧。
2.1 WinRunner的技术特征
由于本文不是专门介绍WinRunner的,所以只列举一些WinRunner的重要特征。注意:这里定义了一些非官方的术语,为的是便于您的理解。
● WinRunner将对象(object)分为两种:窗口(window)和子对象,任一个子对象
都隶属于一个窗口。
注意:窗口也是对象,如一个页面就是一个窗口。
● WinRunner通过一组属性来唯一的识别窗口,也就是说不能有所有属性值都相同的多
个窗口;同样的,WinRunner通过一组属性来唯一的识别同一个窗口下的子对象,也就是说,在同一个窗口下,不能有所有属性值都相同的多个子对象。
● 如果把对象的所有属性的集合称为对象的定义,WinRunner可以把对象的定义保存在
以下两个地方:
☆ 独立script的单独的扩展名为GUI的文件
简称为GUI文件,同时为每个object定义了一个绝对逻辑名,有了绝对逻辑名就一定有相对逻辑名。对于窗口来说,它的绝对逻辑名等于它的相对逻辑名;对于子对象,它的绝对逻辑名等于它隶属的窗口的绝对逻辑名后面加一个”.”再加上它的相对逻辑名。在script开始部分导入GUI文件,在后面部分中只需要写出对象的绝对逻辑名,就可以从GUI文件中获得这个对象的定义了。如:
\{
class: window,
MSW_class: html_frame,
html_name: \} {
ltree_state: open, list_open_data: close }
\Certificate Authority-Certificate Management\Search\{
class: object,
MSW_class: html_text_link,
7
html_name: \}
\Certificate Authority-Edit Policy Result: UniqueCertificateCo\{
class: window,
MSW_class: html_frame,
html_name: \} {
rtree_state: open, ltree_state: open, list_open_data: close }
☆ script本身
把object定义写在script是可以的。一种方法是象GUI文件那样在script的开始部分为对象定义一个绝对逻辑名,这样在script的后续部分就可以通过这个绝对逻辑名来访问对象的;另一种方法是不为对象定义绝对逻辑名,而是在每个要访问对象的地方,直接写该对象的定义。这两种在script中定义对象的方法我们都不推荐,第一种完全就是GUI文件在script中的实现,那么为什么不它放在GUI文件中统一管理呢,第二种虽然省略了导入GUI文件的一步,但是维护起来更麻烦了,如果对象的属性发生变化,那就要修改所有脚本中所有这样定义了该对象的地方。
也许我们还没有意识到在script中定义对象的好处,但是存在就是道理。如: #Gui Objects initialization
set_window(\\\\}\
list_select_item(\class: list, MSW_class: html_combobox, html_name: matchType}\\);
rc=global_web_obj_text_exists(access_info,\Settings\);
if(text!=\)
set_window(\ \\\ \¬ification&\
rc=global_web_obj_text_exists(text_object,\\Information\,\
set_window(\
8
\\\}\
2.2 WinRunner在全球化测试中的局限性
在1.1“全球化测试的内容”一节中我们知道,要在不同的Locale下测试软件的处理数据和显示数据的能力。在不同Locale下,WinRunner赖以识别对象的属性列中有的属性也可能不同,因此在不同的Locale下,同一个对象的定义也可能不同。如同一个窗口,在英文下的html_name属性值为 “OracleAS Certificate Authority-Certificate Management”,在简体中文下的html_name属性值为 “OracleAS Certificate Authority-证书管理”。也就是如果用该窗口在英文下的定义是无法在简体中文或其他Locale下识别该窗口的。
换句话说,在一种Locale下录制的脚本,不论对象定义是保存在GUI文件中还是保存在script中,都无法直接拿到另一种Locale下直接运行。注意:所谓直接拿到,也包括进行少量的修改。
虽然可以在不同的Locale下用WinRunner录制各自的脚本,但是这并不是我们所希望的,那样做的成本是非常高的。
我们的目标是只在一种Locale下录制脚本,经过一定处理后,就可以在其他Locale下使用,即Code Once Fit All Language(简称COFAL)。
2.3 WinRunner满足COFAL的技术可行性
既然WinRunner是通过对象的定义(一组属性)来标识对象的,那么我们就要研究对象的属性在不同的Locale下有什么不同:
● 有的对象在不同的Locale下所有属性值都不变 ● 有的对象在不同的Locale下部分属性发生变化 只要找到变化的属性的规律和属性值的来源,并用自动化的方法来修改这些属性,就可以基本上满足只录制一次的需求。
1、如果对象的定义保存在GUI文件中
假如在英文下录制了一套脚本,该套脚本公用一个GUI文件global.gui,我们要找到一个自动化的方法,生成该GUI文件在其他Locale下对应的GUI文件,如global_zh_CN.gui和global_fr.gui等。这样,在不同Locale下,通过使用不同的GUI文件就可以用同一套脚本运行了。这样看来虽然有多个GUI文件,但是脚本只有一套,其他的GUI文件又是自动生成的,基本上满足了COFAL的要求。 2、如果对象的定义保存在script中
假如在英文下录制了一套脚本,脚本都保存在tina目录下,对象的定义都保存在script中。我们要找到一个自动化的方法,转化该script中对象的定义到不同的Locale下的定义,并把转化的结果保存在新的目录下,如tina_zh_CN和tina_fr目录等。这样,在不同的Locale下,通过使用不同目录下的脚本就可以了。这样看来虽然有多套脚本,但是只录制了一次,其他的都是自动生成的,也基本上满足了COFAL的要求。
9
下面我们会把转化对象定义统一称为“翻译”,接下来要介绍的就是以COFAL命名的一个实现自动化翻译的小工具。
3. 自动翻译工具COFAL简介
CORAL是Code Once Fit All Language的缩写,它是专门为配合WinRunner的全球化测试而开发一个工具,code once fit all language的意思是只需要在一种语言下编写脚本,就可以在所有语言下运行。用COFAL来实现script和GUI文件的自动翻译。
3.1 技术原理
3.1.1 Java应用程序级数据翻译
Oracle AS是一个基于J2EE架构的应用程序,它是通过我们在1.2节中介绍的java数据绑定机制来实现国际化的,也就是说那些需要翻译的属性值其实都是保存在ResourceBundle中。除了前面说过的ListResourceBundle和PropertyResourceBundle外,Oracle AS还把部分ResourceBundle保存在数据库的表中,在运行时根据不同的语言环境用绑定的key动态的从表中检索出对应的值。
由此可知,程序本身是通过关键字(key)结合指定的locale来获得值(value)的;而现在是想在已知值(value)和指定的locale的情况下,获得该key在其他locale下的值(value),这是一个逆向的过程。
为了描述方便,我们用key代表关键字,用prevalue代表当前可得的值,用postvalue代表翻译后的值。
自动翻译的原理是:
(1) 事先定位好resourcebundle的保存位置。
(2) 翻译时,从script或GUI文件中提取出prevalue,然后用prevalue在
ResourceBundle中查询出key,再用key从ResourceBundle中查询出postvalue。
(3) 用postvalue替换prevlaue,把替换的结果保存成新的语言版本。
在COFAL中,我们把Oracle AS用到的ResourceBundle统一保存在一张Oracle数据库表GLOBALRES中。翻译时通过JDBC,用prevalue从表中select出key,再用key去select出postvalue。
GLOBALRES表的结构如下: 列 Component Version
类型 varchar2(60) varchar2(60) 描述 Key所属的组件 组件的版本 10