Selenium Webdriver 基于浏览器的自动化测试

Selenium HQ

SeleniumHQ 是一款基于浏览器开源的自动化测试组件,支持 win、linux、Mac 、Android、Ios 几大平台上的 IE、Firefox、Chrome 等浏览器,也支持模拟安卓、ios、winphone设置上的浏览器,「一切重复操作的流程均可自动化」,对测试来讲,大大提高测试程序对不同平台不同浏览器兼容性的效率。

SeleniumHQ 2.0 组件包括 Selenium webdriver、Selenium IDE 两大组件,与之前的1.0版本有所区别(具体区别可以参考Selenium 发展历史)。使用Selenium IDE,可以通过安装Firefox浏览器插件,录制你所有的操作,也就是说,你操作一遍业务流程,后续的测试回归均可以交给 Selenium IDE 录制下来的程序去测试。优点就是简单,不需要编写代码,缺陷是如果业务流程发生了改变,需要重新录制脚本。使用Selenium Webdriver 可以编写代码,指定程序处理逻辑,比如 form 表单验证登录、提交返回结果验证等,功能强大,可以说能满足所有的场景。

一、基础使用

Selenium Webdriver 支持 Java、C#、Python、Ruby、Php、Perl、Javascript 等几种语言,不过不同的语言实现功能的完整性略有区别,下文介绍下 Java 客户端的使用方法。

1.1 完整实例

创建一个基于 maven 的 java 项目 ,使用 Selenium Webdriver 只需要引用相依的依赖即可:

1
2
3
4
5
6
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<!-- 目前最新版本 -->
<version>2.49.1</version>
</dependency>

一个简单的使用例子:通过程序打开百度首页,收入搜索关键字「两会」,搜索相应的结果内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void main(String[] args) {
// 如果你的 FireFox 没有安装在默认目录,那么必须在程序中设置
// System.setProperty("webdriver.firefox.bin", "D:\\Program Files\\Mozilla Firefox\\firefox.exe");
// 创建一个 FireFox 的浏览器实例
WebDriver driver = new FirefoxDriver();
// 让浏览器访问 Baidu
driver.get("http://www.baidu.com");
// 用下面代码也可以实现
// driver.navigate().to("http://www.baidu.com");
// 获取 网页的 title
System.out.println("1 Page title is: " + driver.getTitle());
// 通过 id 找到 input 的 DOM
WebElement element = driver.findElement(By.id("kw"));
// 输入关键字
element.sendKeys("两会");
// 提交 input 所在的 form
element.submit();
// 通过判断 title 内容等待搜索页面加载完毕,间隔10秒
(new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
return d.getTitle().toLowerCase().endsWith("两会");
}
});
// 显示搜索结果页面的 title
System.out.println("2 Page title is: " + driver.getTitle());
//关闭浏览器
driver.quit();
}

1.2 支持浏览器

Selenium WebDriver 支持如下浏览器,在所有支持这些浏览器的操作系统中能都运行良好。

  • Google Chrome
  • Internet Explorer 6, 7, 8, 9, 10 - 32 and 64-bit where applicable
  • Firefox: latest ESR, previous ESR, current release, one previous release
  • Safari
  • Opera
  • HtmlUnit
  • phantomjs
  • Android (with Selendroid or appium)
  • iOS (with ios-driver or appium)

Selenium WebDriver 直接通过浏览器自动化的本地接口来调用浏览器。如何直接调用,和调用的细节取决于你使用什么浏览器,所以需要相应平台相应的浏览器「驱动」,使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// firefox 浏览器
// firexfox 是原生支持的,不需要特殊的浏览器驱动。如果安装目录不是默认的,需要指定相应的环境变量
// System.setProperty("webdriver.firefox.bin", "D:\\Program Files\\Mozilla Firefox\\firefox.exe");
WebDriver driver = new FirefoxDriver();
// chrome 浏览器
// ChromeDriver is maintained / supported by the Chromium project iteslf. https://github.com/SeleniumHQ/selenium/wiki/ChromeDriver
// 下载 ChromeDriver:https://sites.google.com/a/chromium.org/chromedriver/downloads
// Optional, if not specified, WebDriver will search your path for chromedriver.
// System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver");
WebDriver driver = new ChromeDriver();
// Safari、opera、Android、ios 等浏览器使用同理,下载安装相应的驱动,非默认目录需要设置 System.setProperty 属性
// 下载地址及参考:https://github.com/SeleniumHQ/selenium/wiki

1.3 支持 Api

Selenium Webdriver 有很多的 Api可以调用,进行浏览器操作,举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//获取一个页面 get 和 navigate 结果是一样的
// driver.navigate().to("http://www.baidu.com");
driver.get("http://www.baidu.com");
// 查找 UI 元素(web 元素)
// 根据元素 id 获取 ,对应 js 中 document.getElementById
WebElement element = driver.findElement(By.id("element_id"));
// 根据元素 class 属性获取,对应 js 中的 document.getElementsByClassName
List<WebElement> elementList = driver.findElements(By.className("element_class"));
// 根据元素标签名查找 ,对应 js 中的 document.getElementsByName
WebElement iframe = driver.findElement(By.tagName("iframe"));
// 根据元素 css 属性
// 举例 html: <div id="food"><span class="dairy">milk</span><span class="dairy aged">cheese</span></div>
WebElement cheese = driver.findElement(By.cssSelector("#food span.dairy.aged"));
// 筛选表单元素,对表单进行操作等,更多例子请参考:http://www.seleniumhq.org/docs/03_webdriver.jsp#selenium-webdriver-api-commands-and-operations

有时候页面没加载完成,相应的元素并没有解析完成,通过 driver.findElement 的时候会报找不到Element not found的异常,这时候就需要进行等待一断时间后再获取该元素,实现此功能有两种方式Explicit WaitsImplicit Waits,区别在于前者每隔500ms都会去尝试获取该元素,直到设定的时间到后仍找不到,抛出TimeoutException,而后者会一直等待到指定的时间,才会尝试获取该元素,找不到抛出Element not found的异常,代码上相当于Thread.sleep(),所以推荐使用Explicit Waits 的方式才操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. Explicit Waits
// This waits up to 10 seconds before throwing a TimeoutException or if it finds the element will return it in 0 - 10 seconds. WebDriverWait by default calls the ExpectedCondition every 500 milliseconds until it returns successfully. A successful return is for ExpectedCondition type is Boolean return true or not null return value for all other ExpectedCondition types.
WebDriver driver = new FirefoxDriver();
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(ExpectedConditions.presenceOfElementLocated(By.id("myDynamicElement")));
// 2. Implicit Waits
// An implicit wait is to tell WebDriver to poll the DOM for a certain amount of time when trying to find an element or elements if they are not immediately available. The default setting is 0. Once set, the implicit wait is set for the life of the WebDriver object instance.
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));

1.4 操作 cookie

Selenium Webdriver 如何操作 cookie ? 首先,你必须在 cookie 所在的域。如果你希望在加载一个大页面之前重设 cookie,你可以先访问站点中一个较小的页面,典型的是 404 页面 (http://example.com/some404page)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 进到正确的域
driver.get("http://www.example.com");
// 设置 cookie,这个cookie 对整个域都有效
Cookie cookie = new Cookie("key", "value");
driver.manage().addCookie(cookie);
// 输出当前 url 所有可用的 cookie
Set<Cookie> allCookies = driver.manage().getCookies();
for (Cookie loadedCookie : allCookies) {
System.out.println(String.format("%s -> %s", loadedCookie.getName(), loadedCookie.getValue()));
}
// 你可以通过3中方式删除 cookie
// By name
driver.manage().deleteCookieNamed("CookieName");
// By Cookie
driver.manage().deleteCookie(loadedCookie);
// Or all of them
driver.manage().deleteAllCookies();

真实测试环境中,有些网站是需要登录的,如果想要免登陆直接访问测试某些页面,就需要保持用户的登录态,也就是说需要传递cookie到服务端,一种方式就是获取到网站需要的cookie,通过上面的方法一个 key 一个 key 的设置,另外一种方法直接在 http request header 请求头中设置 cookie的值,比如这种: Alt text

这依赖第三方开源组件BrowserMobProxy 来实现,它可以实现更改请求的 HTTP requests and responses,详情可以参考官网:https://bmp.lightbody.net/ 。使用首选需要依赖相应的 jar 包:

1
2
3
4
5
6
7
<dependency>
<groupId>net.lightbody.bmp</groupId>
<!-- To use the legacy, Jetty-based implementation, change the artifactId to browsermob-core -->
<artifactId>browsermob-core-littleproxy</artifactId>
<version>2.1.0-beta-4</version>
<scope>test</scope> <!-- test类中使用,非test去掉即可 -->
</dependency>

代码使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void testHttpHeader() {
// start the proxy
BrowserMobProxy proxy = new BrowserMobProxyServer();
proxy.start(0);
// get the Selenium proxy object
Proxy seleniumProxy = ClientUtil.createSeleniumProxy(proxy);
// configure it as a desired capability
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(CapabilityType.PROXY, seleniumProxy);
// start the browser up
WebDriver driver = new FirefoxDriver(capabilities);
// 举例获取12306未完成订单
String url12306 = "https://kyfw.12306.cn/otn/queryOrder/initNoComplete";
final String cookieValue = ""; //登录后获取请求中cookie的那长串内容
proxy.addRequestFilter(new RequestFilter() {
@Override
public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
request.headers().add("Cookie", cookieValue);
return null;
}
});
driver.get(url12306);

二、高级用法

高级用户交互API提供了一个更新更完善的机制来定义并描述用户在一个网页上的各种操作。这些操作包括:更改UA、拖拽、按住CTRL键选择多个元素等等。

2.1 更改UA

改变浏览器的 User Agent ,当使用 Firefox Driver 的时候这很容易:

1
2
3
FirefoxProfile profile = new FirefoxProfile();
profile.addAdditionalPreference("general.useragent.override", "some UA string");
WebDriver driver = new FirefoxDriver(profile);

为了生成一连串的动作,我们使用Actions来建立。这一系列的动作应该尽量的短。在使用中最好在执行一个简短的动作后验证页面是否处于正确的状态,然后再执行下面的动作。

1
2
3
4
5
6
7
8
9
10
// 定义动作
Actions builder = new Actions(driver);
builder.keyDown(Keys.CONTROL)
.click(someElement)
.click(someOtherElement)
.keyUp(Keys.CONTROL);
// 获取动作
Action selectMultiple = builder.build();
// 执行动作
selectMultiple.perform();

2.2 键盘交互(Keyboard interactions)

键盘交互是发生在一个特定的页面元素的,而webdriver会确保这个页面元素在执行键盘动作时处于正确的状态。这个正确的状态,包括页面元素滚动到可视区域并定位到这个页面元素。 既然这个新的API是面向用户(user-oriental)的接口,那么对于一个用户,在对一个元素输入文本前做显式的交互就更加的符合逻辑。这意味着,当想定位到相邻的页面元素时,可能需要点击一下元素或按下Tab(Keys.TAB)键。 The new interactions API will (first) support keyboard actions without a provided element. The additional work to focus on an element before sending it keyboard events will be added later on.

这块具体的内容及案例可以参考:http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp#advanceduserinteractions ,中文翻译在:https://wizardforcel.gitbooks.io/selenium-doc/content/official-site/selenium-web-driver.html

2.3 通过代理实现抓取请求header、response等信息

通过使用 BrowserMob Proxy ,详细参考 Performance data collection using BrowserMob Proxy and Selenium:http://www.assertselenium.com/browsermob-proxy/performance-data-collection-using-browsermob-proxy-and-selenium/

三、经常遇到的问题

  1. selenium IeDriver 使用测试 IE11 浏览器后,调用代码 driver.quit() 方法后, IEDriverServer.exe 进程不退出,越来越多,导致内存不断增大最后崩溃。ff、chrome 浏览器并没有问题。 原因是 截图导致,去掉截图的代码就ok。
  2. 截图出现黑屏现象,图片全部是黑色的。webdriver截图黑屏原因:① window的远程桌面连接,连接到agent,然后退出远程连接,会被锁屏 ② window的睡眠时间设置成永不睡眠,否则睡眠后,截图是黑色的 解决方案: 1)、window的远程桌面连接,连接到agent,然后退出远程连接,会被锁屏,所以需要安装tightvnc,通过UltraVNC连接,这样关闭UltraVNC不会影响远程机 2)、UltraVNC 连接agent后,agent的窗口不会大屏的,总是显示800600的,所以需要设置远程agent的分辨率,设置成12801024 3)、window的睡眠时间设置成永不睡眠,否则睡眠后,截图是黑色的

参考资料:

  • SeleniumHQ 官网:http://www.seleniumhq.org/
  • Selenium document :http://www.seleniumhq.org/docs/ ,中文翻译:https://wizardforcel.gitbooks.io/selenium-doc/content/official-site/selenium-web-driver.html
  • New selenium documentation:https://seleniumhq.github.io/docs/
  • Selenium Github:https://github.com/SeleniumHQ/selenium
  • Selenium Java Api:http://seleniumhq.github.io/selenium/docs/api/java/index.html
  • Assert Selenium: http://www.assertselenium.com/
  • BrowserMob Proxy:https://github.com/lightbody/browsermob-proxy
xirong wechat
我在公众号与你分享更多内容