無論哪一種自動化測試的驅動框架(基於B/S,桌面應用,還是手機App)。都應當具有一套優秀的元素定位技術。通常的自動化測試流程也可以簡單的歸結為是一個從被測試程序中識別或是定位元素以及執行操作和驗證元素的過程。這一篇我們就開始給大家介紹一下Selenium中是如何定位DOM元素的。本文將會介紹如下內容:
- Selenium DOM 主要的定位方式。
- Selenium 如何擴展元素定位方式。
- 輔助瀏覽器工具
(一)Selenium DOM主要定位方式
上一篇中,我們介紹了WebDriver 和 WebElement兩個類型。他們都具有方法FindElement和FindElements。 這便是Selenium WebDriver的API提供給我們的元素定位方式。IWebDriver要求同時實現接口ISearchContext,元素查找的方法便是在這個接口中定義的:
1 public interface IWebDriver : ISearchContext, IDisposable 2 { 3 //省略部分Code... ... 4 }
1 namespace OpenQA.Selenium 2 { 3 // Summary: 4 // Defines the interface used to search for elements. 5 public interface ISearchContext 6 { 7 IWebElement FindElement(By by); 8 9 ReadOnlyCollection<IWebElement> FindElements(By by); 10 } 11 }
他們都會接受一個By類型的參數。該類型的官網定義如下所示,Selenium主要提供了以下幾種方式定位元素:
- ID : 根據DOM元素的ID屬性定位。
- Name : 根據DOM元素的Name屬性定位。
- ClassName : 根據DOM元素的Class屬性定位。
- TagName : 根據DOM元素的TagName定位。
- LinkText : 根據DOM元素(link)的文本內容定位。
- PartialLinkText : 根據DOM元素(link)的部分文本內容定位。
- CssSelector:根據CSS 選擇器定位元素。
- XPath:使用XPath定位元素。
元素定位的調用方式如下:
1 IWebDriver driver = new FirefoxDriver (); 2 IWebElement element; 3 4 element = driver.FindElement(By.Id("ObjectID")); 5 element = driver.FindElement(By.Name("ObjectName")); 6 element = driver.FindElement(By.ClassName("ObjectClassName")); 7 element = driver.FindElement(By.TagName("ObjectTagText")); 8 element = driver.FindElement(By.LinkText("ObjectLinkText")); 9 element = driver.FindElement(By.PartialLinkText( "ObjectPartialLinkText")); 10 11 // Use css selector. 12 element = driver.FindElement(By.CssSelector( "CssSelector For Object")); 13 // Use xPath. 14 element = driver.FindElement(By.XPath("XPath For Object"));
下面簡單的介紹一下DOM元素的ID,Name,ClassName... ...XPath,CssSelector都是些什么鬼? 當然,如果你是一個已經很熟悉這些概念的小伙伴就可以直接忽略這部分內容。關於DOM元素的基本概念和知識這里我就不詳細的描述了,有興趣的小伙伴可以到這個鏈接學習一下:http://www.runoob.com/html/html-tutorial.html。
簡單的說基本的HTML元素(DOM元素)構成了網頁的內容,每個元素都是以一個個DOM標簽的形式表現出來的。下面是一個網頁標簽的結構。我標出了ID,TagName,ClassName,Name所對應了位置。
結合之前介紹的By Class提供的靜態方法。我們就可以定位到對應的HTML元素的位置。說到這里,很多資料或是書本都會有這樣的描述:“編碼中,使用ID,Name,Class屬性是定位元素的首選方法”。沒錯這些元素定位簡單直接,而且理論上的執行效率較快。但是(請注意,我說了但是~~~),現實的情況是開發人員可能根本就沒有為元素定義這些屬性,或者實際的應用ID是自動隨機生成的,Name和ClassName又不能准確的唯一定位,此刻我們就需要其他的方式來定位元素。這也是XPath和Css選擇器的作用了,實際的工作中,這兩種定位方式應當是伴隨你的主要方式,so ... ... 好好了解一下還是很有必要的。關於XPath和Css選擇器的語法已經超出了我們要討論的問題,想了解的小伙伴可以網上找一下相關的資料,這里我就不多說了。下面我們可以看一下定位博客園首頁導航的XPath:
1 var divMain = driver.FindElement(By.Id("main")); 2 var lnkHome = driver.FindElement(By.XPath(".//ul[@class='post_nav_block']/li[1]/a")); 3 var lnkEssence = driver.FindElement(By.XPath(".//ul[@class='post_nav_block']/li[2]/a")); 4 var lnkCandidate = driver.FindElement(By.XPath(".//ul[@class='post_nav_block']/li[3]/a")); 5 var lnkNews = driver.FindElement(By.XPath(".//ul[@class='post_nav_block']/li[4]/a"));
關於獲取超鏈接元素的兩個方法By.LinkText和By.PartialLinkText,簡單明了,在此我就不多做介紹了。
(二)Selenium 如何擴展元素定位的方式
除了基本的定位方式之外,如果我們想擁有自定義的定位應該如何處理呢?比如,我們的測試過程中,我們想一次性獲取到所有CSS樣式是.btn的input標簽應當如何處理?
Selenium本身的底層驅動是基於JS的,同時也為我們上層的使用者(測試CASE編寫者或測試框架開發者)為提供了可以的執行自定義JS,並返回數據的方法。回顧一下上文中將到的核心對象WebDriver,都是繼承自RemoteWebDriver。而RemoteWebDriver實現了IJavaScriptExecutor接口,該接口定義了執行JS的行為:
1 namespace OpenQA.Selenium 2 { 3 // Summary: 4 // Defines the interface through which the user can execute JavaScript. 5 public interface IJavaScriptExecutor 6 {
7 object ExecuteAsyncScript(string script, params object[] args); 8 object ExecuteScript(string script, params object[] args); 9 } 10 }
因此,我們可以利用如下方式來執行我們自定義的JS,略懂JavaScript的同學看到這里,貌似應該明白了什么。沒錯,你可以自由的翱翔了(定義任何你想要的定位邏輯,不僅僅是定位元素)。當然,這個只是Selenium提供的接口,真正的使用和企業級的封裝有機會的話我會開一個新的系列為大家介紹,本節先介紹到這里。
1 IJavaScriptExecutor jsExecutor = driver as IJavaScriptExecutor ; 2 jsExecutor.ExecuteScript("return $('#elementID')");
(三)瀏覽器輔助工具
編程就像習武,功力深厚固然好,但總要有合手的兵器。接下來,為大家推薦一下元素定位可能會用到的工具:
如果你用的是Firefox瀏覽器,建議你下載Firebug和FirePath:
- Firebug擁有很多功能(元素定位,腳本調試,網絡監控 等等),可以幫你輕松的定位元素和觀察DOM結構。
- FirePath理論上可以用來生成元素的XPath或是驗證XPath的定位效果。(雖然工具可以生成XPath也不要指望靠他生成,還是要好好學習XPath的... ... 因為,工具生成的結果往往不是你想要的)。
如果你用的是Chrome或是IE瀏覽器,他們都有內置的開發者工具。
關於XPath的驗證步驟,請參考下圖(輸入Xpath>點擊Eval>查看定位到的元素):
(四)Demo
說了這么多,又到了做Demo時候了。so... 同樣做了個簡單的Demo供大家參考。Code上傳到Github地址是:https://github.com/DemoCnblogs/Selenium,本節的Demo是用來驗證了博客園首頁的導航欄內的文字是否正確,使用了幾種方式獲取DOM元素,代碼如下:
1 using OpenQA.Selenium; 2 using OpenQA.Selenium.Firefox; 3 using System.Collections.Generic; 4 using Xunit; 5 using Xunit.Abstractions; 6 7 namespace Demo.SeleniumTest 8 { 9 public class Lesson03_FindElement 10 { 11 /// <summary> 12 /// 輸出對象 13 /// </summary> 14 private readonly ITestOutputHelper _output; 15 /// <summary> 16 /// 構造函數,初始化輸出對象 17 /// </summary> 18 /// <param name="output">注入輸出對象</param> 19 public Lesson03_FindElement(ITestOutputHelper output) 20 { 21 this._output = output; 22 } 23 24 /// <summary> 25 /// demo1 : 獲取元素 26 /// </summary> 27 [Fact(DisplayName = "Cnblogs.CheckNavBar.Demo1")] 28 public void CheckNavBar_GetElement() 29 { 30 _output.WriteLine("Step 01 : 啟動瀏覽器並打開博客園首頁。"); 31 IWebDriver driver = new FirefoxDriver(); 32 driver.Url = "http://www.cnblogs.com"; 33 34 _output.WriteLine("Step 02 : 尋找需要檢查的頁面元素。"); 35 var divMain = driver.FindElement(By.Id("main")); 36 var lnkHome = driver.FindElement(By.XPath(".//ul[@class='post_nav_block']/li[1]/a")); 37 var lnkEssence = driver.FindElement(By.XPath(".//ul[@class='post_nav_block']/li[2]/a")); 38 var lnkCandidate = driver.FindElement(By.XPath(".//ul[@class='post_nav_block']/li[3]/a")); 39 var lnkNews = driver.FindElement(By.XPath(".//ul[@class='post_nav_block']/li[4]/a")); 40 41 _output.WriteLine("Step 03 : 檢查導航條文字信息。"); 42 Assert.Equal<string>("首頁", lnkHome.Text); 43 Assert.Equal<string>("精華", lnkEssence.Text); 44 Assert.Equal<string>("候選", lnkCandidate.Text); 45 Assert.Equal<string>("新聞", lnkNews.Text); 46 47 _output.WriteLine("Step 04 : 關閉瀏覽器。"); 48 driver.Close(); 49 } 50 51 52 [Fact(DisplayName = "Cnblogs.CheckNavBar.Demo2")] 53 public void CheckNavBar_GetElements() 54 { 55 _output.WriteLine("Step 00 : 准備測試數據。"); 56 var testDatas = new List<string>() { "首頁", "精華", "候選", "新聞" }; //准備測試數據 57 58 _output.WriteLine("Step 01 : 啟動瀏覽器並打開博客園首頁。"); 59 IWebDriver driver = new FirefoxDriver(); 60 driver.Url = "http://www.cnblogs.com"; 61 62 _output.WriteLine("Step 02 : 尋找需要檢查的頁面元素。"); 63 var divMain = driver.FindElement(By.Id("main")); 64 var lnkNavList = driver.FindElements(By.XPath(".//ul[@class='post_nav_block']/li[1]/a")); 65 66 _output.WriteLine("Step 03 : 檢查導航條文字信息。"); 67 for (var i = 0; i < lnkNavList.Count; i++) 68 { 69 Assert.Equal<string>(testDatas[i], lnkNavList[i].Text); 70 } 71 _output.WriteLine("Step 04 : 關閉瀏覽器。"); 72 driver.Close(); 73 } 74 } 75 }
總結:本文主要介紹了以下幾點內容。
- Selenium元素定位方式(ID,Name,ClassName,TagName,XPath,Css選擇器等等)。
- 如何擴展Selenium,並自定義定位方式
- 推薦了一些輔助工具
關於《Selenium For C#》 系列,我計划給大家逐一介紹一些Selenium Driver的基礎知識和框架的擴展點。 當然,之后會有更多關於測試框架構以及軟件構建方面的文章。願我主保佑我有時間做完這件事情... ...
《Selenium For C#》的相關文章:Click here.
- [小北De編程手記] : Lesson 01 - Selenium For C# 之 環境搭建
- [小北De編程手記] : Lesson 02 - Selenium For C# 之 核心對象
- [小北De編程手記] : Lesson 03 - Selenium For C# 之 元素定位
- [小北De編程手記] : Lesson 04 - Selenium For C# 之 API 上
- [小北De編程手記] : Lesson 05 - Selenium For C# 之 API 下
- [小北De編程手記] : Lesson 06 - Selenium For C# 之 流程控制
- [小北De編程手記] : Lesson 07 - Selenium For C# 之 窗口處理
- [小北De編程手記] : Lesson 08 - Selenium For C# 之 PageFactory & 團隊構建
說明:Demo地址:https://github.com/DemoCnblogs/Selenium