剛剛以SCRUM的方式結束了一個的ASP.NET網站的測試的第一個Spring,因為團隊從無到有實現自動化測試系統,有必要把這次的經驗和教訓總結一下,以便后續的Spring可以獲取一些有意義的借鑒。
因為是一個技術博客,所以使用SCRUM管理這個測試項目的經驗放在別的地方分享,這系列的文章分享一下使用VSTT和Selenium結合實現自動化測試系統的經驗。
Selenium簡介
Selenium主要是一個錄制并回放的自動化測試用例編制工具,由一個錄制工具Selenium IDE(一個Firefox插件,當然這個工具也可以回放啦),一個回放工具Selenium Remote Control在其他機器和其他操作系統上進行回放。Selenium的一個好處就是你可以使用它測試所有操作系統下的所有主流瀏覽器,至于Linux下面的konqueror和gnome下面自帶的瀏覽器,沒有試過Selenium是否支持,當然那個控制臺界面下的瀏覽器就更沒有試過啦。Selenium還有一個Selenium Grid,據說很強大,因為項目比較緊,就沒有花時間去看它。
至于Selenium各個工具的用法,它的官網上有詳細的文檔,如果文檔也沒說清楚的話,那就直接讀源代碼吧。
Selenium和VSTT的整合
Selenium可以根據錄制的步驟生成直接在NUnit中使用的C#代碼,這些代碼基本上都可以在VSTT中直接使用,就是一些屬性需要更改。例如[TestFixture]改成[TestClass],[Test]改成[TestMethod]之類的,改好以后,啟動Selenium-RC,就可以直接在VSTT里面當作普通的單元測試用例執行了。
Selenium代碼優化
既然要做自動化測試,那么有一點是必須要時刻考慮的,就是在產品開發過程中,程序界面甚至是內部的類庫接口也是時刻改變的。而Selenium只能記錄當時錄制測試用例的界面情況,因此需需要將它生成的代碼分解一下,以面向對象的方式來重寫。例如下面這段代碼的目的是測試用戶可以查看自己的博客:
[TestMethod]
public void TheTestTest()
{
selenium.Open("/");
selenium.Click("link=登錄");
selenium.WaitForPageToLoad("30000");
selenium.Type("tbUserName", "donjuan");
selenium.Type("tbPassword", "");
selenium.Click("btnLogin");
selenium.WaitForPageToLoad("30000");
selenium.Click("link=donjuan");
selenium.WaitForPageToLoad("30000");
selenium.Click("link=博客");
selenium.WaitForPageToLoad("30000");
}
但是網頁頁面布局,或者Html控件的Id、文本等內容隨時都會被程序員修改,修改的原因有多種,例如修復新的錯誤(Bug),或者僅僅就是代碼重構。因此作為測試團隊,不能總是認為網頁的內容一成不變的。而象登錄這種操作,大部分測試用例都會用到,所以最好只要為登錄動作創建唯一的代碼 。有多個方案:
1. 為登錄創建一個獨立的測試用例,本來登錄這個功能就是要測試的嘛,在編輯自動化測試用例列表的時候,把登錄用例放在最前面。
2. 為登錄動作創建一個單獨的函數,例如LogOn(),然后在其他測試用例當中(包括登錄的測試用例)調用這個函數,另外,因為可能會需要用到不同的 用戶,所以最好把用戶名和密碼等變量提取出來,變成LogOn(string username, string password)之類的函數。
兩個方案,顯然是第二個方案的彈性大,但是對于第一個方案,如果測試人員都是新手,且對代碼不熟悉的話,建議可以考慮。
于是我們的代碼就變成類似下面的代碼:
using System;
//
// 這個異常是故意創建出來,用來封裝所有在測試代碼中發生的錯誤
//
public class CaseErrorException : Exception
{
public CaseErrorException(string message)
: base()
{
}
public CaseErrorException(Exception inner)
: this(null, inner)
{
}
public CaseErrorException(string message, Exception inner)
: base(message == null ? "測試代碼錯誤,請修復測試代碼,查看InnerException屬性!" :
string.Format("測試代碼錯誤,請修復測試代碼,詳細錯誤信息:{0};或者查看InnerException屬性!", message),
inner)
{
}
}
public class UserOperationsHelper
{
public void LogOn(string username, string password)
{
// string.Empty留出來為測試目的服務
if (username == null)
throw new CaseErrorException(new ArgumentNullException("username"));
if (password == null)
throw new CaseErrorException(new ArgumentNullException("password"));
selenium.Open("/");
selenium.Click("link=登錄");
selenium.WaitForPageToLoad(Consts.TimeToWaitForPageLoad);
selenium.Type("tbUserName", username);
selenium.Type("tbPassword", password);
selenium.Click("btnLogin");
selenium.WaitForPageToLoad(Consts.TimeToWaitForPageLoad);
}
}
public static class Consts
{
// 將等待的時間提取成一個公開的函數,因為在今后大規模的測試
// 過程中,很多自動化測試用例不簡單地執行,會導致網站響應速度
原文轉自:http://www.anti-gravitydesign.com