本文所述内容在Windows系统下实践完成,涉及的软件版本信息如下:
注:部分软件版本间兼容性十分重要,下文中会提及,使用其他版本时请注意。 |
Selenium是用于测试 Web应用程序用户界面 (UI)的常用框架。它是一款用于运行端到端功能测试的超强工具。您可以使用多个编程语言编写测试,并且 Selenium能够在一个或多个浏览器中执行这些测试。
Selenium1.0
2004年,ThoughtWorks公司的JasonHuggins和他所在的团队采用Javascript编写一种测试工具来验证浏览器页面的行为。这个JavaScript类库就是Selenium core,同时也是seleniumRC、Selenium IDE的核心组件。
关于命名 当时QTP mercury是主流的商业自动化工具,是化学元素汞(俗称水银),而Selenium是开源自动化工具,是化学元素硒,硒可以对抗汞。 |
Selenium 1.0 = Selenium IDE + Selenium Grid + SeleniumRC
n Selenium IDE Selenium IDE是嵌入到Firefox浏览器中的一个插件,实现简单的浏览器操作的录制与回放功能。 n Selenium Grid Selenium Grid是一种自动化的测试辅助工具,Grid通过利用现有的计算机基础设施,能加快Web-App的功能测试。利用Grid可以很方便地实现在多台机器上和异构环境中运行测试用例。 n Selenium RC Selenium RC(Remote Control)是Selenium家族的核心部分。Selenium RC 支持多种不同语言编写的自动化测试脚本,通过Selenium RC的服务器作为代理服务器去访问应用,从而达到测试的目的。 Selenium RC分为Client Libraries和Selenium Server。Client Libraries库主要用于编写测试脚本,用来控制Selenium Server的库。Selenium Server负责控制浏览器行为。 |
Selenium 2.0
2006年,Google的工程师SimonStewart发起了WebDriver的项目;因为长期以来Google一直是Selenium的重度用户,但却被限制在有限的操作范围内。
Selenium RC是在浏览器中运行JavaScript应用,使用浏览器内置的JavaScript翻译器来翻译和执行selenese命令(selenese是Selenium命令集合)。
WebDriver是通过原生浏览器支持或者浏览器扩展来直接控制浏览器。WebDriver针对各个浏览器而开发,取代了嵌入到被测Web应用中的JavaScript,与浏览器紧密集成,因此支持创建更高级的测试,避免了JavaScript安全模型导致的限制。除了来自浏览器厂商的支持之外,WebDriver还利用操作系统级的调用,模拟用户输入。
2011年,Selenium和Webdriver合并, Selenium2.0诞生。
Selenium与WebDriver原是属于两个不同的项目,WebDriver的创建者Simon Stewart早在2009年8月的一份邮件中解释了项目合并的原因。 Selenium与WebDriver合并原因:为何把两个项目合并?部分原因是WebDriver解决了Selenium存在的缺点(例如能够绕过JavaScript沙箱,我们有出色的API),部分原因是Selenium解决了WebDriver存在的问题(例如支持广泛的浏览器),部分原因是因为Selenium的主要贡献者和我都觉得合并项目是为用户提供最优秀框架的最佳途径。 |
Selenium 2.0 = Selenium 1.0 + WebDriver
需要强调的是,在Selenium 2.0中主推的是WebDriver,可以将其看作SeleniumRC的替代品。因为Selenium为了保持向下的兼容性,所以在Selenium 2.0中并没有彻底地抛弃Selenium RC。
Selenium 2具有来自WebDriver的清晰面向对象 API,并能以最佳的方式与浏览器进行交互。Selenium 2不使用 JavaScript沙盒,它支持多种浏览器和多语言绑定。Selenium 2为下列程序提供驱动程序:
Mozilla Firefox
Google Chrome
Microsoft Internet Explorer
Opera
Apple iPhone
Android browsers
……
借助 Selenium 2,您可使用 Java、C#、Ruby、和 Python 编写测试。Selenium2还提供基于 HtmlUnit的无外设驱动,是用于测试 Web应用程序的 Java框架。HtmlUnit运行速度特别快,但它不是一个真正与真实浏览器相关联的驱动。
Selenium3.0
2016年7月,Selenium3.0悄悄发布第一个beta版。
Selenium 3.0 = Selenium 2.0 + Selenium RC(Remote Control)
去掉了对selenium rc的支持。
全面拥抱java 8。
支持macOS (Sierra or later),支持官方的safaridriver。
通过ms官方的webdriverserver支持Edge浏览器。
只支持ie 9.0版本以上。
通过Mozilla官方的geckodriver来支持firefox。
尽管firefox driver非常的稳定高效,但firefoxdriver毕竟是google实现的。geckodriver的出现是必然的结果,因为最新版本的firefox换引擎了,老的firefox driver应该是不支持新引擎的。
firefox driver究竟是什么?为什么你一次都没下载运行过? 这是因为firefox driver包含在selenium各语言版本的分发包里,使用webdriver启动firefox的时候,webdriver会为firefox安装firefox driver扩展。 |
•webdriver协议现在已经成为业内公认的浏览器ui测试的标准实现。webdriver协议是google对开源测试领域的重要贡献。
•各种官方支持意味着以后的浏览器ui测试的速度和稳定性会有较大的提升。
•浏览器ui自动化测试已经成为了行业标配。
• selenium专注web测试。
名称 |
网址 |
Maven |
http://maven.apache.org/ |
Selenium |
http://selenium-release.storage.googleapis.com/index.html |
Firefox |
http://ftp.mozilla.org/pub/firefox/releases/ |
Gecko driver |
https://github.com/mozilla/geckodriver/releases |
Chrome driver |
http://chromedriver.storage.googleapis.com/index.html https://npm.taobao.org/mirrors/chromedriver |
IE driver |
http://selenium-release.storage.googleapis.com/index.html |
Edge driver |
https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ |
Opera driver |
https://github.com/operasoftware/operachromiumdriver/releases |
Safari driver |
https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Introduction/Introduction.html |
PhantomJS driver |
http://phantomjs.org/ |
由于Selenium的限制,需要安装Java8。
32位或64位依系统而定,如果是64位系统则两个版本均可安装。
我们需要创建一个测试工程,但该工程需要依赖很多jar包,为了方便管理这些依赖,我们使用Maven进行管理。
下载Maven解压即可使用。
我们主要需要配置Maven本地库存放目录,以及主仓库地址,如:
|
注:上例使用的是阿里云的Maven仓库作为镜像站点,因为比较快一点。
安装Eclipse
(解压)安装Java或JavaEE版均可,但位数应与JDK匹配。
注:如果系统中JDK是绿色版,或其他注册表信息缺失,或安装有多个版本的JDK而不想使用默认的JDK启动Eclipse,可以在Eclipse根目录下找到配置文件eclipse.ini,并用文本编辑器打开,再在其中加入如下内容,指定用于启动的JDK:
-vm |
这样,应该就可以正常启动Eclipse了。
配置Maven
然后,我们需要在Eclipse中使用刚刚配置好的Maven。
如下图添加Maven配置,然后勾选使用该新配置。
然后,选择使用Maven的配置文件,如下图:
注意:选择了Maven配置文件后,下面的本地仓库路径也会自动改变。最好,点击下“Update Settings”更新下配置。
创建项目
要在项目中使用Maven需要创建Maven工程,因此,菜单中【New】à【Other…】,在如下弹出对话框选择“MavenProject”:
下一步,可自定义工程位置:
下一步,根据需要选择模板,通常测试选择“quickstart”就可以了。
下一步,填写参数:
完成创建,工程目录结构如下。
其中,pom.xml文件即是工程Maven配置文件,相关依赖也将在其中配置。(具体配置见下文)
待添加。
我们可以在Selenium的官网上下载其软件包,但由于我们使用Maven管理依赖,所以,我们只需要在工程pom.xml配置文件中加依赖即可。
<dependencies> … <dependency> <groupId>org.seleniumhq.seleniumgroupId> <artifactId>selenium-javaartifactId> <version>3.8.1version> </dependency> … </dependencies> |
如果我们使用JUnit编写测试用例,则需要在工程中增加其相关依赖:
<dependency> <groupId>junitgroupId> <artifactId>junitartifactId> <version>4.12version> <scope>testscope> </dependency> |
JUnit的使用这里就不赘述了。
待添加。
待添加。
待添加。
简单来说,我们总是从创建WebDriver开始的,并通过WebDriver导航页面和窗口,以及查找元素等等。
测试开始前,我们总是要实例化一个WebDriver对象,不同的浏览器构造方式不太一样。
但总体来说,我们需要根据具体测试的浏览器来创建XXXDriver对象。通常的问题在于,浏览器可执行文件和驱动文件的位置。通常,如果浏览器被安装在默认位置,则不需要在代码中指定;而驱动文件如果加入Path路径下,也不需要在代码中指定。此种情况下,就可以直接new XXXDriver()创建WebDriver对象。
但一些情况下,我们可能想要手动配置测试环境,这可以通过代码实现。
指定Firefox可执行文件和驱动文件路径最简单的方法是,设置系统属性:
System.setProperty("webdriver.gecko.driver", "…/geckodriver.exe"); System.setProperty("webdriver.firefox.bin", "…/firefox.exe"); |
当然,也可以使用GeckoDriverService来创建WebDriver:
FirefoxBinary bin = new FirefoxBinary(new File("…/firefox.exe")); GeckoDriverService service = new GeckoDriverService.Builder() .usingFirefoxBinary(bin) .usingDriverExecutable(new File("…/geckodriver.exe")) .build(); WebDriver driver = new FirefoxDriver(service); |
通常,我们常用的基本操作都从WebDriver开始。
设置浏览器的窗口大小
driver.manage().window().maximize();//最大化 driver.manage().window().setSize(new Dimension(480, 800)); |
导航到测试页面
driver.get( "http://www.baidu.com" ); //导航到页面(方法一) driver.navigate().to( "http://www.baidu.com" ); //导航到页面(方法二) |
“前进”和“后退”
driver.navigate().forward(); //前进 driver.navigate().back(); //后退 |
刷新
driver.navigate().refresh(); |
常用的元素查找方法有下面两种:
WebElement findElement(By by) java.util.List |
findElement()返回第一个查找到的元素,如果不存在则抛出异常;findElements()返回查找到的所有元素,如果不存在返回空列表。
需要注意两点:
Selenium推崇面向接口编程,因此,返回的元素都是接口WebElement;
WebElement也实现了查找接口,因此,也可以进行查找。
查找元素时,需要指定“定位器”,通常我们使用抽象类org.openqa.selenium.By的静态方法来创建所需的定位器。
By ID
通过ID查找。这是最高效也最常用的定位方式。但是,可能存在的陷阱是:网页中ID并非唯一或者ID是自动生成的。使用中应该避免这些情况。
WebElement element = driver.findElement(By.id("xxx")); |
By Name
通过name属性查找。
WebElement cheese = driver.findElement(By.name("cheese")); |
By XPath
通过XPath查找。WebDriver试图在任何地方使用浏览器原生XPath能力,但是对于原生不支持XPath的浏览器,将提供实现。这可能导致一些非预期的行为,除非你清楚不同XPath引擎间的差异。
Driver |
Tag and Attribute Name |
Attribute Values |
Native XPath Support |
HtmlUnit Driver |
Lower-cased |
As they appear in the HTML |
Yes |
Internet Explorer Driver |
Lower-cased |
As they appear in the HTML |
No |
Firefox Driver |
Case insensitive |
As they appear in the HTML |
Yes |
比如如下页面:
|
查找语句为:
List |
匹配结果为:
XPath expression |
HtmlUnit Driver |
Firefox Driver |
Internet Explorer Driver |
//input |
1 (“example”) |
2 |
2 |
//INPUT |
0 |
2 |
0 |
注意:有时元素不必声明某些有默认值的属性,比如input元素的type属性可以缺省,缺省值为text。但是在WebDriver中使用XPath时,你不能期望能够匹配未声明的默认属性。
By Class Name
通过class属性查找。实际使用中相同的class属性一般对应多个元素,所以一般会查找到多个元素,然后取用第一个元素。
List |
By Link Text
通过超链接文本查找超链接。
WebElement cheese = driver.findElement(By.linkText("cheese")); |
By Partial Link Text
通过部分超链接文本查找超链接。
WebElement cheese = driver.findElement(By.partialLinkText("cheese")); |
By Css Selector
通过Css Selector查找。如果浏览器默认原生支持css查询,可参阅w3c css selectors;否则Sizzle被使用。IE6,7和FF3.0都使用Sizzle作为css查询引擎。
注意并非所有的浏览器都是等效的,有的css在一个中起作用也许在另一个中不会。
WebElement cheese = driver.findElement(By.cssSelector("#food span.dairy.aged")); |
By TagName
通过元素标签名查找。
WebElement frame = driver.findElement(By.tagName("iframe")); |
WebElement接口定义了很多元素的通用操作,包括点击、输入、信息获取……,详见API文档。
有一些元素比较特殊,部分操作不在WebElement接口中定义,或借用某个不太直观的方法。下面列举一些比较特殊的用法。
文件域
输入文件路径:
file.sendKeys(filePath); |
下拉列表
对于下拉列表这种较复杂的元素,使用通用的方法来操作将不是那么高效的。我们可以将其封装为Select对象,这将提供更多有用的方法便于操作。
Select select = new Select(driver.findElement(By.tagName("select"))); select.selectByVisibleText(“***”); select.deselectByVisibleText(“***”); select.selectByValue(“***”); select.deselectByValue(“***”); select.deselectAll(); select.getAllSelectedOptions(); select.getFirstSelectedOption(); |
表单
一旦填写完表单,我们就期望提交它。一个提交的方法是找到“提交”按钮并点击它。
driver.findElement(By.id("submit")).click(); |
另外,元素都提供了一个方便的submit()方法。如果你调用一个处于表单中的元素的submit()方法,将沿DOM向上查找直到找到表单然后提交。如果元素不在表单中,则会抛出NoSuchElementException异常。
element.submit(); |
对话框
我们可以获取到各种弹出对话框的对象,并可以执行accept、dismiss、读取内容或者向prompt中输入内容等操作。
Alert alert = driver.switchTo().alert(); alert.accept(); alert.dismiss(); alert.getText(); alert. sendKeys(); |
扩展知识
需要理解的一点是,当浏览器打开一个页面时,可能存在弹出窗口,或者页面是框架集结构,或者包含有iframe。 这些情况下,Selenium的“焦点”是在主窗口的顶级文档之上的。 要操作主窗口顶级文档之外的元素,则需要转换“焦点”。 |
Selenium支持在命名窗口间切换:
driver.switchTo().window(nameOrHandle); |
怎样知道窗口的名字呢?查看一下打开它的JS或超链接:
另外,也可以使用窗口句柄,这可能需要迭代所有窗口句柄来获取某个窗口的句柄了:
for (String handle : driver.getWindowHandles()) { driver.switchTo().window(handle); } |
在框架间切换也是类似的:
driver.switchTo().frame("frameName");
WebElement xf = driver.findElement(by); driver.switchTo().frame(xf);
driver.switchTo().defaultContent(); |
//拖拽元素到目标元素中 new Actions( driver ).dragAndDrop( element, target ).perform(); // hover new Actions(driver).moveToElement( text ).perform(); |
sendKeys(Keys.CONTROL, "a"); //组合键 |
无条件等待
Sleeper.SYSTEM_SLEEPER.sleep(new Duration(5, TimeUnit.SECONDS)); |
等待有2种:显性等待和隐性等待。
显式等待
显式等待使用WebDriverWait类和ExpectedCondition接口完成,编码实现ExpectedCondition接口用以判断某种条件,然后等待这种条件达成或超时。
WebElement myDynamicElement = (new WebDriverWait(driver, 10)) .until(ExpectedConditions.presenceOfElementLocated(By.id("myDynamicElement"))); |
ExpectedConditions类提供了许多静态工厂方法,返回常用的ExpectedCondition对象。
隐式等待
隐式等待影响设置后全局的相关操作的等待时间。
//页面加载超时时间设置为5s。超时异常。 driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS); //定位对象超时时间设置为10s。超时NoSuchElement异常。 driver.manage().timeouts().implicitlyWait( 10, TimeUnit.SECONDS ); //异步脚本的超时时间设置为3s。 driver.manage().timeouts().setScriptTimeout(3, TimeUnit.SECONDS); |
测试过程中,可以把页面截图,保存成为图片。
需要注意,截取的图片存在临时目录,所以需要将其拷贝到指定的目录,否则测试完成可就找不到截图了。
File screenshotFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); FileUtils.copyFile( screenshotFile, new File("E:\\1.png") ); |
可以执行任意JS脚本来查找一个元素,并且只要返回一个DOM元素,它将被自动转换为WebElement对象。
WebElement element = (WebElement) ((JavascriptExecutor)driver).executeScript("return $('.cheese')[0]"); |
Cookie c1 = new Cookie("name", "key-aaaaaaa"); Cookie c2 = new Cookie("value", "value-bbbbbb"); driver.manage().addCookie(c1); driver.manage().addCookie(c2);
//获得 cookie Set //删除所有 cookie //driver.manage().deleteAllCookies(); |
下载文件时有个特殊的问题是,可能会弹出下载窗口,而该窗口属于系统窗口,超出了页面范畴,并且selenium也没有提供接口进行相关操作。
一种处理方案是通过类似autoIt这样的脚本对下载窗口进行处理,另一种方案是配置浏览器使其自动下载到指定目录。
当然,前一种方案已经超出了selenium的范畴,这里不进行讨论。我们来看第二种方案如何实现。
Firefox
FirefoxProfile profile = new FirefoxProfile(); profile.setPreference("browser.download.folderList", 2); profile.setPreference("browser.download.dir", "d:\\java"); profile.setPreference("browser.helperApps.neverAsk.saveToDisk", "binary/octet-stream"); |
以上代码设置了Firefox的首选项,它们分别代表:
browser.download.folderList
0 -下载到浏览器默认下载路径;
2 -保存到指定目录。
browser.download.dir
指定所下载文件的目录
browser.helperApps.neverAsk.saveToDisk
指定要下载页面的 Content-type值,“binary/octet-stream”为文件的类型。
Chrome
HashMap chromePrefs.put("profile.default_content_settings.popups", 0); chromePrefs.put("download.default_directory", downloadFilepath); ChromeOptions options = new ChromeOptions(); HashMap options.setExperimentalOption("prefs",chromePrefs); options.addArguments("--test-type"); DesiredCapabilities cap = DesiredCapabilities.chrome(); cap.setCapability(ChromeOptions.CAPABILITY, chromeOptionsMap); cap.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true); cap.setCapability(ChromeOptions.CAPABILITY, options); |
profile.default_content_settings.popups
0 -禁止弹出下载窗口
download.default_directory
文件下载路径
当调用WebElement.click()时,如果被操作元素是不可用的(disabled),click()方法调用仍会是正常的,不会有异常。
所以,如果会出现被点击的元素是不可用的情况,而点击不可用元素又被认为是非法的,那么应该在click()调用前使用isEnable()方法检查元素可用性。
在测试结束后通常需要关闭浏览器,这个操作通常使用close()或quit()方法完成。但是两者是有区别的,其方法说明也体现了这一点。
close() |
Close the current window, quitting the browser if it's the last window currently open. |
quit() |
Quits this driver, closing every associated window. |
对于close()而言,如果当前窗口是最后一个打开窗口,那么关闭当前窗口退出浏览器;quit()则关闭每个关联的窗口退出浏览器。
实际使用中发现,即使是只有最后一个窗口的情况下,调用两个方法还是有差异的。简单来说,close()通常不会关闭驱动进程,会使驱动进程驻留内存;而quit()方法会关闭驱动进程,不过总是会有个进程终止错误,但可忽略。总之,应尽量使用quit()来结束测试。
Gecko driver对Firefox的兼容性
Gecko驱动的发布页面会说明推荐的Fireofx版本
Note that with geckodrwer vO.19.O the following versions are recommended:
• Firefox 55.0 (and greater)
• Selenium 3.5 (and greater)
内置Webdriver对Firefox的兼容性
如果使用webdriver驱动,则需要在selenium-server-standalone-x.x.x.jar!\org\openqa\selenium\firefox\webdriver.xpi!\install.rdf
注:selenium3.8.1已不再使用Webdrver驱动,而转用Gecko驱动,但这些文件还是保留的。
htmlunit
selenium内置无头浏览器内核,纯Java编写,页面渲染能力可能与预期不一致。
PhantomJS
使用 QtWebKit浏览器内核渲染页面,基本可以和真正浏览器保持一致。
Chrome-headless
Google针对 Chrome浏览器 59版新增加的一种模式,可以让你不打开UI界面的情况下使用 Chrome浏览器,所以运行效果与 Chrome保持完美一致。
ChromeOptions chromeOptions = new ChromeOptions(); chromeOptions.addArguments("--headless"); WebDriver driver = new ChromeDriver(chromeOptions); |
chromedriver版本 |
支持的Chrome版本 |
v2.34 |
v61-63 |
v2.33 |
v60-62 |
v2.32 |
v59-61 |
v2.31 |
v58-60 |
v2.30 |
v58-60 |
v2.29 |
v56-58 |
v2.28 |
v55-57 |
v2.27 |
v54-56 |
v2.26 |
v53-55 |
v2.25 |
v53-55 |
v2.24 |
v52-54 |
v2.23 |
v51-53 |
v2.22 |
v49-52 |
v2.21 |
v46-50 |
v2.20 |
v43-48 |
v2.19 |
v43-47 |
v2.18 |
v43-46 |
v2.17 |
v42-43 |
v2.13 |
v42-45 |
v2.15 |
v40-43 |
v2.14 |
v39-42 |
v2.13 |
v38-41 |
v2.12 |
v36-40 |
v2.11 |
v36-40 |
v2.10 |
v33-36 |
v2.9 |
v31-34 |
v2.8 |
v30-33 |
v2.7 |
v30-33 |
v2.6 |
v29-32 |
v2.5 |
v29-32 |
v2.4 |
v29-32 |
参考文档:
https://www.w3.org/TR/webdriver/
https://www.w3.org/testing/Activity
https://github.com/w3c/webdriver