diff --git a/src/university/105.md b/src/university/105.md new file mode 100644 index 0000000..2502116 --- /dev/null +++ b/src/university/105.md @@ -0,0 +1,1151 @@ +!> 转载链接:[selenium 使用教程详解-java版本](https://www.cnblogs.com/tester-ggf/p/12602211.html) + + + +## Selenium 概述 + +### Selenium 发展史 + + Selenium是一系列基于Web的自动化工具,提供一套测试函数,用于支持Web自动化测试。函数非常灵活,能够完成界面元素定位、窗口跳转、结果比较。具有如下特点: + +- 多浏览器支持 + - 如IE、Firefox、Safari、Chrome、Android手机浏览器等。 +- 支持多语言 + - 如Java、C#、Python、Ruby、PHP等。 +- 支持多操作系统 + - 如Windows、Linux、IOS、Android等。 +- 开源免费 + - 官网:http://www.seleniumhg.org/ + +Selenium框架由多个工具组成,包括:Selenium IDE,Selenium RC,Selenium WebDriver和SeleniumRC。 + +发展到如今 Selenium 已经发布到了 3.0 版本了,以下是简单总结发展过程以及一些变化: + +- **Selenium 1.0** + +Jason Huggins在2004年发起的 Selenium 项目,使用 JavaScript 编写的一个类库,这个 JavaScript 类库就是Selenium core,同时也是seleniumRC、Selenium IDE的核心组件。Selenium由此诞生。 + +![image-20211202161830598](Selenium使用教程—Java.assets/image-20211202161830598.png) + +- **Selenium 2.0** + +因为Selenium和Webdriver的合并,所以,Selenium 2.0由此诞生。 + +简单用公式表示为:Selenium 2.0 = Selenium 1.0 + WebDriver + +需要强调的是,在Selenium 2.0中主推的是WebDriver,可以将其看作Selenium RC的替代品。因为Selenium为了保持向下的兼容性,所以在Selenium 2.0中并没有彻底地抛弃Selenium RC。 + +所以,我们在学习Selenium2.0的时候,核心是学习WebDriver。它的工作原理是这样的: + +![image-20211202161915574](Selenium使用教程—Java.assets/image-20211202161915574.png) + +- **Selenium 3.0** + + Selenium 3.0做了一些不大不小的更新: + + - 1、终于去掉了RC,简单用公式表示为:Selenium 3.0 = Selenium 2.0 - Selenium RC(Remote Control) + - 2、Selenium3.0只支持Java8版本以上。 + - 3、Selenium3.0中的Firefox浏览器驱动独立了,以前装完selenium2就可以驱动Firefox浏览器了,现在和Chrome一样,必须下载和设置浏览器驱动。 + - 4、MAC OS 集成Safari的浏览器驱动。默认在/usr/bin/safaridriver 目录下。 + - 5、只支持IE 9.0版本以上。 + +### Selenium WebDriver原理 + +将 WebDriver 驱动浏览器类比成开出租车的场景。 + +在开出租车时有三个角色: + +- **乘客:**他/她告诉出租车司机去哪里,大概怎么走。 +- **出租车司机:**他按照乘客的要求来操控出租车。 +- **出租车:**出租车按照司机的操控完成真正的行驶,把乘客送到目的地。 + +![image-20211202162100830](Selenium使用教程—Java.assets/image-20211202162100830.png) + +在WebDriver中也有类似的三个角色: + +- **工程师写的自动化测试代码**:自动化测试代码发送请求给浏览器的驱动(比如火狐驱动、谷歌驱动) +- **浏览器的驱动**:它来解析这些自动化测试的代码,解析后把它们发送给浏览器 +- **浏览器**:执行浏览器驱动发来的指令,并最终完成工程师想要的操作。 + +![image-20211202162129084](Selenium使用教程—Java.assets/image-20211202162129084.png) + +## Selenium 环境搭建 + +### java 环境 + +想要通过 java 语言来使用 selenium 框架,前提要完成 jdk 环境的安装。 + +详细安装教程见:https://blog.csdn.net/shengmer/article/details/78836255 + +### selenium 环境 + +**简单 java 工程:** + +直接导入 selenium的 jar 包就可以了。 + +jar包下载地址:[ Selenium各个版本jar下载](https://npm.taobao.org/mirrors/selenium) , 下载对应的版本即可 + +**maven工程:** + +在pom文件中引入对应的依赖即可: + +maven仓库:https://mvnrepository.com/ + +```xml + + org.seleniumhq.selenium + selenium-java + 3.4.0 + +``` + +### selenium3 对应浏览器驱动下载 + +当selenium升级到3.0之后,对不同的浏览器驱动进行了规范。如果想使用selenium驱动不同的浏览器,必须单独下载并设置不同的浏览器驱动。 +各浏览器下载地址: + +- Firefox浏览器驱动:[geckodriver](https://github.com/mozilla/geckodriver/releases) + +- Chrome浏览器驱动:[chromedriver](https://sites.google.com/a/chromium.org/chromedriver/home)[taobao备用地址](https://npm.taobao.org/mirrors/chromedriver) + +- IE浏览器驱动:[IEDriverServer](http://selenium-release.storage.googleapis.com/index.html) + +- Edge浏览器驱动:[MicrosoftWebDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) + +- Opera浏览器驱动:[operadriver](https://github.com/operasoftware/operachromiumdriver/releases) + +- PhantomJS浏览器驱动:[phantomjs](http://phantomjs.org/) + + 注:部分浏览器驱动地址需要梯子。 + +**设置浏览器驱动** + +设置浏览器的地址非常简单。 我们可以手动创建一个存放浏览器驱动的目录,如: C:\driver , 将下载的浏览器驱动文件(例如:chromedriver、geckodriver)丢到该目录下。 +我的电脑–>属性–>系统设置–>高级–>环境变量–>系统变量–>Path,将“C:\driver”目录添加到Path的值中。 + +也可以通过设置`System.setProperty("webdriver.chrome.driver", "YOUR_DRIVER_PATH");`进行设置 + +**验证浏览器驱动** + +验证不同的浏览器驱动是否正常使用。 + +```java +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.edge.EdgeDriver; +import org.openqa.selenium.ie.InternetExplorerDriver; +import org.openqa.selenium.opera.OperaDriver; +import org.openqa.selenium.phantomjs.PhantomJSDriver; + +// Chrome浏览器 +System.setProperty("webdriver.chrome.driver", "YOUR_DRIVER_PATH"); +WebDriver driver = new ChromeDriver(); + +// Firefox浏览器 +WebDriver driver = new FirefoxDriver(); + +// Edge浏览器 +System.setProperty("webdriver.edge.driver", "YOUR_DRIVER_PATH"); +WebDriver driver = new EdgeDriver(); + +// Internet Explorer浏览器 +WebDriver driver = new InternetExplorerDriver(); + +// Opera浏览器 +WebDriver driver = new OperaDriver(); + +// PhantomJS +WebDriver driver = new PhantomJSDriver(); +``` + +## Selenium 简单示例 + +- **打开百度进行搜索:** + +```java +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; + +/** + * @Description: 通过selenium操作浏览器打开百度进行搜索 + * selenium版本:3.12.0; 通过maven管理jar包 + * 开发工具:IDEA + * jdk:1.8 + * 浏览器:chrome 75+ + * @Author: ggf + * @Date: 2020/03/22 + */ +public class BaiduSearch { + public static void main(String[] args) { + // 1.创建webdriver驱动 + WebDriver driver = new ChromeDriver(); + // 2.打开百度首页 + driver.get("https://www.baidu.com"); + // 3.获取输入框,输入selenium + driver.findElement(By.id("kw")).sendKeys("selenium"); + // 4.获取“百度一下”按钮,进行搜索 + driver.findElement(By.id("su")).click(); + // 5.退出浏览器 + driver.quit(); + } +} +``` + +## 八大元素定位 + +- **为什么要进行元素定位?** + + 我们必须告诉 selenium 怎么去定位元素,用来模拟用户的动作,或者查看元素的属性和状态,以便于我们可以执行检查。例如:我们要搜索一个产品,首先要找到搜索框与搜索按钮,接着通过键盘输入要查询的关键字,最后鼠标单击搜索按钮,提交搜索请求。 + + 正如上述的人工操作步骤一样,我们也希望 selenium 能模拟这样的动作,然而,selenium 并不能理解类似在搜索框中输入关键字或者点击搜索按钮这样的图形化的操作。所以需要我们程序化的告诉 selenium 如何定位搜索框和搜索按钮,从而模拟键盘和鼠标的操作。 + +### 定位方式 + +**selenium 提供了8种的定位方式:** + +- id +- name +- class name +- tag name +- link text +- partial link text +- xpath +- css selector + +这8种定位方式在java selenium 中对应的方法为: + +| 方法 | 描述 | 参数 | 示例 | +| --------------------------------- | ------------------------------------------ | --------------- | ------------------------------------------ | +| findElement(By.id()) | 通过元素的 id 属性值来定位元素 | 对应的id属性值 | findElement(By.id("kw")) | +| findElement(By.name()) | 通过元素的 name 属性值来定位元素 | 对应的name值 | findElement(By.name("user")) | +| findElement(By.className()) | 通过元素的 class 名来定位元素 | 对应的class类名 | findElement(By.className("passworld")) | +| findElement(By.tagName()) | 通过元素的 tag 标签名来定位元素 | 对应的标签名 | findElement(By.tagName("input")) | +| findElement(By.linkText()) | 通过元素标签对之间的文本信息来定位元素 | 文本内容 | findElement(By.linkText("登录")) | +| findElement(By.partialLinkText()) | 通过元素标签对之间的部分文本信息来定位元素 | 部分文本内容 | findElement(By.partialLinkText("百度")) | +| findElement(By.xpath()) | 通过xpath语法来定位元素 | xpath表达式 | findElement(By.xpath("//input[@id='kw']")) | +| findElement(By.cssSelector()) | 通过css选择器来定位元素 | css元素选择器 | findElement(By.cssSelector("#kw")) | + +同时这8种方法都对应有着返回复数元素的方法,分别在调用的方法findElements(By.id()) 加上一个s: + +- findElements(By.id()) +- findElements(By.name()) +- findElements(By.className()) +- findElements(By.tagName()) +- findElements(By.linkText()) +- findElements(By.partialLinkText()) +- findElements(By.xpath()) +- findElements(By.cssSelector()) + +### 定位方式的用法 + +假如我们有一个Web页面,通过前端工具(如,Firebug)查看到一个元素的属性是这样的。 + +```html + + + +
新闻 +hao123 +``` + +- 通过linkText定位: + +```java +driver.findElement(By.linkText("新闻") +driver.findElement(By.linkText("hao123") +``` + +- 通过 partialLinkText 定位: + +```java +driver.findElement(By.partialLinkText("新") +driver.findElement(By.partialLinkText("hao") +driver.findElement(By.partialLinkText("123") +``` + +### xpath进阶-轴定位 + +- parent::div 上层父节点,你那叫div的亲生爸爸,最多有一个; +- child::div 下层所有子节点,你的所有亲儿子中叫div的; +- ancestor::div 上面所有直系节点,是你亲生爸爸或者你亲爹或者你亲爹的爸爸中叫div的; +- descendant::div 下面所有节点,你的后代中叫div的,不包括你弟弟的后代; +- following::div 自你以下页面中所有节点叫div的; +- following-sibling::div 同层下节点,你所有的亲弟弟中叫div的; +- preceding::div 同层上节点,你所有的亲哥哥以及他们的后代中叫div的; +- preceding-sibling::div 同层上节点,你所有的亲哥哥中叫div的; + +轴定位详细操作可参考:https://www.cnblogs.com/wangyi0419/p/11638652.html +xpath 多条件组合查找:https://blog.csdn.net/li575098618/article/details/47853949 + +关于 xpaht 和 css 的定位比较复杂,请参考: [xpath语法](http://www.w3school.com.cn/xpath/xpath_syntax.asp)、 [css选择器](http://www.w3school.com.cn/cssref/css_selectors.asp) + +## Selenium API + +### WebDriver 常用 API + +WebDriver 提供了一系列的 API 来和浏览器进行交互,如下: + +| 方法 | 描述 | +| ------------------ | --------------------------- | +| get(String url) | 访问目标 url 地址,打开网页 | +| getCurrentUrl() | 获取当前页面 url 地址 | +| getTitle() | 获取页面标题 | +| getPageSource() | 获取页面源代码 | +| close() | 关闭浏览器当前打开的窗口 | +| quit() | 关闭浏览器所有的窗口 | +| findElement(by) | 查找单个元素 | +| findElements(by) | 查到元素列表,返回一个集合 | +| getWindowHandle() | 获取当前窗口句柄 | +| getWindowHandles() | 获取所有窗口的句柄 | + +### WebElement 常用 API + + 通过 WebElement 实现与网站页面上元素的交互,这些元素包含文本框、文本域、按钮、单选框、div等,WebElement提供了一系列的方法对这些元素进行操作: + +| 方法 | 描述 | +| --------------------------- | ------------------------------------------ | +| click() | 对元素进行点击 | +| clear() | 清空内容(如文本框内容) | +| sendKeys(...) | 写入内容与模拟按键操作 | +| isDisplayed() | 元素是否可见(true:可见,false:不可见) | +| isEnabled() | 元素是否启用 | +| isSelected() | 元素是否已选择 | +| getTagName() | 获取元素标签名 | +| getAttribute(attributeName) | 获取元素对应的属性值 | +| getText() | 获取元素文本值(元素可见状态下才能获取到) | +| submit() | 表单提交 | + +> 代码示例 + +```java +public class BaiduSearch { + public static void main(String[] args) { + // 1.创建webdriver驱动 + WebDriver driver = new ChromeDriver(); + // 2.打开百度首页 + driver.get("https://www.baidu.com"); + + // 获取搜索框元素 + WebElement inputElem = driver.findElement(By.id("kw")); + + // clear()方法,清空输入框内容 + inputElem.clear(); + + // sendKeys()方法,在搜索框中输入搜索内容 + inputElem.sendKeys("selenium"); + + // 元素是否显示 + boolean displayed = inputElem.isDisplayed(); + System.out.println(displayed); // 输出true + + // 元素是否启用 + boolean enabled = inputElem.isEnabled(); + System.out.println(enabled); // 输出true + + // 判断元素是否被选中状态,一般用在Radio(单选),Checkbox(多选),Select(下拉选) + // 在输入框中使用无意义 + boolean selected = inputElem.isSelected(); + System.out.println(selected); // 输出fasle + + // 获取标签名 + String tagName = inputElem.getTagName(); + System.out.println(tagName); // 输出input + + // 获取属性名(name属性) + String name = inputElem.getAttribute("name"); + System.out.println(name); // 输出wd + + // 获取文本值 + String text = inputElem.getText(); + System.out.println(text); // 输出selenium + + // 通过submit提交 + driver.findElement(By.id("su")).submit(); + + // click()方法,点击百度一下按钮 + driver.findElement(By.id("su")).click(); + + // 退出浏览器 + driver.quit(); + } +} +``` + +## 元素等待机制 + + 在对元素进行定位时,有时候网页加载时间比较长,元素还没有加载出来,这个时候去查找这个元素的话程序中就会抛出异常,所以我们在编写代码时需要考虑延时问题,在selenium中有几种延时机制可以使用如下: + +### 硬性等待 + + 硬性等待就是不管你浏览器元素是否加载完成,都要进行等待设置好的时间,利用 java 语言中的线程类 Thread 中的 sleep 方法,进行强制等待。 + +``` +Thread.sleep(long millis) 该方法会让线程进行休眠。 +``` + +如:Thread.sleep(3000) 表示程序执行的线程暂停 3 秒钟。 + + 这种方法在一定的程度上是可以解决元素加载过慢的情况,但是不建议使用该方法,因为一般情况下我们无法判断网页到底需要多长时间加载完成,如果我们设置的时间过长,非常影响效率。 + +### 隐式等待 + + 隐式等待的理解,就是我们通过代码设置一个等待时间,如果在这个等待时间内,网页加载完成后就执行下一步,否则一直等待到时间截止。 + +代码表示: + +``` +driver.manage.timeouts.implicitlyWait(long time, TimeUtil unit); +``` + + 这种方法相对于硬性等待显的会灵活一点,但是隐式等待也有个弊端,因为这个设置是全局的,程序需要等待整个页面加载完成,直到超时,有时候我需要找的那个元素早就加载完成了,只是页面上有个别其他元素加载比较慢,程序还是会一直等待下去。直到所有的元素加载完成在执行下一步。 + +### 显式等待 + + 显示等待是等待指定元素设置的等待时间,在设置时间内,默认每隔0.5s检测一次当前的页面这个元素是否存在,如果在规定的时间内找到了元素则执行相关操作,如果超过设置时间检测不到则抛出异常。默认抛出异常为:NoSuchElementException。推荐使用显示等待。 + +代码表示: + +```java +WebDriberWait wait = new WebDriverWait(dirver, timeOutInSeconds); +wait.nutil(expectCondition); +``` + +具体使用案例: + +1.查找元素是否已经加载出来 + +```java +WebDriverWait wait = new WebDriverWait(driver, 5); +// 查找id为“kw"的元素是否加载出来了(已经在页面DOM中存在) +wait.until(ExpectedConditions.presenceOfElementLocated(By.id("kw"))); + +// 在设定时间内找到后就返回,超时直接抛异常 +``` + +2.查找元素是否可见 + +```java +WebDriverWait wait = new WebDriverWait(driver, 5); +// 查找id为"kw"的元素是否可见 +wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("kw"))); +``` + +3.查找元素是否可点击 + +```java +WebDriverWait wait = new WebDriverWait(driver, 5); +// 查找id为"kw"的元素是否可以点击 +wait.until(ExpectedConditions.elementToBeClickable(By.id("kw"))); +``` + +4.自定义方法,重写ExpectedCondition中的apply方法 + +```java +/* +自定义查找元素的方法,对元素查找方法进行二次封装,更加的灵活,可以加上自己逻辑。 +*/ +public WebElement getElement(long timeOutInSecond, By by) { + WebDriverWait wait = new WebDriverWait(driver, timeOutInSecond); + WebElement element = wait.until(new ExpectedCondition() { + @NullableDecl + @Override + public WebElement apply(@NullableDecl WebDriver webDriver) { + return webDriver.findElement(by); + } + }); + + return element; + } +``` + +#### ExpectedConditions类中常用方法 + +| 方法 | 描述 | +| ------------------------------------------------------ | ------------------------------------------------------------ | +| presenceOfElementLocated(By locator) | 判断某个元素是否被加到了dom树里,并不代表该元素一定可见; | +| visibilityOfElementLocated(By locator) | 判断某个元素是否可见(代表元素非隐藏,元素的宽和高都不等于0); | +| elementToBeClickable(By locator) | 判断某个元素中是否可见并且是enable的且可点击; | +| elementToBeSelected(By locator) | 判断某个元素是否被选中了,一般用在下拉列表; | +| alertIsPresent() | 判断页面上是否存在alert; | +| titleIs(String title) | 判断当前页面的title是否精确等于预期; | +| titleContains(String title) | 判断当前页面的title是否包含预期字符串; | +| textToBePresentInElement(By locator, String text) | 判断某个元素中的text是否包含了预期的字符串; | +| textToBePresentInElementValue(By locator, String text) | 判断某个元素中的value属性是否包含了预期的字符串; | +| invisibilityOfElementLocated(By locator) | 判断某个元素中是否不存在于dom树或不可见; | +| frameToBeAvailableAndSwitchToIt(By) | 判断iframe可用,并且切换到iframe中 | + +### 页面加载超时设置 + +通过TimeOuts 对象进行全局页面加载超时的设置,该设置必须放置get 方法之前。如下代码: + +```java +driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS); +driver.get("https://www.baidu.com"); +``` + +如果百度首页在超过5秒钟没有加载完毕,程序就会抛出异常,如果在 2秒就加载完了,就直接往下执行,如果需要对页面加载时间有要求的,可以用这个设置进行检验。 + +## 特殊元素操作 + +### 弹出框处理(alert、confirm) + +操作alert、confirm弹出框,可以通过Alert 对象来进行操作,Alert类包含了确认、取消、输入和获取弹出窗内容。 + +Alert对应属性和方法: + +| 方法 | 描述 | +| ------------------------ | ------------------------------------ | +| Alert.getText() | 获取弹出框内容。 | +| Alert.accept() | 接受弹窗的提示,相当于点击确认按钮。 | +| Alert.dismiss() | 取消提示窗。 | +| Alert.sendKeys(String s) | 给弹窗输入内容。 | + +简单使用示例: + +```java +// 首先需要切换到弹出框中,获取Alert对象。 +Alert alert = driver.switchTo().alert(); +// 获取弹窗文本内容 +alert.getText(); +// 点击确定按钮 +alert.accept(); +// 点击取消按钮 +alert.dismiss(); +``` + +注:如果弹出框不是 js 原生的 alert 弹窗,我们还是按照原来的获取元素的方法。 + +### iframe 切换 + +有时候我们定位元素的时候,发现怎么都定位不了。 这时候你需要查一查你要定位的元素是否在iframe里面。 + +**什么是iframe?** + +iframe 就是HTML 中,用于网页嵌套网页的。 一个网页可以嵌套到另一个网页中,可以嵌套很多层。 + +例如: + +**main.html** + +```html + + + FrameTest + + +
this is main page's div!
+ +
+ + + +``` + +**frame.html** + +```html + + + this is a frame! + + +
this is iframes div,
+ + + +``` + +使用selenium 操作浏览器时,如果需要操作iframe中的元素,首先需要切换到对应的内联框架中。 + +selenium 给我们提供了三个重载的方法,进行操作iframe; + +**切换方法:** + +```java +// 方法一:通过 iframe的索引值,在页面中的位置 +driver.switchTo().frame(index); +// 方法二:通过 iframe 的name 或者id +driver.switchTo().frame(nameOrId); +// 方法三:通过iframe 对应的webElement +driver.switchTo().frame(frameElement); +``` + +**selenium 代码:** + +```java +public static void testIframe(WebDriver driver){ + // 在 主窗口的时候 + driver.findElement(By.id("maininput")).sendKeys("main input"); + // 此时 没有进入到iframe, 以下语句会报错 + //driver.findElement(By.id("iframeinput")).sendKeys("iframe input"); + + driver.switchTo().frame("frameA"); + driver.findElement(By.id("iframeinput")).sendKeys("iframe input"); + + // 此时没有在主窗口,下面语句会报错 + //driver.findElement(By.id("maininput")).sendKeys("main input"); + + // 回到主窗口 + driver.switchTo().defaultContent(); + driver.findElement(By.id("maininput")).sendKeys("main input"); +} +``` + +注:如果已经切换进入了其中的一个 iframe 中,再想对 iframe 外的元素进行操作,需要切换回到默认的页面中,否则会找不到元素。 + +```java +// 切换到默认内容页面 +driver.switchTo().defaultContent(); +``` + +### 浏览器窗口的切换 + + 有时候后在操作浏览器,可能打开了一个新的窗口,这个时候如果要对新窗口的元素进行操作,需要切换到新窗口中去,怎么去切换呢?在 selenium 中有个叫**句柄**的概念。 + + 什么是**句柄**,简单理解就是浏览器窗口的一个标识,浏览器打开的每个窗口都有唯一的一个标识,也就是句柄,我们可以通过句柄来进行窗口之间的切换,从而来达到我们操作不同窗口的元素。 + +WebDriver 中提供了两个 API 来获取窗口的相关句柄: + +```java +// 获取当前窗口的句柄 +String handle = driver.getWindowHandle(); +// 获取所有窗口的句柄,返回一个集合 +Set handles = driver.getWindowHandles(); +``` + +获取到句柄后,通过对应的方法进行切换: + +```java +// 切换到窗口 +driver.switchTo.windwo(String handle); +``` + +多窗口之间的切换方法: + +```java +/** +* 切换窗口的方法 +* 通过传入一个标题来找到我们需要的窗口。 +* @param title 窗口的标题 +*/ +public void switchWindow(String title){ + Set handles = driver.getWindowHandles(); + // 切换窗口的方式--循环遍历handles集合 + for (String handle : handles) { + //判断是哪一个页面的句柄??--根据什么来判断???title + if(driver.getTitle().equals(title)){ + break; + }else{ + //切换窗口--根据窗口标识来切换 + driver.switchTo().window(handle); + } +} +``` + +### select 下拉框处理 + +如果一个页面元素是一个下拉框(select),对应下拉框的操作,selenium有专门的类 Select 进行处理。其中包含了单选和多选下拉框的各种操作,如获得所有的选项、选择某一项、取消选中某一项、是否是多选下拉框等。 + +**Select类常用的一些方法:** + +| 方法 | 说明 | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| void [deselectAll](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#deselectAll--)() | 取消所有选择项,仅对下拉框的多选模式有效,若下拉不支持多选模式,则会抛出异常 UnsupportedOperationException(不支持的操作) | +| void [deselectByIndex](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#deselectByIndex-int-)(int index) | 取消指定index的选择,index从零开始,仅对多选模式有效,否则抛出异常 UnsupportedOperationException(不支持的操作) | +| void [deselectByValue](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#deselectByValue-java.lang.String-)(String value) | 取消Select标签中,value为指定值的选择,仅对多选模式有效,否则抛出异常 UnsupportedOperationException(不支持的操作) | +| void [deselectByVisibleText](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#deselectByVisibleText-java.lang.String-)(String Text) | 取消项的文字为指定值的项,例如指定值为Bar,项的html为,仅对多选模式有效,单选模式无效,但不会抛出异常 | +| List`getAllSelectedOptions()` | 获得所有选中项,单选多选模式均有效,但没有一个被选中时,返回空列表,不会抛出异常 | +| WebElement `getFirstSelectedOption()` | 获得第一个被选中的项,单选多选模式均有效,当多选模式下,没有一个被选中时,会抛出[NoSuchElementException](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/NoSuchElementException.html)异常 | +| List`getOptions()` | 获得下拉框的所有项,单选多选模式均有效,当下拉框没有任何项时,返回空列表,不会抛出异常 | +| boolean `isMultiple()` | 判断下拉框是否多选模式 | +| void [selectByIndex](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#selectByIndex-int-)(int index) | 选中指定index的项,单选多选均有效,当index超出范围时,抛出[NoSuchElementException](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/NoSuchElementException.html)异常 | +| void [selectByValue](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#selectByValue-java.lang.String-)(String value) | 选中所有Select标签中,value为指定值的所有项,单选多选均有效,当没有适合的项时,抛出[NoSuchElementException](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/NoSuchElementException.html)异常 | +| void [selectByVisibleText](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#selectByVisibleText-java.lang.String-)(String text) | 选中所有项的文字为指定值的项,与deselectByValue相反,但单选多选模式均有效,当没有适合的项时,抛出[NoSuchElementException](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/NoSuchElementException.html)异常 | + +**示例:2345网址导航首页的城市省份切换。** + +1.进入2345.com首页,点击头部【切换】进行城市切换,我们切换省份为北京。 + +[![img](https://pic.downk.cc/item/5e8079f1504f4bcb04461282.png)](https://pic.downk.cc/item/5e8079f1504f4bcb04461282.png) + +2.HTML页面DOM结构。 + +[![img](https://pic.downk.cc/item/5e8079f1504f4bcb04461285.png)](https://pic.downk.cc/item/5e8079f1504f4bcb04461285.png) + +3.代码编写,这里需要注意下拉选是在一个iframe中,需要先切换到这个iframe后再操作。 + +```java + // 创建驱动 + WebDriver driver = new ChromeDriver(); + // 打开2345网站 + driver.get("https://www.2345.com"); + // 切换城市 + driver.findElement(By.linkText("切换")).click(); + // 切换到iframe内联框架中 + driver.switchTo().frame("city_set_ifr"); + // 定位到省份下拉框 + WebElement province = driver.findElement(By.id("province")); + province.click(); + // 创建Select对象 + Select select = new Select(province); + // 根据文本来获取下拉值 + select.selectByVisibleText("B 北京"); + driver.quit(); +``` + +### 带 readonly 属性的元素操作 + + 标签元素如果带有 readonly 属性,表示只读不能进行编辑,如果我们需要操作这样的元素,需要把这个 readonly 属性进行移除后,再进行操作。删除标签属性的话,webdriver 没有对应的 API,我们使用 JavaScript 脚本来进行操作。 + +**示例:12306 网站购票页面日期。** + +[![img](https://pic.downk.cc/item/5e80841d504f4bcb044e452b.png)](https://pic.downk.cc/item/5e80841d504f4bcb044e452b.png) + +selenium 代码实现: + +```java +// 创建驱动 +WebDriver driver = new ChromeDriver(); +// 打开12306网站 +driver.get("https://www.12306.cn/index/"); +// 通过js来移除readonly属性 +String removeAttr = "document.getElementById('train_date').removeAttribute('readonly');"; +// 执行js +((JavascriptExecutor)driver).executeScript(removeAttr); +// 获取日期日历输入框 +WebElement train_date = driver.findElement(By.id("train_date")); +// 清除原来的值 +train_date.clear(); +// 输入内容 +train_date.sendKeys("2020-03-30"); +driver.quit(); +``` + +### 日期控件操作 + +对于页面中出现时间控件选择时,一般分为两种: + +(1)控件没有限制手动填写的,我们直接使用 sendKeys() 方法进行赋值即可。 + +```java +driver.findElement(By).sendKeys("2020-03-30"); +``` + +(2)控件限制了手动输入的,只能通过点击控件时间进行输入的,我们就需要使用 js 脚本进行操作了。 + +```java +// 获取js执行器 +JavaScriptExecutor js = (JavaScriptExecutor)driver; +// 对时间输入框进入赋值 +String script = "document.getElementById('xxx').value='2020-03-30';"; +// 执行 +js.executeScript(script); +``` + +注:需要注意的是,不管使用哪种方式进行时间的赋值,一点要注意输入时间的格式是否符合系统的要求; + +### 文件上传 + +对于通过input标签实现的上传功能,可以将其看作是一个输入框,即通过sendKeys()指定本地文件路径的方式实现文件上传。 + +创建upfile.html文件,代码如下: + +```html + + + +upload_file + + + +
+
+

upload_file

+ +
+
+ +