[小北De編程手記] : Lesson 05 - Selenium For C# 之 API 下


  上一篇,我們介紹了一些Selenium WebDriver相關的API,下面我們就接着上一篇繼續介紹Selenium常用的API,這一篇的內容主要涉及到以下話題:

  • Selenium API:復雜事件處理
  • Selenium API:特殊DOM元素處理
  • Selenium API:截圖功能
  • Selenium API:關於框架擴展

(一)Selenium API:復雜事件處理

  首先,我們試想一下這樣的場景。待測試的系統支持一些組合鍵的操作,例如:按住Ctrl的同時點擊某個表格的某個單元格,該數據行會高亮顯示。要模擬這樣的操作,我們應該怎么處理呢?Selenium WebDriver的API為我們提供了一個Actions類,使得我們可以模擬用戶復雜的操作。我先列出一個測試剛才描述場景的測試用例實現:

 1             //Step 01 : 啟動瀏覽器並打開某個站點的首頁。
 2             var driver = new FirefoxDriver();
 3             driver.Url = "http://www.xxx.com";
 4 
 5             //Step 02 : 尋找需要操作的表格單元格。
 6             var tableCol = driver.FindElement(By.XPath("//table[@id='tblTest']/tbody/tr/td[1]"));
 7             var action = new Actions(driver);
 8             action.KeyDown(Keys.Control);
 9             action.Click(tableCol);
10             action.Build();
11             action.Perform();

  上面代碼的Step02展示了如何利用Actions對象構建復雜的操作,值得注意的是當調用Actions對象的操作方法(即Click和KeyDown)時並沒有真正的執行你想要的操作,而是當Perform被調用的時候才真正的執行一些列的操作。另外,Actions類所有的操作方法都是自引用的。因此,我們可以用連續調用的方式更簡潔的完成操作。(說明:自引用的概念是編程大師C++之父 “比亞尼·斯特朗斯特魯普” 在《The C++ Programming Language》中最早提出的,就是一個對象的某個方法調用結束之后返回該對象本身的一種編程方式),因此之前的代碼等價於:

1             //Step 01 : 啟動瀏覽器並打開某個站點的首頁。
2             var driver = new FirefoxDriver();
3             driver.Url = "http://www.xxx.com";
4 
5             //Step 02 : 尋找需要操作的表格單元格。
6             var tableCol = driver.FindElement(By.XPath("//table[@id='tblTest']/tbody/tr/td[1]"));
7             var action = new Actions(driver);
8             action.KeyDown(Keys.Control).Click(tableCol).Build().Perform();

下面我簡單的羅列一下Actions類常用的方法:

  • Click : 點擊元素。
  • ClickAndHold : 單擊元素並保持按下的狀態。
  • Release : 在元素上釋放鼠標。
  • ContextClick : 單擊鼠標右鍵。
  • DoubleClick : 雙擊元素。
  • MoveByOffset : 移動鼠標到特定位置或某個元素。
  • KeyDown : 按下鍵盤的某個按鍵。
  • KeyUp: 釋放鍵盤的某個按鍵。
  • SendKeys : 向元素輸入指定文本。
  • DragAndDrop : 拖拽元素。
  • DragAndDropToOffset : 拖拽元素到指定位置。
  • Build : 構建操作的鏈表。
  • Perform : 執行當前Built 的 Action。(即執行用戶定義的一系列操作)

這些函數的聲明如下:

  關於上述操作,還是照例給大家做了個Demo。打開博客園首頁實現選中“代碼改變世界”的效果,選中本身意義不大,這里只是展示一下Selenium的操作粒度可以細到一個怎么樣的級別。代碼如下:

 1         /// <summary>
 2         /// demo4 : 復雜交互
 3         /// </summary>
 4         [Fact(DisplayName = "Cnblogs.SeleniumAPI.Demo4")]
 5         public void SeleniumAPI_Demo4()
 6         {
 7             _output.WriteLine("Step 01 : 啟動瀏覽器並打開博客園首頁。");
 8             IWebDriver driver = new FirefoxDriver();
 9             driver.Url = "http://www.cnblogs.com";
10 
11             _output.WriteLine("Step 02 : 尋找需要操作的頁面元素。");
12             var divText = driver.FindElement(By.Id("site_nav_top"));            
13             var point = divText.Location;
14             var width = int.Parse(divText.GetCssValue("width").Replace("px", string.Empty));
15             
16             _output.WriteLine("Step 03 : 選中文本信息。");
17             var action = new Actions(driver);
18             action
19                 .MoveByOffset(point.X, point.Y)         //移動鼠標到文本開頭
20                 .ClickAndHold()                         //按下鼠標    
21                 .MoveByOffset(point.X + width, point.Y) //移動鼠標到文本結束
22                 .Release()                              //釋放鼠標
23                 .Build()
24                 .Perform();
25 
26             System.Threading.Thread.Sleep(5000);
27             _output.WriteLine("Step 04 : 關閉瀏覽器。");
28             driver.Close();
29         }

 運行結果如下圖所示,我們可以看到首頁上的文字已經被選中:

(二)Selenium API:特殊DOM元素處理

  對於常見的按鈕,輸入框,超鏈接等元素,之前的Demo已經有很多相關的演示了,大多數對DOM元素的操作也都是點擊,輸入文本,獲取值... ... 這些操作IWebElement中都有相關的定義,我之前的文章《Lesson 02 - Selenium For C# 之 核心對象》里有相關的介紹,這里就不再贅述了。這一部分,我們主要介紹一下特殊的DOM元素以及處理方式。

@.下拉菜單處理

  下拉菜單是一種很常見的DOM元素,它不同於文本框、按鈕這樣的元素(一個標簽就可以描述)。而是由多個標簽組成的,其結構如下圖所示:

  那么在Selenium中我們應該如何處理呢?針對Select標簽,Selenium提供了一個專門的類來處理。聲明如下:

 1     // Summary:
 2     //     Provides a convenience method for manipulating selections of options in an
 3     //     HTML select element.
 4     public class SelectElement : IWrapsElement
 5     {
 6         // Summary:
 7         //     Gets all of the selected options within the select element.
 8         public IList<IWebElement> AllSelectedOptions { get; }
 9         //
10         // Summary:
11         //     Gets a value indicating whether the parent element supports multiple selections.
12         public bool IsMultiple { get; }
13         //
14         // Summary:
15         //     Gets the list of options for the select element.
16         public IList<IWebElement> Options { get; }
17 
18         public IWebElement SelectedOption { get; }
19 
20         public void DeselectAll();
21 
22         public void DeselectByIndex(int index);
23 
24         public void DeselectByText(string text);
25 
26         public void DeselectByValue(string value);
27 
28         public void SelectByIndex(int index);
29 
30         public void SelectByText(string text);
31 
32         public void SelectByValue(string value);
33     }

  SelectElement常見的操作方法和屬性如下:

  • IsMultiple : 獲取DOM對象是不是支持多選。
  • Options : 獲取所有的Option對象。
  • SelectedOption : 獲取選中的Option對象。
  • SelectByIndex : 根據索引選中元素。
  • SelectByText : 根據文本內容選中元素。
  • SelectByValue : 根據Value屬性的值選中元素。
  • DeselectXXX : Deselect開頭的方法都是用來撤銷選中的。

  實在是找不的一個下拉菜單,so... ...只能簡單的寫一下如何使用了,下面的Demo中展示了如何轉化並得到SelectElement對象並進行相關的操作。還有一點值得說明的是xUnit.Net中當設置Fact標簽的Skip屬性時,這個case在運行時是會被Runner忽略掉的,代碼如下:

 1         /// <summary>
 2         /// demo5 : SelectElement
 3         /// </summary>
 4         [Fact(DisplayName = "Cnblogs.SeleniumAPI.Demo5", Skip = "Just Demo")]
 5         public void SeleniumAPI_Demo5()
 6         {
 7             _output.WriteLine("Step 01 : 啟動瀏覽器並打開某個網站。");
 8             IWebDriver driver = new FirefoxDriver();
 9             driver.Url = "http://www.xxx.com";
10 
11             _output.WriteLine("Step 02 : 尋找需要操作的頁面元素。");
12             var dllDom = (SelectElement)driver.FindElement(By.Id("selectDomId"));
13             var isMultiple = dllDom.IsMultiple;
14             var option = dllDom.SelectedOption;
15             dllDom.SelectByIndex(1);
16             dllDom.SelectByText("Text");
17             dllDom.SelectByValue("Value");
18             
19             _output.WriteLine("Step 03 : 關閉瀏覽器。");
20             driver.Close();
21         }

@.單選按鈕的處理

  這里需要說明的另一種DOM元素是單選按鈕,其實在處理單選按鈕的時候和其他的IWebElement元素是一致的。都是使用IWebElement定義的Click方法點擊,用Selected屬性獲取元素的選中狀態。那為什么我要把這個元素單獨提出講解呢?原因就是單選按鈕往往是一組只能有一個被選中。對於這樣的元素狀態我們要特別注意。如:頁面上有A,B,C三個單選按鈕,默認A會被選中,此時你執行下面的代碼就可能會引發異常(Selenium會告知你A元素狀態已經改變需要重新加載)。

1             var radioA = driver.FindElement(By.Id("radioA_Id"));
2             var radioB = driver.FindElement(By.Id("radioB_Id"));
3             radioB.Click();
4             var isSelected = radioA.Selected;  // 拋出異常:元素已經改變

  其實,Selenium中我們經常需要處理某個已經緩存元素發生變化的情況。至於相關的最佳實踐,我會在后續的文章中介紹,本文主要是講解一些基礎的操作。

  對於不熟悉HTML的同學(說明一下,做B/S自動化 HTML是必須會的,so....還是要好好了解一下的),在此附上單選按鈕的DOM結構實例:

(三)Selenium API:截圖功能

  企業級的測試框架大多都會在測試CASE失敗的時候對當前的UI進行截圖,以方便測試人員定位Test Case失敗的原因。這里我就來專門介紹一下Selenium 提供的截圖功能,之前我們提到過我們使用的WebDriver繼承自RemoteWebDriver。而RemoteWebDriver實現的很多的功能接口,比如之前我們所提到的IJavaScriptExecutor接口。ITakesScreenshot接口定義了獲取屏幕截圖方法,同樣RemoteWebDriver實現了這個接口,也就是說我們可以利用WebDriver對象來獲取屏幕的截圖。ITakesScreenshot的定義如下:

 1 namespace OpenQA.Selenium
 2 {
 3     // Summary:
 4     //     Defines the interface used to take screen shot images of the screen.
 5     public interface ITakesScreenshot
 6     {
 7         // Summary:
 8         //     Gets a OpenQA.Selenium.Screenshot object representing the image of the page
 9         //     on the screen.
10         //
11         // Returns:
12         //     A OpenQA.Selenium.Screenshot object containing the image.
13         Screenshot GetScreenshot();
14     }
15 }

  下面我們就來看看如何實現截圖的功能,我們重寫了之前的一個Demo,打開博客園首頁搜索“小北De編程手記”。這一次,添加了截圖查詢結果頁面的功能,並把圖片保存到了當前的執行目錄下,使用ITakesScreenshot.GetScreenshot獲取Selenium提供的截屏對象,並保存截屏圖片到本地文件

 1         /// <summary>
 2         /// demo6 : 截屏
 3         /// </summary>
 4         [Fact(DisplayName = "Cnblogs.SeleniumAPI.Demo6")]
 5         public void SeleniumAPI_Demo6()
 6         {
 7             _output.WriteLine("Step 01 : 啟動瀏覽器並打開博客園首頁。");
 8             IWebDriver driver = new FirefoxDriver();
 9             driver.Url = "http://www.cnblogs.com";
10 
11             _output.WriteLine("Step 02 : 尋找需要操作的頁面元素。");
12             var txtSearch = driver.FindElement(By.Id("zzk_q"));
13             var btnSearch = driver.FindElement(By.XPath(".//input[@type='button' and @value='找找看']"));
14 
15             _output.WriteLine("Step 03 : 輸入查詢文本&點擊查詢");
16             txtSearch.SendKeys("小北De編程手記");
17             btnSearch.Click();
18             
19             _output.WriteLine("Step 04 : 截屏");
20             var takesScreenshot = (ITakesScreenshot)driver;
21             var screenshot = takesScreenshot.GetScreenshot(); 22             screenshot.SaveAsFile("screenshot.png", ImageFormat.Png); 23 
24             _output.WriteLine("Step 05 : 關閉瀏覽器。");
25             driver.Close();
26         }

代碼運行完畢之后,查看本地目錄,可以找到剛剛保存的文件:

 

(四)Selenium API:框架功能擴展

  最后,還是要給大家提一下關於如何擴展Selenium API的功能,這個部分我會在其他的關於自動化測試框架構建的系列中詳細描述,在這里只是簡單的提一下框架預留的接口IJavaScriptExecutor,之前的文章《Lesson 03 - Selenium For C# 之 元素定位》對這個接口有一個簡要的介紹。這里再次提及,主要有兩個原因:

  第一,該接口可以很好的發揮Javascript的能力,如果結合C#本身的功能(當然你也可以用Java,Ruby等其他Selenium支持的編程語言)可以幫助讓你的自動化測試框架具有很多很酷的功能(例如:控制瀏覽器滾動條,更簡單的讀取表格元素... ...)。

  第二,Selenium有着和優雅的設計,WebDriver還實現了許許多多其他的接口,IJavaScriptExecutor ITakesScreenshot也只是其中的一部分。每個接口都有特定的能力。這些往往是一般的自動化測試用例編寫人員不需要關心的地方,但如果你是一個測試框架的架構師或是一個打算構建自己的測試框架的小伙伴。那么了解這些接口是你的必經之路。

 

關於《Selenium For C#》 系列,我計划給大家逐一介紹一些Selenium Driver的基礎知識和框架的擴展點。 當然,之后會有更多關於測試框架構以及軟件構建方面的文章。願我主保佑我有時間做完這件事情... ...

《Selenium For C#》的相關文章:Click here.

說明:Demo地址:https://github.com/DemoCnblogs/Selenium

 

如果您認為這篇文章還不錯或者有所收獲,可以點擊右下角的 【推薦】按鈕,因為你的支持是我繼續寫作,分享的最大動力!
作者:小北@North
來源:http://www.cnblogs.com/NorthAlan
聲明:本博客原創文字只代表本人工作中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關系。非商業,未授權,貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文連接。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM