自动化测试如此容易!多语言自动化测试框架 Selenium 编程(C#篇)
介绍
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
更改为 eager
或 none
以加快会话加载速度。
设置方法:
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=file
的 input
标签中,填写本地路径的文件地址,这个地址需要填写文件的绝对路径。
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 的教程就到这里,读者可到官方文档了解更多。