在熟悉了元素定位之后,我们接下来就要学习对定位到的元素进行操作这项内容了。我简要做了个总结,如下图:
元素的基本操作有很多,常用的有三个:click(点击)、clear(清空)、sendkeys(输入内容)、submit(提交)。何为基本操作,即这些方法在WebElement
接口类中定义,通过实例化的WebElement
直接调用。
void click()
:单击目标元素。void submit()
:提交当前form
(表单)内容到远程服务器,注意是特定于表单元素而言的。void sendKeys(CharSequence... keysToSend)
:使用此方法模拟键入元素,这可能会设置元素的值。一般是对文本输入元素进行此操作,否则会报错org.openqa.selenium.ElementNotInteractableException: element not interactable
void clear()
:如果此元素是文本输入元素(INPUT
型元素和TEXTAREA
元素),则将清除该值。对其他元素没有影响。此方法并不会触发鼠标和键盘操作。String getTagName()
:获取并返回此元素的tagName
(String
类型)。String getAttribute(String name)
:获取并返回当前元素的给定属性的值(String
类型)。boolean isSelected()
:确定是否选择了此元素。此操作仅适用于输入元素,如checkboxes
(复选框)、options in a select
(下拉选择框中的选项) 和 radio buttons
(单选框按钮)boolean isEnabled()
:元素当前是否已启用?除了禁用的输入元素之外的内容,通常都会返回true
。String getText()
:获取此元素及子元素的可见(即不被CSS隐藏)内文本,不带任何前导或尾随空格。boolean isDisplayed()
:是否显示此元素?Point getLocation()
:获取并返回此元素的左上角在页面上的位置(以一组x,y
轴坐标值表示)。Dimension getSize()
:获取并返回此元素的高度和宽度(一对整型像素值,如332,450)Rectangle getRect()
:呈现元素的位置和大小。String getCssValue(String propertyName)
:获取并返回此元素给定CSS
属性的值(String
类型)select
,即下拉选择框,这类元素的操作一般是选中select
中的某一项,selenium
中的Select
类提供了很多对select
元素的操作方法。
首先,从Select
类的有参构造函数可以看出,在初始化一个select
时,需要WebElement
作为入参。接下来,我们看看,Select
类提供了哪些函数吧。
首先,我们最关心的当然是,下拉选择的相关方法,其中单选3个方法:
void selectByVisibleText(String text)
:使用可见文本来选中某一项void selectByIndex(int index)
:使用索引来选中某一项void selectByValue(String value)
:使用value
来选中某一项相对应的,多选也有4个方法(当然多选需要select
标签的multiple
属性的值为multiple
):
void deselectAll()
:选中所有项void deSelectByVisibleText(String text)
:使用可见文本来选中某一项。void deSelectByIndex(int index)
:使用索引来选中某一项。void deSelectByValue(String value)
:使用value
来选中某一项。另外,还提供一些其他方法:
boolean isMultiple()
:此select
元素的multiple
属性的值是否为multiple
?List getOptions()
:返回此select
元素所有的项。List getAllSelectedOptions()
:返回此select
元素所有选中状态的项的集合。WebElement getFirstSelectedOption()
:返回此select
元素第一个选中状态的项。setSelected(WebElement option, boolean select)
:使得此select
元素的某项被点击(从而被选中),这个方法是下拉选择的相关方法的核心实现方法。下面用一个实例演示一下select
的操作。
示例代码-select-1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UI自动化测试页面</title>
</head>
<body>
<div id="div">
<select id="city" multiple="multiple">
<option value="">请选择一个城市</option>
<option value="010" selected>北京</option>
<option value="021">上海</option>
<option value="0571">杭州</option>
</select>
</div>
</body>
</html>
实现代码-select-1:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;
/**
* @author muguozheng
* @date 2020/4/18 17:56
* @Description: 元素定位测试
* @modify
*/
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 访问测试页面-路径改成自己的
driver.get("file:///E:/project/automation/src/test/java/test.html");
// 获取select元素
WebElement element = driver.findElement(By.cssSelector("#city"));
// 实例化一个select
Select select = new Select(element);
select.selectByVisibleText("上海");
// 增加延时以便观察
Thread.sleep(3000);
select.selectByIndex(3);
Thread.sleep(3000);
select.selectByValue("010");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
这里的页面跳转是广义上的,包括跳转到window
(新页面)、frame
、alert
弹窗等。在selenium中,是借助switchTo()
函数完成的。我们查看源码,发现switchTo()
函数的返回值是TargetLocator
,这是接口类WebDriver
的一个内部接口,这个内部接口定义一系列跳转方法。
这些方法的返回值都是WebDriver
,我们可以理解为driver的焦点发生了转移。因此,有一点需要留意,既然焦点转移到了新的页面上,那么想要定位原页面的元素,就要跳转回去。
WebDriver frame(int index)
:根据索引获取frame
WebDriver frame(String nameOrId)
:根据name或id获取frame
WebDriver frame(WebElement frameElement)
:根据WebElement
(也就是可以用xpath、css等定位到frame
元素作为参数)获取frame
WebDriver parentFrame()
:转移焦点到父级内容,如果当前内容是顶级内容,将不发生变化。下面进行实例演示。
页面代码-frame-1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UI自动化测试页面</title>
</head>
<body>
<div id="div">
<select id="city">
<option value="">请选择一个城市</option>
<option value="010">北京</option>
<option value="021">上海</option>
<option value="0571">杭州</option>
</select>
</div>
<iframe style="height:1000px;width:100%" id="myIframe" src="http://www.baidu.com"></iframe>
</body>
</html>
实现代码-frame-1:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 访问测试页面
driver.get("file:///E:/project/automation/src/test/java/test.html");
// 跳转到frame
// driver.switchTo().frame("myIframe") 通过nameOrId跳转
// frame(WebElement frameElement)方式跳转
driver.switchTo().frame(driver.findElement(By.xpath("//*[@id='myIframe']")));
// 在frame中进一步操作
driver.findElement(By.id("kw")).sendKeys("测试");
Thread.sleep(3000);
// 跳转回父级页面
driver.switchTo().parentFrame();
Select select = new Select(driver.findElement(By.id("city")));
select.selectByValue("010");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
当我们点击了a
标签元素时,会触发打开链接页面的事件,有两种情形:
a
标签有target="_blank"
时触发当发生第2种情况时,同上文的frame
类似,由于driver的焦点还停留在原窗口,我们在新窗口的页面上定位元素时,自然会产生错误,因此引出driver
焦点跳转问题。
selenium
提供了唯一的窗口跳转方法:WebDriver window(String nameOrHandle)
,方法的入参nameOrHandle
意为窗口名称(name
)或句柄(handle
),但查看源码和很多资料也没弄清楚窗口的name
是什么,只好先研究一下handle
了。
通过handle跳转窗口有3种思路:
String getWindowHandle()
),打开新页面后获取所有窗口句柄的集合,遍历此集合,与句柄1不同则跳转该句柄所指向的窗口。Set getWindowHandles()
),通过索引(越晚打开的窗口,其索引越大)来跳转到目标窗口。title
)来跳转到目标窗口。下面通过一个例子来进行演示,我们要实现的场景是:
页面代码-window-1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UI自动化测试页面</title>
</head>
<body>
<div id="div">
<a id="new_page" target="_blank" href="file:///E:/project/58coin/automation/src/test/java/testNew.html">点击跳转新页面</a>
</div>
<div style="margin-top: 10px">
<input type="text" id="input">
</div>
</body>
</html>
页面代码-window-2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UI自动化-新页面</title>
</head>
<body>
<div>
<input type="text" id="new_input">
</div>
</body>
</html>
实现代码-window-1(句柄对比方式):
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.Set;
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 访问测试页面
driver.get("file:///E:/project/automation/src/test/java/test.html");
// 获取当前窗口句柄
String handlePresent = driver.getWindowHandle();
// 点击超链接打开新页面
driver.findElement(By.id("new_page")).click();
// 遍历句柄集合,与handlePresent不同,则是新窗口,跳转并结束遍历
Set<String> handles = driver.getWindowHandles();
for (String handle : handles) {
if (!handle.equals(handlePresent)) {
driver.switchTo().window(handle);
break;
}
}
System.out.println("当前页面title:" + driver.getTitle());
driver.findElement(By.cssSelector("#new_input")).sendKeys("新页面");
Thread.sleep(1000); // 暂停1s以便观察
// 跳转到原来窗口
driver.switchTo().window(handlePresent);
System.out.println("当前页面title:" + driver.getTitle());
driver.findElement(By.id("input")).sendKeys("原页面");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
实现代码-window-2(索引方式):
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 访问测试页面
driver.get("file:///E:/project/automation/src/test/java/test.html");
// 点击超链接打开新页面
driver.findElement(By.id("new_page")).click();
Set<String> winSet = driver.getWindowHandles();//获取所有句柄
List<String> winList = new ArrayList<String>(winSet);//转成list列表
// 跳转到最新打开的窗口
driver.switchTo().window(winList.get(winList.size() - 1));
System.out.println("当前页面title:" + driver.getTitle());
driver.findElement(By.cssSelector("#new_input")).sendKeys("新页面");
Thread.sleep(1000); // 暂停1s以便观察
// 跳转到倒数第二个打开的窗口
driver.switchTo().window(winList.get(winList.size() - 2));
System.out.println("当前页面title:" + driver.getTitle());
driver.findElement(By.id("input")).sendKeys("原页面");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
实现代码-window-3(标题方式):
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.Set;
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 访问测试页面
driver.get("file:///E:/project/automation/src/test/java/test.html");
// 点击超链接打开新页面
driver.findElement(By.id("new_page")).click();
// 获取窗口句柄的集合(set)
Set<String> handles = driver.getWindowHandles();
// 遍历并通过title判断目标窗口
for (String handle : handles) {
if (driver.switchTo().window(handle).getTitle().contains("UI自动化-新页面")) {
driver.switchTo().window(handle);
break;
}
}
System.out.println("当前页面title:" + driver.getTitle());
driver.findElement(By.cssSelector("#new_input")).sendKeys("新页面");
Thread.sleep(1000); // 暂停1s以便观察
// 遍历并通过title判断目标窗口
for (String handle : handles) {
if (driver.switchTo().window(handle).getTitle().contains("UI自动化测试页面")) {
driver.switchTo().window(handle);
break;
}
}
System.out.println("当前页面title:" + driver.getTitle());
driver.findElement(By.id("input")).sendKeys("原页面");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
alert弹窗本质是js原生代码,不是标签元素,并且一个页面中最多有且仅有一个alert弹窗,如果页面中出现了alert弹窗,一定要先处理它,不然无法进行其他操作。alert弹窗切换的操作非常简单。
Alert接口中提供了以下几个方法:
void dismiss()
:点击弹窗的取消按钮void accept()
:点击弹窗的确认按钮String getText()
:获取弹窗的文本内容void sendKeys(String keysToSend)
:向弹窗中输入内容下面演示一个示例:
页面代码-alert-1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UI自动化测试页面</title>
<script type="text/javascript">
function display_alert() {
alert("I am an alert box!!")
}
</script>
</head>
<body>
<div>
<input type="button" id="alert" onclick="display_alert()" value="Display alert box"/>
<input type="text" id="input">
</div>
</body>
</html>
实现代码-alert-1:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 访问测试页面
driver.get("file:///E:/project/automation/src/test/java/test.html");
// 点击弹窗按钮
driver.findElement(By.id("alert")).click();
// 弹窗确定:没有这一步处理,后续操作将报错
driver.switchTo().alert().accept();
// 输入框输入内容
driver.findElement(By.id("input")).sendKeys("测试");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
在selenium中,所有的鼠标操作的实现全部由Actions
类提供。首先,Actions类提供了多个有参构造方法:
public Actions(WebDriver driver)
public Actions(Keyboard keyboard, Mouse mouse)
public Actions(Keyboard keyboard)
但后两个构造方法只是扩展方法,很少用。第一个构造方法才是最重要的,它的入参是当前的WebDriver
。
再让我们看一下这个类提供了哪些操作鼠标的方法:
Actions clickAndHold(WebElement target)
:在特定元素上单击鼠标左键(不释放)Actions release(WebElement target)
:在特定元素上释放鼠标左键Actions doubleClick(WebElement target)
:在特定元素上双击鼠标左键Actions moveToElement(WebElement target)
:移动鼠标指针到特定元素Actions contextClick(WebElement target)
:在特定元素上右键单击Actions dragAndDrop(WebElement source, WebElement target)
:拖拽元素void perform()
:执行具体的操作。前面6个方法都是声明一个操作,只有调用perform()
后才会真正执行操作。下面以拖拽元素做一下鼠标操作的演示:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 访问测试页面
driver.get("https://www.runoob.com/try/try-cdnjs.php?filename=jqueryui-api-droppable");
// 跳转到右侧iframe
driver.switchTo().frame("iframeResult");
WebElement source = driver.findElement(By.id("draggable"));
WebElement target = driver.findElement(By.id("droppable"));
Actions actions = new Actions(driver);
actions.dragAndDropBy(source, 110, 120).perform();
Thread.sleep(2000); // 延时以观察效果
actions.dragAndDrop(source, target).perform();
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
对于键盘的模拟操作,Actions
类中有提供 keyUp(CharSequence key)
、keyDown(CharSequence key)
、sendKeys(CharSequence key)
等方法来实现。
另外在Keys类中,提供了很多模拟按键,如BACK_SPACE
、ENTER
等。
对于普通键盘,使用 sendKeys(CharSequence key)
就可以实现:
Actions action = new Actions(driver);
action.sendKeys(Keys.BACK_SPACE);// 模拟按下并释放 BACK_SPACE键
action.sendKeys(Keys.ENTER);// 模拟按下并释放回车键
而对于修饰键,在 WebDriver 中需要用到 KeyDown(theKey)、keyUp(theKey) 方法来操作。
Actions action = new Actions(driver);
action.keyDown(Keys.CONTROL);// 按下 Ctrl 键
action.keyDown(Keys.SHIFT);// 按下 Shift 键
action.keyDown(Key.ALT);// 按下 Alt 键
action.keyUp(Keys.CONTROL);// 释放 Ctrl 键
action.keyUp(Keys.SHIFT);// 释放 Shift 键
action.keyUp(Keys.ALT);// 释放 Alt 键
修饰键是键盘上的一个或者一组特别的键,包括Shift、Ctrl、Alt(Option)、AltGr、Windows logo、Command、FN(Function)等,与一般按键同时使用的时候,用来临时改变一般键盘的普通行为。
我们发现, Actions
类和WebElement
类都有一个sendKeys(CharSequence key)
方法,这两个方法对于一般的输入操作基本上相同,不同点在于以下几点:
Actions
中的sendKeys(CharSequence key)
对于修饰键 (Modifier Keys) 的调用并不会释放,也就是说当调用 actions.sendKeys(Keys.ALT)
、actions.sendKeys(Keys.CONTROL)
、 action.sendKeys(Keys.SHIFT)
的时候,相当于调用 actions.keyDown(keysToSend),而如果在现实的应用中想要模拟按下并且释放这些修饰键,应该再调用 action.sendKeys(keys.NULL)
来完成这个动作。Actions
的 sendKeys(keysToSend)
执行完之后,焦点就不在当前元素了。所以我们可以使用 sendKeys(Keys.TAB) 来切换元素的焦点,从而达到选择元素的作用,这个最常用到的场景就是在用户名和密码的输入过程中。下面以一个百度搜索测试的例子来演示键盘操作:
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 访问测试页面
driver.get("http://www.baidu.com");
WebElement input = driver.findElement(By.id("kw"));
input.sendKeys("测试试");
// 按下退格键删除最后一个字
input.sendKeys(Keys.BACK_SPACE);
Thread.sleep(1000);
Actions actions = new Actions(driver);
// 按下回车键
actions.sendKeys(Keys.ENTER).perform();
Thread.sleep(15000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
在UI自动化执行过程中,如果页面或元素没有加载完成,就进行下一步操作,无疑是会抛出异常的,因此selenium
提供了多种元素等待的方法。
隐式等待是一种全局设置,在driver的整个生命周期都有效,设置方式如下:
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
按源码解释,驱动程序会轮询页面,直到找到该元素(找到后立即执行下一步)或超时(抛出org.openqa.selenium.NoSuchElementException
)。这种设置应该谨慎,充分考虑Xpath
等方式定位元素较慢的可能性。
显示等待相对于隐式等待更加灵活,能针对各个元素进行单独的设置。只有满足显式等待的条件满足,测试代码才会继续向后执行后续的测试逻辑,如果超过设定的最大显式等待时间阈值, 这测试程序会抛出异常。
等待方法由ExpectedConditions
类提供,常用的有以下几个(E代表ExpectedCondition):
E titleIs(final String title)
:判断标题是否是给定标题。E titleContains(final String title)
:判断标题是否包含给定标题。E presenceOfElementLocated(final By locator)
:判断页面元素在页面中存在。E textToBePresentInElement(final WebElement element, final String text)
:给定元素中是否包含特定的文本。E elementToBeClickable(final By locator)
:判断给定元素是否可点击。E elementToBeSelected(WebElement element)
:判断给定元素是否处于选中状态。还有非常多的其他判断方法,可以根据场景灵活选用。下面以百度搜索为例,演示显示等待的使用:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class ElementTest {
public static void main(String[] args) {
// 指定浏览器驱动的路径
String driverPath = "E:/source/driver/chromedriver_80_2.exe";
System.setProperty("webdriver.chrome.driver", driverPath);
// 创建一个chrome driver
WebDriver driver = new ChromeDriver();
// 页面最大化
driver.manage().window().maximize();
try {
// 设定显示等待时间为3s
WebDriverWait wait = new WebDriverWait(driver, 3);
// 访问测试页面
driver.get("http://www.baidu.com");
// 设置By.id("abc")这个元素加载完成才进行下一步,最多等待3s,否则抛出异常
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("abc")));
driver.findElement(By.id("kw")).sendKeys("测试");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}