Selenium 自动化框架
Selenium 是流行的浏览器自动化框架,包含了很多库和工具。常被用于端到端的测试,可以配合 mocha、chai 等测试库使用。
Selenium 使用 WebDriver 让一套代码能运行于多个主要浏览器。
Example
const {Builder, By, Key, until} = require('selenium-webdriver')
async function example() {
const driver = await new Builder().forBrowser('chrome').build()
try {
await driver.get('https://cn.bing.com')
await driver.findElement(By.name('q')).sendKeys('selenium', Key.ENTER)
const firstResult = await driver.wait(until.elementLocated(By.css('h2')), 10000)
console.log(await firstResult.getAttribute('textContent'))
} finally {
driver.quit()
}
}
example()
保存为文件 test.js
,运行
node test.js
Install
Selenium webdriver
用于控制浏览器的驱动,提供多种语言的接口。以 JavaScript 为例
npm i selenium-webdriver
Driver binaries
下载对应浏览器的 driver 二进制文件,添加到系统路径。版本需要随着浏览器版本一起更新。
以 Chrome 为例:https://chromedriver.storage.googleapis.com/index.html
Getting started with WebDriver
Locating elements
WebDriver 基本功能之一是定位页面元素。
使用driver.findElement
,By
等接口
const {By} = require('selenium-webdriver')
const result = driver.findElement(By.css('h2'))
判断元素是否存在
const elements = await driver.findElements(By.css('#id'))
const hasElement = elements.length
By.[css|xpath|name|...]()
Locator | Description |
---|---|
class name | Locates elements whose class name contains the search value (compound class names are not permitted) |
css selector | Locates elements matching a CSS selector |
id | Locates elements whose ID attribute matches the search value |
name | Locates elements whose NAME attribute matches the search value |
link text | Locates anchor elements whose visible text matches the search value |
partial link text | Locates anchor elements whose visible text contains the search value. If multiple elements are matching, only the first one will be selected. |
tag name | Locates elements whose tag name matches the search value |
xpath | Locates elements matching an XPath expression |
Perform actions
获取元素后可以执行相关的操作
发送按键来模拟用户输入
await driver.findElement(By.name('q')).sendKeys('selenium', Key.ENTER)
拖放元素(drag and drop)
const actions = driver.actions({ bridge: true })
const source = driver.findElement(By.id('source'))
const target = driver.findElement(By.id('target'))
await actions.dragAndDrop(source, target).perform()
点击元素
driver.findElement(By.css('input[type="submit"]')).click()
WebDriver
WebDriver 负责通过各种方式与浏览器交互。
术语(terminology)
- API: 命令。The commands to manipulate WebDriver.
- Libary:代码模块。A code module which contains the APIs and the code.
- Driver: 浏览器驱动。Responsible for controlling the actual browser.
- Framework: 整体环境。A support for WebDriver suites
各组件关系(component)
最简单的情况,WebDriver 直接调用 driver 来控制对应的浏览器。
也可以借助 Remote WebDriver, selenium Server or Grid 等间接调用 driver。
比较复杂的情况, 测试框架(Test Framework)负责运行 WebDriver 相关的测试步骤。
调用关系为 The Test Framework <-> WebDriver <-> ... <-> Driver <-> Browser
Browser manipulation
通过 WebDriver 启动浏览器后,可以模拟用户进行各种操作。
-
Browser navigation
await driver.get('https://selenium.dev') await driver.navigate().back() await driver.navigate().forward() await driver.navigate().refresh() await driver.getCurrentUrl() await driver.getTitle()
-
Windows and tabs
-
Frames and Iframes
-
Window management
-
Set window position
Waits
浏览器有很多异步操作修改 DOM 的状态。为了确保 WebDriver 获取到正确的 DOM 状态,需要在一定程度上控制异步的流程。Waits 可以让当前进程一直等待,条件满足后再执行后续流程。
Explicit wait
显式的等待,可用于 imperative, procedural language(命令式、过程式语言)。可以暂停或冻结进程,定时检查继续的条件是否满足。适合于同步浏览器和 DOM 的状态到脚本。
使用driver.wait
,可以指定最大等待时间(timeout)
await driver.get('file///path/to/test.html')
const isInitialised = () =>
driver.executeScript('return initialised')
await driver.wait(isInitialised, 2000)
const element = driver.findElement(By.css('p'))
配合until
的方法可以写得更简洁
const {By, until} = require('selenium-webdriver')
const element = await driver.wait(
until.elementLocated(By.css('p')),
2000,
)
test.html
中的模拟脚本
let initialised = false
window.addEventListener('load', function () {
setTimeout(() => {
const newElement = document.createElement('p')
newElement.textContent = 'Hello from JavaScript!'
document.body.appendChild(newElement)
initialised = true
}, 500)
})
Implicit wait
隐式等待。设置间隔后,WebDriver 每次获取 DOM 元素时,如果不存在,等待一定间隔后会重新尝试。默认为0
, 不启用。
driver.manage().setTimeouts({ implicit: 10000 })
await driver.get('file:///Users/apple/files/html/tmp.html')
const element = driver.findElement(By.css('p'))
FluentWait
弹性等待。同时设置最大等待时间和检查条件的间隔。
// Waiting 30 seconds for an element to be present on the page,
// checking for its presence once every 5 seconds
const element = await driver.wait(
until.elementLocated(By.css('p')),
30000,
'Timed out after 30 seconds',
5000
)
Web element
WebElement 对象代表了一个 DOM element。可以通过 WebDriver 或其它 WebElement 来查找。条件包括 ID, Name, Class, XPath, CSS Selectors, link Text, etc.
主要组合使用findElement()
和By.[css|xpath|name|...]()
Find Element
const element = driver.findElement(By.name('q'))
Find Elements
const elements = await driver.findElements(By.name('q'))
for(let e of elements) {
console.log(await e.getText())
}
Find Element/Elements from Element
调用同样的方法,查找已有 WebElement 的子元素
Get Active Element
获取当前焦点所在的元素
await driver.switchTo().activeElement().getAttribute('title')
Is Element Enabled
await driver.findElement(By.name('btn')).isEnabled()
Is Element Selected
This method is widely used on Check boxes, radio butotns, input elements, and option elements.
await driver.findElement(By.css("input[type='checkbox']:last-of-type")).isSelected()
Get Element TagName
await driver.findElement(By.css('h1')).getTagName()
Get Elment Rect
await driver.findElement(By.css('h1')).getRect()
Get Element CSS Value
await driver.findElement(By.css('h1')).getCssValue('color')
Keyboard
Keyboard 代表了键盘相关的事件,用来模拟输入。
sendKeys
模拟发送连续的按键
await driver.findElement(By.name('q')).sendKeys('selenium', Key.ENTER)
keyDown
模拟按下 modifier key(CONTROL, SHIFT, ALT, COMMAND, ...)
await driver.actions().keyDown(Key.CONTROL).sendKeys('a').perform()
keyUp
模拟释放 modifier key(CONTROL, SHIFT, ALT, COMMAND, ...)
const search = driver.findElement(By.name('q'))
await driver.actions().click(search)
.keyDown(Key.SHIFT).sendKeys('qwerty')
.keyUp(Key.SHIFT).sendKeys('qerty').perform()
clear
This is only applied for the elements which are editable and interactable
await search.clear()
Cookie
const cookies = await driver.manage().getCookies()
console.log(cookies)
Selenium Grid
Selenium Grid 支持使用 remote driver 远程调用
new RemoteWebDriver(new URL(seleniumUrl), options)
同时通过 seleniumUrl 也可以访问 web ui, 根据 session id 来查看对应 session 的实时页面 (仅开发模式)
((RemoteWebDriver) driver).getSessionId()