RFT與Spring結合進行自動化腳本開發之一
Spring具有強大的功能:Ioc,可以實現在xml中把一個測試對象注入到另一個測試對象中,再加上可以利用RFT的find方法可以把spring的配置文件配置成一張RFT測試對象地圖。目前,大部分自動化腳本開發者利用RFT的測試對象地圖管理測試腳本的測試對象,RFT的測試對象地圖有兩種類型:公有測試對象地圖和私有測試對象地圖。
(1)大部分腳本采用的都是私有測試對象地圖,如果被測試的程序頂層對象發生變化,這樣每個腳本關聯的測試對象地圖都要進行修改,之所以這樣,有一個原因:在RFT中測試對象地圖不能夠繼承(僅僅可以合并),如果兩個測試對象地圖之間可以進行繼承,這樣把公用的測試對象放在父測試對象地圖中,其他的測試對象地圖繼承這個公用的測試對象地圖(同時繼承公有的測試對象),如果被測試程序頂層框架發生變化,僅僅只修改父測試對象地圖就可以了,但是目前RFT中不能實現(據我了解)。
(2)RFT中還有一種類型的測試對象地圖是公有的測試對象地圖,公有的測試對象地圖可以實現公用的測試對象重復使用,如果測試對象是一模一樣的,RFT在公有的測試對象地圖中只保留此測試對象的一個實例,但是把多個測試對象都插入到一個測試對象地圖中,同樣又面臨著難以管理的困難。
(3)另外,RFT中的測試對象地圖與腳本緊緊的耦合,如果程序即使發生很小的變動,也要更新測試對象地圖,修改腳本等等。
(4)一個spring配置文件可以被另一個spring文件import進來。這樣可以把公有的測試對象放在一個spring配置文件中,然后有其他的spring配置文件進行導入,可以實現測試對象地圖(spring的配置文件)的繼承。另外,在spring的配置文件中,可以注入一個對象的屬性,這樣可以人工的更改這個測試對象的父框架,另外可以可以注入測試對象的識別屬性,這樣可以進行人為的進行測試對象識別屬性的更改(這樣可以更好的應對被測試程序的變化)。
例如:
在腳本中引用測試對象通過如下語句:
(5)采用spring管理測試對象,也不是說一勞永逸的,如果被測試程序發生變化,避免不了的也要修改spring配置文件。但是,至少比RFT管理測試對象地圖的修改量要小的多。
(6)采用spring管理測試對象也有以下缺點:
(1)需要測試腳本開發者有比較高的編程技能(熟悉springIoc基本配置和對RFT API比較熟悉)
(2)被測試程序結構規范,最好每個HTML元素都有其名字,還有每次修改程序,如果HTML元素名稱能不修改最后就不修改(RFT可以通過組件名稱查找對象)還有就是按鈕上面的文本,采用RFT管理測試對象地圖也有此要求。
(3)要求對被測程序的組件結構有充分了解(可以通過RFT測試地圖了解)
(4)開發采用spring管理測試對象地圖的腳本比開發有RFT管理的測試對象地圖時間要長的多,但是帶來的好處也是明顯的。
(5)測試對象執行動作的速度也沒有RFT管理的測試對象地圖快。
base-test-object-map.xml 可以作為公有的測試對象地圖,其中customEditorConfigurer向容器注冊了一個屬性編輯,pkg.BaseTestObjectScript是一個RFT腳本,此腳本有getBrowserTestObject()和documentTestObject(),可以在這個類中放入基本的測試對象,通過Spring將這些對象封裝為bean, 然后有其他的spring配置文件來import,這樣就實現了對象的繼承。pkg.BaseTestObjectScript的代碼如下:
java 代碼
pkg.TestObjectPropertyEditor代碼如下,其中如果用精確匹配就用=作為分隔符,如果是通過正則表達式匹配就通過:作為分隔符,然后在程序內部就會做正則表達式的轉換。
java 代碼
然后介紹一下其他的Spring配置文件通過導入另一個Spring配置文件實現,測試對象的繼承。如下,通過import另一個配置文件,這個spring配置文件中的對象就可以使用被導入的spring配置文件中的測試對象,從而可以實現測試對象的繼承。
下面是上面的配置文件中用到的pkg.TestObjectHelper類,這個類暴露出parentTestObject 和 objectProperties兩個屬性用spring來注入,從而確定從什么對象開始查找對象和以什么條件查找對象。通過getSonTestObject() 方法返回找到的測試對象(只允許返回一個對象,如果找到多個就會拋出異常),其中還有個getSonTestObjects()方法是為了返回多個測試對象而準備的。代碼如下:
(1)大部分腳本采用的都是私有測試對象地圖,如果被測試的程序頂層對象發生變化,這樣每個腳本關聯的測試對象地圖都要進行修改,之所以這樣,有一個原因:在RFT中測試對象地圖不能夠繼承(僅僅可以合并),如果兩個測試對象地圖之間可以進行繼承,這樣把公用的測試對象放在父測試對象地圖中,其他的測試對象地圖繼承這個公用的測試對象地圖(同時繼承公有的測試對象),如果被測試程序頂層框架發生變化,僅僅只修改父測試對象地圖就可以了,但是目前RFT中不能實現(據我了解)。
(2)RFT中還有一種類型的測試對象地圖是公有的測試對象地圖,公有的測試對象地圖可以實現公用的測試對象重復使用,如果測試對象是一模一樣的,RFT在公有的測試對象地圖中只保留此測試對象的一個實例,但是把多個測試對象都插入到一個測試對象地圖中,同樣又面臨著難以管理的困難。
(3)另外,RFT中的測試對象地圖與腳本緊緊的耦合,如果程序即使發生很小的變動,也要更新測試對象地圖,修改腳本等等。
(4)一個spring配置文件可以被另一個spring文件import進來。這樣可以把公有的測試對象放在一個spring配置文件中,然后有其他的spring配置文件進行導入,可以實現測試對象地圖(spring的配置文件)的繼承。另外,在spring的配置文件中,可以注入一個對象的屬性,這樣可以人工的更改這個測試對象的父框架,另外可以可以注入測試對象的識別屬性,這樣可以進行人為的進行測試對象識別屬性的更改(這樣可以更好的應對被測試程序的變化)。
例如:
xml 代碼 <!--從classpath(類路徑)中導入base-test-object-map.xml文件 可以使用base-test-object-map.xml文件中定義的bean--> <!--導入時候要包含完整的包名--> <import resource="classpath:pkg/base-test-object-map.xml"/> <!--設置待查找對象的識別屬性 格式:propertyName-propertyValue--> <property name="objectProperties"> <list> <!--屬性名稱和屬性精確匹配用=--> <!--屬性名稱和屬性用正則表達式匹配用:--> <value>.class=Html.FORMvalue> 然后是怎么使用spring配置文件中的測試對象 <value>.name:.*Formvalue> list> property> <!--設置從什么對象開始查找 注入父測試對象--> <!--browserTestObject 引用的是base-test-object-map.xml文件中已經定義的bean--> <property name="parentTestObject"> <ref bean="browserTestObject"/> property> |
在腳本中引用測試對象通過如下語句:
java 代碼 TextGuiTestObject text_userName = new TextGuiTestObject((TestObject)context.getBean("userNameText")); text_userName.setText("system"); |
(5)采用spring管理測試對象,也不是說一勞永逸的,如果被測試程序發生變化,避免不了的也要修改spring配置文件。但是,至少比RFT管理測試對象地圖的修改量要小的多。
(6)采用spring管理測試對象也有以下缺點:
(1)需要測試腳本開發者有比較高的編程技能(熟悉springIoc基本配置和對RFT API比較熟悉)
(2)被測試程序結構規范,最好每個HTML元素都有其名字,還有每次修改程序,如果HTML元素名稱能不修改最后就不修改(RFT可以通過組件名稱查找對象)還有就是按鈕上面的文本,采用RFT管理測試對象地圖也有此要求。
(3)要求對被測程序的組件結構有充分了解(可以通過RFT測試地圖了解)
(4)開發采用spring管理測試對象地圖的腳本比開發有RFT管理的測試對象地圖時間要長的多,但是帶來的好處也是明顯的。
(5)測試對象執行動作的速度也沒有RFT管理的測試對象地圖快。
base-test-object-map.xml 代碼 xml 代碼 <!--sp-->xml version="1.0" encoding="gb2312"?> <!--CTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" </sp--> "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!--注冊com.rational.test.ft.script.Property屬性編輯器--> <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="com.rational.test.ft.script.Property"> <bean class="pkg.TestObjectPropertyEditor"/> entry> map> property> bean> <!--實例化pkg.BaseTestObjectScript類--> <bean id="baseTestObjectScript" class="pkg.BaseTestObjectScript"/> <!--調用baseTestObjectScript中的非靜態方法getBrowserTestObject產生一個BrowserTestObject測試對象--> <bean id="browserTestObject" factory-bean="baseTestObjectScript" factory-method="getBrowserTestObject" singleton="false"/> <!--調用baseTestObjectScript中的非靜態方法getDocumentTestObject產生一個DocumentTestObject測試對象--> <bean id="documentTestObject" factory-bean="baseTestObjectScript" factory-method="getDocumentTestObject" singleton="false"/> beans> |
base-test-object-map.xml 可以作為公有的測試對象地圖,其中customEditorConfigurer向容器注冊了一個屬性編輯,pkg.BaseTestObjectScript是一個RFT腳本,此腳本有getBrowserTestObject()和documentTestObject(),可以在這個類中放入基本的測試對象,通過Spring將這些對象封裝為bean, 然后有其他的spring配置文件來import,這樣就實現了對象的繼承。pkg.BaseTestObjectScript的代碼如下:
java 代碼
package pkg; import resources.pkg.BaseTestObjectScriptHelper; import com.rational.test.ft.*; import com.rational.test.ft.object.interfaces.*; import com.rational.test.ft.object.interfaces.siebel.*; import com.rational.test.ft.script.*; import com.rational.test.ft.value.*; import com.rational.test.ft.vp.*; public class BaseTestObjectScript extends BaseTestObjectScriptHelper { public void testMain(Object[] args) { } public BrowserTestObject getBrowserTestObject() { return browser_htmlBrowser(document_H(),DEFAULT_FLAGS); } public GuiTestObject getDocumentTestObject() { return document_H(); } } |
pkg.TestObjectPropertyEditor代碼如下,其中如果用精確匹配就用=作為分隔符,如果是通過正則表達式匹配就通過:作為分隔符,然后在程序內部就會做正則表達式的轉換。
java 代碼
package pkg; import com.rational.test.ft.script.Property; import java.beans.PropertyEditorSupport; import java.util.StringTokenizer; import com.rational.test.ft.value.RegularExpression; public class TestObjectPropertyEditor extends PropertyEditorSupport { public void setAsText(String text) { String delimiter = null; Object propValue = null; if(text == null || text.length() < 1 || (text.indexOf(EQUAL_MARK) == -1 && text.indexOf(COLON) == -1)) { throw new IllegalArgumentException("識別屬性為空或格式不正確 =表示進行精確匹配 :表示使用正則表達式匹配"); } if(text.indexOf(EQUAL_MARK) != -1) { delimiter = EQUAL_MARK; } else if(text.indexOf(COLON) != -1) { delimiter = COLON; } //解析字符串 StringTokenizer st = new StringTokenizer(text, delimiter); String name = st.nextToken(); String value = st.nextToken(); propValue = value; if(text.indexOf(COLON) != -1) //如果分隔符為 : 將propValue設置為正則表達式 { propValue = new RegularExpression(value, false); } setValue(new Property(name, propValue)); } public String getAsText() { Property property = (Property)getValue(); return property.getPropertyName() + "-" + property.getPropertyValue(); } public final String EQUAL_MARK = "="; //"=" 表示進行精確匹配 public final String COLON = ":"; //":" 表示使用正則表達式匹配 } |
然后介紹一下其他的Spring配置文件通過導入另一個Spring配置文件實現,測試對象的繼承。如下,通過import另一個配置文件,這個spring配置文件中的對象就可以使用被導入的spring配置文件中的測試對象,從而可以實現測試對象的繼承。
pkg/logon-map.xml xml 代碼 <!--sp-->xml version="1.0" encoding="gb2312"?> <!--CTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" </sp--> "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!--從classpath(類路徑)中導入base-test-object-map.xml文件 可以使用base-test-object-map.xml文件中定義的bean--> <!--導入時候要包含完整的包名--> <import resource="classpath:pkg/base-test-object-map.xml"/> <!--實例化pkg.TestObjectHelper類--> <bean id="loginFormHelper" class="pkg.TestObjectHelper" singleton="false"> <!--設置待查找對象的識別屬性 格式:propertyName-propertyValue--> <property name="objectProperties"> <list> <!--屬性名稱和屬性精確匹配用=--> <!--屬性名稱和屬性用正則表達式匹配用:--> <value>.class=Html.FORMvalue> <value>.name:.*Formvalue> list> property> <!--設置從什么對象開始查找 注入父測試對象--> <!--browserTestObject 引用的是base-test-object-map.xml文件中已經定義的bean--> <property name="parentTestObject"> <ref bean="browserTestObject"/> property> bean> <!--調用loginFormHelper中的getSonTestObject 此方法返回TestObject測試對象--> <bean id="loginForm" factory-bean="loginFormHelper" factory-method="getSonTestObject" singleton="false"> bean> <!--動態查找text_userName測試對象--> <bean id="userNameTextHelper" class="pkg.TestObjectHelper" singleton="false"> <property name="objectProperties"> <list> <value>.class=Html.INPUT.textvalue> <value>.name=userNamevalue> list> property> <property name="parentTestObject"> <ref bean="loginForm"/> property> bean> <!--動態查找text_password測試對象--> <bean id="userNameText" factory-bean="userNameTextHelper" factory-method="getSonTestObject" singleton="false"> bean> <bean id="passwordTextHelper" class="pkg.TestObjectHelper" singleton="false"> <property name="objectProperties"> <list> <value>.class=Html.INPUT.passwordvalue> <value>.name=passWordvalue> list> property> <property name="parentTestObject"> <ref bean="loginForm"/> property> bean> <bean id="passwordText" factory-bean="passwordTextHelper" factory-method="getSonTestObject" singleton="false"> bean> <!--動態查找ubmit_button測試對象--> <bean id="submitButtonHelper" class="pkg.TestObjectHelper" singleton="false"> <property name="objectProperties"> <list> <value>.class=Html.INPUT.submitvalue> <value>.value=進入value> list> property> <property name="parentTestObject"> <ref bean="loginForm"/> property> bean> <bean id="submitButton" factory-bean="submitButtonHelper" factory-method="getSonTestObject" singleton="false"> bean> <!--點擊菜單--> <bean id="menuHelper" class="pkg.MenuHelper" singleton="false"> <!--待點擊菜單名稱 必須按照先后順序--> <property name="menus"> <list> <value>菜單名稱1value> <value>菜單名稱2value> <value>菜單名稱3value> <value>菜單名稱4value> list> property> <!--注入父測試對象 定義從什么對象開始查找菜單并點擊--> <property name="parentTestObject"> <ref bean="documentTestObject"/> property> bean> beans> |
下面是上面的配置文件中用到的pkg.TestObjectHelper類,這個類暴露出parentTestObject 和 objectProperties兩個屬性用spring來注入,從而確定從什么對象開始查找對象和以什么條件查找對象。通過getSonTestObject() 方法返回找到的測試對象(只允許返回一個對象,如果找到多個就會拋出異常),其中還有個getSonTestObjects()方法是為了返回多個測試對象而準備的。代碼如下:
package pkg; import com.rational.test.ft.AmbiguousRecognitionException; import com.rational.test.ft.ObjectNotFoundException; import com.rational.test.ft.object.interfaces.TestObject; import com.rational.test.ft.script.Property; import com.rational.test.ft.script.SubitemFactory; public class TestObjectHelper { public Property[] getObjectProperties() { return objectProperties; } public void setObjectProperties(Property[] objectProperties) { this.objectProperties = objectProperties; } public TestObject getParentTestObject() { return parentTestObject; } public void setParentTestObject(TestObject parentTestObject) { this.parentTestObject = parentTestObject; } public TestObject getSonTestObject() { return findSonTestObject(); } public void setSonTestObject(TestObject sonTestObject) { this.sonTestObject = sonTestObject; } |