Selenium用戶指南 - 第七章 測試設計的考慮[2]

From: http://blog.csdn.net/planisnothing/article/details/7252959


封裝Selenium調用

正如任何編程,你會想要使用實用函數去處理,遍及你的測試的重複代碼。一種防止重複代碼的方式是,使用你自己設計的函數或類方法,封裝頻繁使用的調用。例如,許多測試在一個頁面上,會頻繁地點擊一個頁面元素,並等待頁面裝載。

selenium.click(elementLocator);
selenium.waitForPageToLoad(waitPeriod);

代替重複這段代碼,你可以編寫一個封裝器方法執行這兩個函數。

/**
* Clicks and Waits for page to load.
*
* param elementLocator
* param waitPeriod
*/
public void clickAndWait(String elementLocator, String waitPeriod) {
selenium.click(elementLocator);
selenium.waitForPageToLoad(waitPeriod);
}

元素呈現的“安全操作”

另外一個常用的封裝Selenium方法是,在執行某些操作前,檢查元素是否呈現。這有時被稱爲“安全操作”。例如,下面的方法可能被使用於實現一個安全操作,

/**
* Selenum-RC -- Clicks on element only if it is available on page.
*
* param elementLocator
*/
public void safeClick(String elementLocator) {
if(selenium.isElementPresent(elementLocator)) {
selenium.click(elementLocator);
} else {
// Using the TestNG API for logging
Reporter.log("Element: " +elementLocator+ ", is not available on page - "
+selenium.getLocation());
}
}

此示例使用Selenium 1 API,但Selenium 2 也支持。

/**
* Selenium-WebDriver -- Clicks on element only if it is available on page.
*
* param elementLocator
*/
public void safeClick(String elementLocator) {
WebElement webElement = getDriver().findElement(By.XXXX(elementLocator));
if(webElement != null) {
selenium.click(elementLocator);
} else {
// Using the TestNG API for logging
Reporter.log("Element: " +elementLocator+ ", is not available on page - "
+ getDriver().getUrl());
}
}

在第二個示例中"XXXX"僅僅是一個佔位符,代表可以被調用的多個方法中的一個。

使用安全方法取決於測試開發者的判斷。因此,如果測試執行需要繼續,甚至在頁面缺失元素的情況下,則可以使用安全方法,然而應該記錄一個關於缺失元素的消息到日誌。這基本上實現了一個帶有報告的驗證機制,而非一個退出的斷言。但如果元素爲了能夠執行進一步的操作,必須在頁面上是可得到的(例在一個門戶站點的登錄按鈕),那麼安全方法技術不應該被使用。

UI映射

一個UI映射是一個存儲測試集的所有定位器到一個地方,爲了當在一個AUT的UI元素的標識符或路徑改變時,方便修改的機制。測試腳本然後使用這個UI映射用來定位要測試的元素。基本上,一個UI映射是一個測試腳本對象的倉庫,對應於被測試應用程序的UI元素。

什麼使得UI映射有用?它的主要目的是使得測試腳本管理更容易。當一個定位器需要被編輯時,有一箇中心的位置用於容易地查找對象,而不是必須從頭至尾搜索測試腳本代碼。同樣,它允許在一個單一的位置改變標識符,而不是不得不在多個地方做出改變,在一個測試腳本中,或者在多個測試腳本中處理。

簡言之,一個UI映射有兩個重大的優點:

- 爲UI對象使用一箇中心位置,代替讓他們分散在腳本中。這使得腳本維護更有效。

- 含義模糊的HTML標識符和名稱可以給於更可讀的,改善測試腳本的可讀性的名稱。

考慮下面的,難以理解的示例(Java):

public void testNew() throws Exception {
selenium.open("http://www.test.com");
selenium.type("loginForm:tbUsername", "xxxxxxxx");
selenium.click("loginForm:btnLogin");
selenium.click("adminHomeForm:_activitynew");
selenium.waitForPageToLoad("30000");
selenium.click("addEditEventForm:_IDcancel");
selenium.waitForPageToLoad("30000");
selenium.click("adminHomeForm:_activityold");
selenium.waitForPageToLoad("30000");
}

對不熟悉AUT的頁面源代碼的任何人來說,這腳本可能是難以閱讀的。甚至是應用程序的正式用戶理解這樣的腳本在做什麼也有困難。一個更好的腳本可以是:

public void testNew() throws Exception {
selenium.open("http://www.test.com");
selenium.type(admin.username, "xxxxxxxx");
selenium.click(admin.loginbutton);
selenium.click(admin.events.createnewevent);
selenium.waitForPageToLoad("30000");
selenium.click(admin.events.cancel);
selenium.waitForPageToLoad("30000");
selenium.click(admin.events.viewoldevents);
selenium.waitForPageToLoad("30000");
}

現在,使用某些註釋和空白以及UI映射標識符做出一個非常可讀的腳本。


public void testNew() throws Exception {

// 打開應用程序URL

selenium.open("http://www.test.com");

// 提供管理用戶名稱

selenium.type(admin.username, "xxxxxxxx");

// 點擊一個登錄(Login)按鈕

selenium.click(admin.loginbutton);

// 點擊創建新事件(Create New Event)按鈕

selenium.click(admin.events.createnewevent);
selenium.waitForPageToLoad("30000");

// 點擊取消(Cancel )按鈕

selenium.click(admin.events.cancel);
selenium.waitForPageToLoad("30000");

// 點擊查看舊事件(View Old Events)按鈕

selenium.click(admin.events.viewoldevents);
selenium.waitForPageToLoad("30000");
}

一個UI映射可以被實現,使用各種各樣的方法。一個人可以創建一個類或結構,那個僅僅存儲公共的字符串變量,每一個存儲一個定位器。替代地,可以使用一個存儲鍵值對的文件。在Java一個包含鍵/值對的屬性文件可能是最佳的方法。

考慮一個屬性文件prop.properties,爲來自上一個示例的UI元素分配具有親和力的標識符別名。

admin.username = loginForm:tbUsername
admin.loginbutton = loginForm:btnLogin
admin.events.createnewevent = adminHomeForm:_activitynew
admin.events.cancel = addEditEventForm:_IDcancel
admin.events.viewoldevents = adminHomeForm:_activityold

定位器會仍然引用HTML對象,但你已經在測試腳本和UI元素之間引入了一個抽象層。值讀取自屬性文件,並且使用在測試類,以實現UI映射。參考下面的鏈接,以獲取關於Java屬性文件的更多信息。

頁面對象設計模式

頁面對象是一個,用於增強測試的可維護性和減少代碼重複,在測試自動化領域流行的設計模式。一個頁面對象是一個面向對象的類,作爲你的AUT頁面的接口。當測試需要與頁面的UI交互時,使用頁面對象類的方法。好處是,如果頁面UI改變,測試本身不需要改變,僅僅在頁面對象中的代碼需要改變。結果是,支持新UI的所有改變都位於同一個地方。

頁面對象設計模式提供了下面的優點。

1、有一個測試代碼和頁面特定代碼的清晰分離,出入定位器(或者定位器的惡使用,如果你使用UI映射)和佈局。

2、有一個單一的頁面提供的服務和操作的倉庫,而不是讓這些服務散佈在測試中。

在兩種情況下,這允許任何由於UI改變而需要的修改可以在一個地方做出。有關這項技術的有用信息可以在無數的blog中找到,因爲這個測試設計模式正得到廣泛地使用。我們鼓勵希望瞭解更多的讀者,在英特網上搜索有關這個主題的blog。很多人寫過這個設計模式,並且可以提供超越本用戶指南的有用技巧。爲讓你開始,我們使用一個簡單的示例演示一個頁面對象。

First, consider an example, typical of test automation, that does not use a page object.

首先,考慮一個示例,典型的測試自動化,沒有使用頁面對象。

/***
* Tests login feature
*/
public class Login {

public void testLogin() {
selenium.type("inputBox", "testUser");
selenium.type("password", "my supersecret password");
selenium.click("sign-in");
selenium.waitForPageToLoad("PageWaitPeriod");
Assert.assertTrue(selenium.isElementPresent("compose button"),
Login was unsuccessful");
}
}

使用這個方法有兩個問題。

1、在測試方法和AUT的定位器(在這個示例中是id)之間沒有分離;兩者糾纏在一個方法中。如果AUT的UI改變它的標識符,佈局,或者一個登錄如何輸入和繼續,這測試本身必須改變。

2、id定位器散佈在多個測試中,那些必須使用這個個登錄頁的所有測試中。

應用頁面對象技術,這個示例可以重寫,像下面的這個登錄頁的頁面對象示例。

/**

* 頁面對象封裝登錄頁

public class SignInPage {

private Selenium selenium;

public SignInPage(Selenium selenium) {
this.selenium = selenium;
if(!selenium.getTitle().equals("Sign in page")) {
throw new IllegalStateException("This is not sign in page, current page is: "
+selenium.getLocation());
}
}

/**
* Login as valid user
*
* @param userName
* @param password
* @return HomePage object
*/
public HomePage loginValidUser(String userName, String password) {
selenium.type("usernamefield", userName);
selenium.type("passwordfield", password);
selenium.click("sign-in");
selenium.waitForPageToLoad("waitPeriod");

return new HomePage(selenium);
}
}

一個Home頁面的頁面對象可能像這個。

/**
* Page Object encapsulates the Home Page
*/
public class HomePage {

private Selenium selenium;

public HomePage(Selenium selenium) {
if (!selenium.getTitle().equals("Home Page of logged in user")) {
throw new IllegalStateException("This is not Home Page of logged in user, current page" +
+selenium.getLocation());
}
}

public HomePage manageProfile() {
// Page encapsulation to manage profile functionality
return new HomePage(selenium);
}

/*More methods offering the services represented by Home Page
of Logged User. These methods in turn might return more Page Objects
for example click on Compose mail button could return ComposeMail class object*/

}

現在登錄測試可以使用這兩個頁面對象如下:

/***
* Tests login feature
*/
public class TestLogin {

public void testLogin() {
SignInPage signInPage = new SignInPage(selenium);
HomePage homePage = signInPage.loginValidUser("userName", "password");
Assert.assertTrue(selenium.isElementPresent("compose button"),
Login was unsuccessful");
}
}

在頁面對象可以如何設計方面,有大量的靈活性,但有幾個基本的規則,用於達成你的測試代碼想要的可維護性。頁面對象本身應該永遠不要做驗證和斷言。這是你的測試部分,並且應該總是在測試代碼中,永遠不要在一個頁面對象中。頁面對象將包含頁面的呈現,和頁面通過方法提供的服務,但在頁面對象中沒有代碼與即將被測試的內容相關。

有一個,唯一的,驗證,可以和應該被寫在頁面對象裏,那就是去驗證那個頁面,和可能的在頁面上的關鍵元素,應被正確地裝載。這個驗證應該在頁面對象實例化時完成。在上面的示例中,SignInPage和HomePage構造函數檢查預期的頁面是可得到的,而且已經準備好接受來自測試的請求。

一個頁面對象不一定需要代表一個完整的頁面。頁面對象設計模式可以使用於代表在一個頁面上的組件。如果在AUT的一個頁面上有多個組件,每一個組件有一個分離的頁面對象,這可以改善它的可維護性。

其他的設計模式也可以用於測試。有些人使用頁面工廠實例化他們的頁面對象。討論這些設計模式超出了本用戶指南的範疇。這裏,我們僅僅希望引入這些概念,讓讀者意識到某些可以做的事情。正如較早提及的,很多人發佈過有關這個主題的blog,我們鼓勵讀者查找有關這些主題的blog。

數據驅動的測試

數據驅動的測試指的是使用帶有變化的數據的相同的測試(或多個測試)多次。這些數據集常常來自外部文件,諸如.cvs文件,文本文件,或可能裝載自一個數據庫。數據驅動的測試是一個常用的數據測試自動化技術,使用於驗證面對許多變化的輸入的應用程序。當測試是爲變化的數據設計時,輸入數據可以擴展,本質地創建附加的測試,而無需改變測試代碼。

In Python:

這Phython腳本打開一個文本文件。該文件在每一行包含一個不同的搜索串。代碼然後保存這個到一個字符串數組,並迭代通過這個數組,執行一個搜索和斷言每個搜索串。

只是一個非常基本的示例,但這思想是去運行一個帶有變化的數據的測試,可以被容易的完成,使用編程或腳本語言。爲了更多的示例,參見Selenium RC wiki,從電子表格讀取數據或使用TestNG的數據提供者能力的示例。此外,這是一個在測試自動化專業人士中衆所周知的主題,包括那些不使用Selenium的,如此在Internet上可以找到許多有關這個“數據驅動測試”主題的blog

數據庫驗證

另外一個共同的測試類型是比較在UI中的數據與實際存儲在AUT數據庫中的數據。因爲你也可以從一個編程語言做數據庫查詢,假定你有數據庫支持函數,你可以使用它們去存取數據,然後使用這個數據去驗證AUT顯示的內容是正確的。

考慮這個取自數據庫的註冊郵件地址的示例,然後稍後與UI進行比較。一個建立一個DB連接,然後從DB存取數據的示例可能看起來像這樣。

In Java:

// 裝載Microsoft SQL Server JDBC driver。

Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

// 準備連接url

String url = "jdbc:sqlserver://192.168.1.180:1433;DatabaseName=TEST_DB";

// 得到數據庫連接

public static Connection con =
DriverManager.getConnection(url, "username", "password");

// 創建可以使用於編寫DDL和DML SQL語句的語句對象

public static Statement stmt = con.createStatement();

// 通過Statement.executeQuery方法發出SQL SELECT語句到數據庫

// 返回一個包含請求信息,以及帶有數據行的ResultSet對象


ResultSet result =  stmt.executeQuery
("select top 1 email_address from user_register_table");

// 從“result”對象存取“email_address”值

String emailaddress = result.getString("email_address");

// 使用emailAddress值登錄到應用程序

selenium.type("userID", emailaddress);
selenium.type("password", secretPassword);
selenium.click("loginButton");
selenium.waitForPageToLoad(timeOut);
Assert.assertTrue(selenium.isTextPresent("Welcome back" +emailaddress), "Unable to log in for user" +emailaddress)

這個簡單的,從一個數據庫存取數據的Java示例。


© Copyright 2008-2012, Selenium Project. Last updated on Feb 02, 2012.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章