自動化測試如此容易!多語言自動化測試框架 Selenium 程式設計(C#篇)

2023-02-21 09:00:30

介紹

Selenium 官網:https://www.selenium.dev/

Selenium 是功能強大的自動化測試工具集,是支援 Web 瀏覽器自動化的一系列工具和庫的總括專案,一共包括以下三個專案:

  • Selenium WebDriver
  • Selenium IDE
  • Selenium Grid

Selenium 的核心是 WebDriver,可以在許多瀏覽器中交換執行,WebDriver 以原生的方式驅動瀏覽器,。

WebDriver 架構設計如下:
對每種瀏覽器編寫一個 Driver,如 ChromeDriver,這是操作瀏覽器的驅動,對外提供了各類操作介面。Selenium 設計了 WebDriver 抽象,以便通過統一的抽象使用各類瀏覽器驅動。

或者還可以遠端存取介面:

下面筆者介紹在 C# 中如何使用 Selenium WebDriver 編寫自動化測試程式。

安裝依賴

建立一個 C# 控制檯專案,首先安裝依賴包 Selenium.WebDriver,這個庫提供了瀏覽器驅動介面的基礎 API 和統一抽象。

Selenium.WebDriver

接著,安裝瀏覽器對應的驅動實現:

Selenium.WebDriver.ChromeDriver

只要搜尋 Selenium.WebDriver 即可,然後根據瀏覽器補充字尾,下載對應的瀏覽器驅動。

第一個 demo

開啟:https://www.selenium.dev/selenium/web/web-form.html

這個地址是官方用於測試的頁面,裡面有比較多的 html 元件,足夠我們學習使用。

下面這個範例中,包括了開啟頁面、查詢元素、填充內容和獲取資訊的程式碼,讀者可以執行這段程式碼從中瞭解編寫自動化測試程式的基本執行流程,更多的細節將在後面的小節中講解。

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

class Program
{
	static void Main()
	{
        // 使用 ChromeDriver 驅動
		IWebDriver driver = new ChromeDriver();
		
		// 啟動的時候開啟這個頁面
		driver.Navigate().GoToUrl("https://www.selenium.dev/selenium/web/web-form.html");
		
		// 獲取頁面資訊
		var title = driver.Title;
		// 隱式等待,頁面元素不會立馬出現,需要單獨一段時間
		driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500);
		// 搜尋元素
		var textBox = driver.FindElement(By.Name("my-text"));
		var submitButton = driver.FindElement(By.TagName("button"));
		
		// 往輸入框填充文字
		textBox.SendKeys("Selenium");
		// 點選提交按鈕
		submitButton.Click();
		
		// 點選提交按鈕之後,頁面會重新整理,此時獲取的是跳轉之後的頁面的元素
		var message = driver.FindElement(By.Id("message"));
		var value = message.Text;
		
		// 退出
		driver.Quit();
	}
}

注意:demo 程式啟動時,會啟動 Chrome 瀏覽器,如果啟動瀏覽器太慢,demo 程式會報錯退出。

因此需要先啟動 Chrome 瀏覽器,再啟動 demo 程式,以便減少 Chrome 瀏覽器新視窗的啟動時間。

demo 程式啟動後,會自動填充表單和提交,接著跳轉到新的頁面。

頁面載入策略

頁面開發模式有多種多樣,如 PHP、asp 這種一體式開發,如伺服器渲染然後返回整個頁面、前後端分離先載入靜態資源然後從後端 API 中載入資料生成頁面。

很多時候,頁面不會短時間完成渲染,有些頁面元素需要一段時間後才能出現。在使用 WebDriver 的時候,我們也可以根據需求決定在什麼時候啟動自動化操作。

頁面有三種基本載入策略:

策略 就緒狀態 備註
normal complete 預設值,,等待所有資源下載
eager interactive DOM 存取已準備就緒, 但諸如影象的其他資源可能仍在載入。
none Any 完全不會阻塞 WebDriver,WebDriver 僅等待初始頁面已下載。

如果由於下載對自動化不重要的資源(例如, 影象、css、js) 而需要很長時間才能載入頁面,,可以將預設引數 normal 更改為 eagernone 以加快對談載入速度。

設定方法:

var chromeOptions = new ChromeOptions();
chromeOptions.PageLoadStrategy = PageLoadStrategy.Normal;
IWebDriver driver = new ChromeDriver(chromeOptions);

另外,WebDriver 提供了三種方式等待頁面元素的出現:

  • 顯式等待

  • 隱式等待

  • 流暢等待

我們可以使用等待來讓 findElement 呼叫等待直到指令碼中動態新增的元素被新增到DOM中:

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement firstResult = wait.Until(e => e.FindElement(By.XPath("//a/h3")));

這種方法稱為顯式等待

WebDriver 會等待路徑 //a/h3 的元素出現,最大等待時間為 10s。

而通過隱式等待,WebDriver 在試圖查詢_任何_元素時在一定時間內輪詢DOM。當網頁上的某些元素不是立即可用並且需要一些時間來載入時是很有用的。

隱式等待是告訴 WebDriver 如果在查詢一個或多個不是立即可用的元素時輪詢 DOM 一段時間。一旦設定好,隱式等待就被設定為對談的生命週期。

設定隱式等待的輪詢時間:

driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500);

警告: 不要混合使用隱式和顯式等待。這樣做會導致不可預測的等待時間。例如,將隱式等待設定為10秒,將顯式等待設定為15秒,可能會導致在20秒後發生超時。

流暢等待 定義了等待條件的最大時間量,以及檢查條件的頻率。

使用者可以設定等待來忽略等待時出現的特定型別的異常,例如在頁面上搜尋元素時出現的NoSuchElementException

WebDriverWait wait = new WebDriverWait(driver, timeout: TimeSpan.FromSeconds(30))
{
	PollingInterval = TimeSpan.FromSeconds(5),
};

代理

代理伺服器充當使用者端和伺服器之間的請求中介,使用代理伺服器用於 Selenium 的自動化指令碼, 可能對以下方面有益:

  • 捕獲網路流量
  • 模擬網站後端響應
  • 在複雜的網路拓撲結構或嚴格的公司限制/政策下存取目標站點.

如果在公司環境中,或者需要開啟飛機上網,瀏覽器無法連線到 URL,則需要藉助代理進行存取。

Selenium WebDriver 提供瞭如下設定代理的方法,程式碼範例如下:

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

class Program
{
	static void Main()
	{   		
		ChromeOptions options = new ChromeOptions();
		
		Proxy proxy = new Proxy();
		proxy.Kind = ProxyKind.Manual;
		proxy.IsAutoDetect = false;
		proxy.SslProxy = "<HOST:PORT>";
		options.Proxy = proxy;
		options.AddArgument("ignore-certificate-errors");
		
		IWebDriver driver = new ChromeDriver(options);
		driver.Navigate().GoToUrl("https://www.selenium.dev/");
	}
}

瀏覽器版本

例如, 假設想使用 Chrome 版本 67 在 Windows XP 上執行 Chrome:

var chromeOptions = new ChromeOptions();

chromeOptions.BrowserVersion = "67";
chromeOptions.PlatformName = "Windows XP";

元素操作

元素操作主要分為下面這幾種:

  • 檔案上傳

  • 查詢網路元素:根據提供的定位值定位元素

  • Web元素互動:用於操縱表單的高階指令集

  • 定位策略:在 DOM中 標識一個或多個特定元素的方法

  • 元素的資訊:html 元素的屬性

下面來介紹不同 html 元素的操作方法範例。

檔案上傳

上傳檔案實際上是在 type=fileinput 標籤中,填寫本地路徑的檔案地址,這個地址需要填寫檔案的絕對路徑。

using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

namespace SeleniumDocumentation.SeleniumPRs
{
	class FileUploadExample
	{
		static void Main(String[] args)
		{
			IWebDriver driver = new ChromeDriver();
			try
			{
				// Navigate to Url
				driver.Navigate().GoToUrl("https://www.selenium.dev/selenium/web/web-form.html");
				// 檔案路徑一定是可以存在的,不能亂填,建議絕對路徑
				driver.FindElement(By.Name("my-file")).SendKeys("D:/Desktop/images/學習.jpg");
				var submitButton = driver.FindElement(By.TagName("button"));
				
				submitButton.Click();
				
				if (driver.PageSource.Contains("File Uploaded!"))
				{
					Console.WriteLine("file uploaded");
				}
				else
				{
					Console.WriteLine("file not uploaded");
				}

				driver.Quit();
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex);
			}
		}
	}
}

查詢元素

在 WebDriver 中有 8 種不同的內建元素定位策略:

定位器 Locator 描述
class name 定位class屬性與搜尋值匹配的元素(不允許使用複合類名)
css selector 定位 CSS 選擇器匹配的元素
id 定位 id 屬性與搜尋值匹配的元素
name 定位 name 屬性與搜尋值匹配的元素
link text 定位link text可視文字與搜尋值完全匹配的錨元素
partial link text 定位link text可視文字部分與搜尋值部分匹配的錨點元素。如果匹配多個元素,則只選擇第一個元素。
tag name 定位標籤名稱與搜尋值匹配的元素
xpath 定位與 XPath 表示式匹配的元素

下面是查詢元素的用例:

// 通過 id 或 name
IWebElement vegetable = driver.FindElement(By.ClassName("tomatoes"));
IWebElement fruits = driver.FindElement(By.Id("fruits"));
IWebElement fruit = fruits.FindElement(By.ClassName("tomatoes"));

// 通過 css 選擇器
var fruit = driver.FindElement(By.CssSelector("#fruits .tomatoes"));

// 返回多個元素
IReadOnlyList<IWebElement> plants = driver.FindElements(By.TagName("li"));

獲取當前頁面的焦點在哪個元素:

var element = driver.SwitchTo().ActiveElement();
string attr = element.GetAttribute("title");

頁面元素互動

僅有五種基本命令可用於元素的操作:

  • 點選 (適用於任何元素)
  • 傳送鍵位 (僅適用於文字欄位和內容可編輯元素,.SendKeys())
  • 清除 (僅適用於文字欄位和內容可編輯元素)
  • 提交 (僅適用於表單元素)(在Selenium 4中不再建議使用)
  • 選擇(查詢元素)
點選

可以觸發元素的點選事件:

var submitButton = driver.FindElement(By.TagName("button"));
submitButton.Click();
輸入

元素傳送鍵位命令,即 .SendKeys() ,這個方法對可編輯的元素都通用,如 input、select 等元素。

driver.FindElement(By.Name("my-file")).SendKeys("D:/Desktop/images/學習.jpg");
清除

對於可編輯文字或具有輸入的元素,如文字域、選擇框、檔案上傳框的,可以清除元素當前的value 屬性。

IWebElement searchInput = driver.FindElement(By.Name("q"));
searchInput.SendKeys("selenium");
// Clears the entered text
searchInput.Clear();

獲取元素屬性

  • 是否顯示
  • 是否啟用
  • 是否被選定
  • 獲取元素標籤名
  • 位置和大小
  • 獲取元素CSS值
  • 文字內容
  • 獲取特性或屬性

在 JS 中,我們可以這樣獲取一個元素的值或其它屬性:

document.getElementById("my-text-id").value
"111111111"

在 WebDriver 中可以通過 IWebElement 介面的 欄位/屬性 獲取元素屬性,但不多:

Boolean is_email_visible = driver.FindElement(By.Name("email_input")).Displayed;

其它需要的屬性可以通過 GetAttribute 等方法獲取,如:

string attr = element.GetAttribute("title");

IWebElement 的定義如下:

  public interface IWebElement : ISearchContext
  {
    string TagName { get; }
    string Text { get; }
    bool Enabled { get; }
    bool Selected { get; }
    Point Location { get; }
    Size Size { get; }
    bool Displayed { get; }
    void Clear();
    void SendKeys(string text);
    void Submit();
    void Click();
    string GetAttribute(string attributeName);
    string GetDomAttribute(string attributeName);
    string GetDomProperty(string propertyName);
    string GetCssValue(string propertyName);
    ISearchContext GetShadowRoot();
  }

瀏覽器頁面

對於瀏覽器頁面的操作,無外乎下面四種:

  • 開啟網站

  • 後退

  • 前進

  • 重新整理

範例程式碼也很簡單:

// 開啟
driver.Navigate().GoToUrl(@"https://selenium.dev");
// 後退
driver.Navigate().Back();
// 前進
driver.Navigate().Forward();
// 重新整理
driver.Navigate().Refresh();

使用者登入憑證

目前只發現了使用 Basic、Cookie 兩種登入認證方式, JWT Token 這種需要設定 Header 頭的方式找不到實現。

下面是使用 Cookie 開啟網頁的範例:

			var chromeOptions = new ChromeOptions();
			IWebDriver driver = new ChromeDriver(chromeOptions);
			
			try
			{
				driver.Navigate().GoToUrl("https://www.google.com");
				// Adds the cookie into current browser context
				driver.Manage().Cookies.AddCookie(new Cookie("key", "value"));
				
				driver.FindElement(By.CssSelector("[name='q']")).SendKeys("webElement");
				// Get attribute of current active element
				var btnK = driver.FindElement(By.Name("btnK"));
				btnK.Click();
			}
			finally
			{
				driver.Quit();
			}

關於使用 C# 開發 Selenium WebDriver 的教學就到這裡,讀者可到官方檔案瞭解更多。