一、元素定位
web⾃动化测试的操作核⼼是能够找到⻚⾯对应的元素,然后才能对元素进⾏具体的操作。常⻅的元素定位⽅式⾮常多,如id,classname,tagname,xpath,cssSelector常⽤的主要由cssSelector和xpath
1.1 cssSelector
这两种,建议使用cssSelector选择器的功能:选中⻚⾯中指定的 标签元素选择器的种类分为基础选择器和复合选择器,常⻅的元素定位⽅式可以通过id选择器和⼦类选择器来 进⾏定位
在网页开发者工具这里,选中代码右键,复制selector
定位百度⾸⻚的“百度热搜”元素,可以使⽤通过id选择器和⼦类选择器进⾏定位:#s-hotsearch > wrapper > div“搜索输⼊框元素”:#kw“百度⼀下按钮”:#su* cssSelector选择器 有多个标签时,想选择第几个标签,就用 小括号(数字)例如: li:nth-child(1)就是选择 第一个 <li></li>标签![]()
使用选择器试一下
分析报错原因:没有找到这个元素
我们刚才手动复制了这个元素的选择器,怎么可能找不到呢?
于是对比,手动打开的百度页面 和 自动化测试打开的百度页面
问题在于:自动测试打开的百度,没有进行登录。
手动打开的百度,登录过了。
于是在复制 选择器 的时候,产生了偏差。
手动打开百度,复制的选择器
#s_xmancard_news_new > div > div.s-news-rank-wrapper.s-news-special-rank-wrapper.c-container-r > div > div > div
自动化测试代开的百度,选择器名是
#s-hotsearch-wrapper > div
把选择器名修改过来就好了
1.2 xpath
XML路径语⾔,不仅可以在XML⽂件中查找信息,还可以在HTML中选取节点。xpath使⽤路径表达式来选择xml⽂档中的节点xpath语法中:1.2.1 获取HTML⻚⾯所有的节点 //*
![]()
1.2.2 获取HTML⻚⾯指定的节点 //[指定节点]
//ul :获取HTML⻚⾯所有的ul节点//input:获取HTML⻚⾯所有的input节点![]()
1.2.3 获取⼀个节点中的直接⼦节点 /
//span/input1.2.4 获取⼀个节点的⽗节点 ..
//input/.. 获取input节点的⽗节点1.2.5 实现节点属性的匹配 //*[@]
//*[@id='kw'] 匹配HTML⻚⾯中id属性为kw的节点
![]()
使用xpath定位元素
1.2.6 使⽤指定索引的⽅式获取对应的节点内容
* XPATH 有多个标签时,想选择第几个标签,就用 中括号[数字]例如: li[1]就是选择 第一个 <li></li>标签![]()
1.2.7 findElement(By) 和 findElements(By)
1)findElement(By)找某一个元素
2)List<WebElement> findElements(By) 找多个元素
测试遍历这些热搜,打印到终端
先找到热搜的选择器 #hotsearch-content-wrapper > li > a > span.title-content-title
使用自动化测试脚本
import io.github.bonigarcia.wdm.WebDriverManager; 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.chrome.ChromeOptions; import org.openqa.selenium.edge.EdgeDriver; import org.openqa.selenium.edge.EdgeOptions; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxOptions; import java.util.List; public class FirstTest { //创建浏览器驱动 WebDriver driver = null; void createDriver() throws InterruptedException { // 以下任何一种都可以,global是全局配置 // WebDriverManager.globalConfig().setUseMirror(true); // WebDriverManager.edgedriver().config().setUseMirror(true); // System.setProperty("wdm.useMirror", "true"); WebDriverManager.chromedriver().setup(); ChromeOptions options = new ChromeOptions(); //1.打开浏览器(使用驱动打开) // //edge 浏览器 // WebDriverManager.edgedriver().setup(); // //增加浏览器配置:创建驱动对象时,指定允许访问所有链接。否则请求会被拒绝 // EdgeOptions options = new EdgeOptions(); options.addArguments("--remote-allow-origins=*"); // driver = new EdgeDriver(options); //创建浏览器驱动对象,带上options driver = new ChromeDriver(options); // Thread.sleep(3000);//为了看到效果,增加停留时间 //谷歌浏览器--不稳定 //改用edge就可以了哈 // WebDriverManager.firefoxdriver().setup(); //打开浏览器驱动 // FirefoxOptions options = new FirefoxOptions(); // options.addArguments("--remote-allow-origins=*"); // driver = new FirefoxDriver(options); //创建浏览器驱动对象,带上options Thread.sleep(3000);//为了看到效果,增加停留时间 } //测试百度搜索关键词:迪丽热巴 void test01() throws InterruptedException { //1.打开浏览器(使用驱动打开) createDriver(); //2.输入完整的网址 https://www.baidu.com driver.get("https://www.baidu.com"); Thread.sleep(3000); //3.找到输入框,并输入关键词:迪丽热巴 driver.findElement(By.cssSelector("#kw")).sendKeys("迪丽热巴"); Thread.sleep(3000); //4.找到百度一下按钮,并点击 driver.findElement(By.cssSelector("#su")).click(); Thread.sleep(3000); //5.关闭浏览器 driver.quit(); } //元素定位 void test02() throws InterruptedException { //1.打开浏览器驱动 createDriver(); //2.打开百度地址 driver.get("https://www.baidu.com/"); //3.定位到百度热搜 //选择器 cssSelector //选择单个元素 // driver.findElement(By.cssSelector("#s_xmancard_news_new > div > div.s-news-rank-wrapper.s-news-special-rank-wrapper.c-container-r > div > div > div")); // driver.findElement(By.cssSelector("#s-hotsearch-wrapper > div")); //选择多个元素 List<WebElement> ll = driver.findElements(By.cssSelector("#hotsearch-content-wrapper > li > a > span.title-content-title")); for (int i = 0; i < ll.size(); i++) { System.out.println(ll.get(i).getText()); } //xpath // driver.findElement(By.xpath("//*[@id=\"s-hotsearch-wrapper\"]/div")); Thread.sleep(3000); //4.关闭驱动 driver.quit(); } }
发现找到了这些热搜
至于为什么最后一个不一样,因为热搜是实时更新变动的,上一秒这个在5名,下一秒可能就排到后面或者前面了
二、 操作测试对象
获取到了⻚⾯的元素之后,接下来就是要对元素进⾏操作了。常⻅的操作有点击、提交、输⼊、清除、获取⽂本。对元素的操作:都在 WebElement 下(click()、sendKeys()、clear()、getText())对网页的操作:都在 driver 下(getTitle()、getCurrentUrl())
2.1 点击/提交对象 click()
// 找到百度⼀下按钮并点击 driver .findElement(By. cssSelector ( "#su" )).click()![]()
对于点击操作,没有特别多的限制
例如:我选择在这个空白的地方(蓝色蒙层)进行点击,没有hidden
也能顺利点击![]()
但是如果是hidden的元素,就会报错(元素不可交互)
![]()
2.2 模拟按键输⼊ sendKeys("")
driver .findElement(By. cssSelector ( "#kw" )).sendKeys( " 输⼊⽂字 " );
编写![]()
![]()
2.3 清除⽂本内容 clear()
输⼊⽂本后⼜想换⼀个新的关键词,这⾥就需要⽤到 clear()如果不清楚,就会拼接了
driver .findElement(By. cssSelector ( "#kw" )).sendKeys( "流行音乐 " );driver.findElement(By. cssSelector ( "#kw" )) .clear();driver .findElement(By. cssSelector ( "#kw" )).sendKeys( "电影推荐 " );![]()
![]()
2.4 获取⽂本信息 getText()
如果判断获取到的元素对应的⽂本是否符合预期呢?获取元素对应的⽂本并打印⼀下~~获取⽂本信息: getText()String bdtext = driver.findElement(By.xpath( "//*[@id=" title- content "]/span[1]" )).getText();System.out.println( " 打印的内容是: " +bdtext);![]()
![]()
![]()
问题:是否可以通过 getText() 获取到“百度⼀下按钮”上的⽂字“百度⼀下”呢?尝试⼀下获取不到
注意:⽂本和属性值不要混淆了。获取属性值需要使⽤⽅法 getAttribute(" 属性名称 ") ;仔细观察,文库之所以能获取文本,是因为,他是标签之间的文字内容,文本内容
而百度一下按钮,这里的 “百度一下”文字,是input标签里的属性
通过 getText()获取内容就没有效果。
获取属性值需要使⽤⽅法 getAttribute("属性名称") ;
2.5 获取当前⻚⾯标题 getTitle()
driver 下的方法
2.6获取当前⻚⾯URL getCurrentUrl()
driver 下的方法
三、窗⼝
打开⼀个新的⻚⾯之后获取到的title和URL仍然还是前⼀个⻚⾯的?当我们⼿⼯测试的时候,我们可以通过眼睛来判断当前的窗⼝是什么,但对于程序来说它是不知道当 前最新的窗⼝应该是哪⼀个。对于程序来说它怎么来识别每⼀个窗⼝呢?每个浏览器窗⼝都有⼀个唯 ⼀的属性句柄(handle)来表⽰,我们就可以通过句柄来切换
3.1 切换窗⼝
1)获取当前⻚⾯句柄: driver.getWindowHandle()
3)获取所有⻚⾯句柄: driver.getWindowHandles()
3)切换当前句柄为最新⻚⾯
String curWindow = driver.getWindowHandle();Set<String> allWindow = driver.getWindowHandles();for( String w : allWindow){if(w!=curWindow){driver.switchTo().window(w);}}
例如:我想打开百度页面,然后点击左上角的 “新闻”,测试新闻页面里的“热点新闻”
结果报错:没有找到这个元素
打印日志 ,分析原因:
根本就没有切换页面
从始至终一直都在 “百度一下,你就知道” 这个页面里
获取百度一下的 handle句柄
363F731476D08D3574958C89821CF8C1
获取 所有句柄
(发现 同一个页面每次获取的句柄不一样,不同页面之间的句柄也不一样)
切换driver
driver.switchTo().window(handle)
可以发现这时 页面才是真正切换了,从句柄值和切换之后打印的标题可以看出来。
3.2 窗⼝设置⼤⼩
1)窗⼝的⼤⼩设置
// 窗⼝最⼤化driver.manage().window(). maximize() ;// 窗⼝最⼩化driver.manage().window(). minimize() ;// 全屏窗⼝driver.manage().window(). fullscreen() ;// ⼿动设置窗⼝⼤⼩driver.manage().window() .setSize ( new Dimension ( 1024 , 768 ))什么都不设置,打开浏览器窗口大小是这样的
最小化窗口收到底层的任务栏了
最大化
全屏
设置自定义窗口大小![]()
3.4 屏幕截图
我们的⾃动化脚本⼀般部署在机器上⾃动的去运⾏,如果出现了报错,我们是不知道的,可以通过抓拍来记录当时的错误场景。屏幕截图⽅法需要额外导⼊包<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
1)初阶版本的截图 - 会覆盖之前的截图
File file = ((TakesScreenshot)webDriver).getScreenshotAs(OutputType. FILE );FileUtils. copyFile (file, new File (filename));
发现 在target目录下生成这种图片
![]()
2)高阶版本的截图
设计image文件夹存截图的方式
./src/test/image/
/2022-08-01/
methodName01-214130.png
methodName02-214130.png
/2022-08-02/
methodName01-214130.png
methodName02-214130.png
// ⾼阶版本List < String > times = getTime();// ⽣成的⽂件夹路径 ./src/test/autotest-2022-08-01/goodsbroser-20220801-214130.pngString filename = "./src/test/autotest-" +times.get( 0 )+ "/" +str+ "-" +times.get( 1 )+ ".png" ;File srcfile = driver.getScreenshotAs( OutputType .FILE);// 把屏幕截图放到指定的路径下FileUtils .copyFile(srcfile, new File (filename));import io.github.bonigarcia.wdm.WebDriverManager; import org.apache.commons.io.FileUtils; import org.openqa.selenium.*; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.edge.EdgeDriver; import org.openqa.selenium.edge.EdgeOptions; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxOptions; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.List; import java.util.Set; public class FirstTest { /** * 创建浏览器驱动 */ WebDriver driver = null; void createDriver() throws InterruptedException { 1.打开浏览器(使用驱动打开) // WebDriverManager.chromedriver().setup(); //打开浏览器驱动 //这一行代码 指定驱动器的位置(因为不知道什么原因 WebDriverManager 总是无法管理我的驱动) System.setProperty("webdriver.chrome.driver", "C:\\Users\\27224\\.cache\\selenium\\chromedriver\\win64\\128.0.6613.84\\chromedriver.exe"); //增加浏览器配置:创建驱动对象时,指定允许访问所有链接。否则请求会被拒绝 ChromeOptions options = new ChromeOptions(); options.addArguments("--remote-allow-origins=*"); driver = new ChromeDriver(options); //创建浏览器驱动对象,带上options Thread.sleep(3000);//为了看到效果,增加停留时间 } /** * 窗口操作 * */ void test04() throws InterruptedException, IOException { createDriver(); //打开网页 driver.get("https://www.baidu.com/"); Thread.sleep(1000); /** * 获取当前窗口的handle句柄 */ String curHandle = driver.getWindowHandle(); System.out.println("百度一下的句柄handle:"+curHandle); System.out.println("切换页面前标题:"+driver.getTitle()); driver.findElement(By.cssSelector("#s-top-left > a:nth-child(1)")).click();//点击新闻标签 Thread.sleep(1000); /** * 屏幕截图 - 初阶版本 * 每次截图会覆盖掉上次截图 * 弊端:看不到完整的图片的差异和变化 */ // File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); // //将截图放到指定位置 // FileUtils.copyFile(srcFile,new File("my.png")); // System.out.println("已拍摄截图,文件名:my.png"); /** * 屏幕截图 - 高阶截图 * */ getScreenShot(getClass().getName()); //期望 测试 新标签页里的 "热点新闻" driver.findElement(By.cssSelector("#headline-tabs > ul > li > a")); void getScreenShot(String name) throws IOException { /** * ./src/test/image/ * /2022-08-01/ * methodName01-214130.png * methodName02-214134.png * /2022-08-02/ * methodName01-214130.png * methodName02-214134.png */ //定义截图格式 SimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");//年月日 2022-08-01 SimpleDateFormat sim2 = new SimpleDateFormat("HHmmss");//时分秒 214130 //格式化 String dirTime = sim1.format(System.currentTimeMillis()); String fileTime = sim2.format(System.currentTimeMillis()); // ./src/test/image/2022-08-01/methodName01-214130.png String filename = "./src/test/image/"+dirTime+'/'+name +'-'+ fileTime + ".png";//methodName01-214130.png File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); //将截图放到指定位置 FileUtils.copyFile(srcFile,new File(filename)); System.out.println("已拍摄截图,文件名:"+filename); } }
并且多次执行不会覆盖
但是也存在一个问题,如果同一时间运行多次,那么图片也会被覆盖将这个图片命名设置更精确一点,到毫秒SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");//时 分 秒 毫秒
3.5 关闭窗口 driver.close()
driver.close()与quit()的区别
1)一个浏览器可以开很多个窗口页面,driver.close()就是关闭当前这一个窗口页面。
close使用在:测试打开新的标签页之后,还要继续返回到前一个标签页中进行测试。
2)而driver.quit()是关闭整个浏览器,是释放driver这个资源。
driver释放只有手动quit(),或者程序执行完毕自动释放。(建议手动释放)
*** 如果使用close()关闭所有窗口是不是就等同于 quit()了呢??
不是,因为quit()是释放掉 driver这个资源,虽然看起来效果和close()很像,其实是不同的。
这里,在打开百度一下页面之后,又打开了新闻页面
执行两次页面关闭
在关闭了新闻页面之后,报异常了(没有这个窗口)
分析原因:
在上面百度一下到新闻页面的时候,有一个窗口切换的操作
此时,driver已经指向了新闻页面的句柄了,接着关闭新闻页面,当再关闭页面的时候,driver仍在指向着新闻的句柄,所以没有关闭成功,报错了。
解决办法:
在关闭新闻页面之前,保存百度一下页面的句柄
当关闭掉新闻页面之后,让窗口切换到百度一下页面,也就是让driver指向百度一下的句柄。
四、等待
通常代码执⾏的速度⽐⻚⾯渲染的速度要快,页面还没渲染出来,程序已经开始找了,就会出现异常报错。怎么避免因为渲染过慢出现的⾃动化误报的问题呢?可以使⽤selenium中提供的三种等待⽅法
打开百度,输入迪丽热巴,使用cssSelector和xpath找“百度百科”元素
都出现异常(找不到这个元素)
但是使用了sleep之后就没事了,这是为什么??
因为,通常代码执⾏的速度⽐⻚⾯渲染的速度要快,页面还没渲染出来,程序已经开始找了,就会出现异常报错。
4.1 强制等待 Thread.sleep()
Thread.sleep (3000)参数是毫秒 3000毫秒就是3秒这个感觉就是硬控死等,尽管这个时间内页面已经渲染完毕了,还是会等到规定时间结束才继续执行程序,一般用于调试。优点:使⽤简单,好理解,直接阻塞程序。调试的时候⽐较有效。缺点:极大的增加流量自动化的执行时间,影响运⾏效率。假如这个项目有1000个测试用例,每个等待3秒,就是3000s也就是50min,将近一个小时。一般自动化执行时间在10min以内,理想情况最好是控制在2~3min。通过等待的方式可以让程序阻塞3秒,在这段时间网页大致渲染完成,3s之后,程序继续运行,就能找到元素了。
4.2 隐式等待 implicitlyWait()
隐式等待 是⼀种智能等待,他可以规定在查找元素时,在指定时间内不断查找元素。如果找到则代码继续执⾏,直到超时没找到元素才会报错。implicitlyWait() 参数:Duration类中提供的毫秒、秒、分钟等⽅法// 隐式等待 1000 毫秒driver.manage().timeouts().implicitlyWait(Duration.ofMillis( 1000 ));// 隐式等待 5 秒driver.manage().timeouts().implicitlyWait(Duration. ofSeconds ( 5 ));隐式等待 作⽤域 是整个脚本的所有元素。即只要driver对象没有被释放掉(driver.quit()),隐式等待 就⼀直⽣效。优点:智能等待,作⽤于全局缺点: 只能查找元素(主要原因 )、每次查找元素都要等待![]()
发现问题:先搜索迪丽热巴,再搜索邓紫棋,输出邓紫棋页面下邓紫棋的名字
预期结果:输出的是 “邓紫棋”
实际结果:输出的是“迪丽热巴”
分析原因:
implictlyWait 只用来查找元素是否存在,不管其他语句是否执行完成。
在这个例子中,因为 最先搜索的迪丽热巴页面也有同样的元素,implictlyWait忽略了其他语句的执行(搜索邓紫棋),于是直接再迪丽热巴的网页,把迪丽热巴的名字输出了
4.3 显⽰等待 new WebDriverWait(driver, Duration.ofSeconds(3))
显⽰等待也是⼀种智能等待,在 指定超时时间范围 内只要 满⾜操作的条件 就会继续执⾏后续代码new WebDriverWait(driver, Duration.ofSeconds(3)).until($express)$express:表达式(条件),涉及到selenium.support.ui.ExpectedConditions包下的ExpectedConditions类返回值:booleanWebDriverWait foo = new WebDriverWait (driver, Duration.ofSeconds( 3 ))foo.until(ExpectedConditions.elementToBeClickable(By.cssSelector( "#id" )));ExpectedConditions预定义⽅法的⼀些⽰例:• elementToBeClickable(By locator) ‒ ⽤于检查元素的期望是可⻅的并已启⽤,以便您可以单击它。• textToBe (By locator , String str) - 检查文本。(精确匹配)• presenceOfElementLocated(By locator) ‒ 检查⻚⾯的 DOM 上是否存在元素。• urlToBe(java.lang.String url) ‒ 检查当前⻚⾯的 URL 是⼀个特定的 URL
优点:显⽰等待是智能等待,可以⾃定义显⽰等待的条件,操作灵活缺点:写法复杂
/**
* 显示等待
* 只做用在当前的条件下
*/
driver.get("https://www.baidu.com/");//打开百度
//1.连续写法
new WebDriverWait(driver,Duration.ofSeconds(3)).until(ExpectedConditions.elementToBeClickable(By.cssSelector("su")));
//2.分开写法
WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(3));//显示等待,设置3s
// //presenceOfElementLocated() 元素是否存在
// //百度输入框是否存在
wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("#su")));
// //存在就输入“迪丽热巴”
driver.findElement(By.cssSelector("#kw")).sendKeys("迪丽热巴");
// //elementToBeClickable() 元素是否可点击
// //先保证元素存在,再判断这个“百度一下”元素是否可以点击
// wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#su")));
// //上一句显示等待通过,才会继续这里的语句,点击百度按钮
// driver.findElement(By.cssSelector("#su")).click();
//textToBe() 文本是否匹配
//先保证这个元素存在,再判断这个元素的文本是否是“新闻”
wait.until(ExpectedConditions.textToBe(By.cssSelector("#s-top-left > a:nth-child(1)"),"新闻"));
String text = driver.findElement(By.cssSelector("#s-top-left > a:nth-child(1)")).getText();
System.out.println("txt:"+text);
driver.quit();
4.4 **注意混合隐式和显式等待的使用,可能会导致不可预测的等待时间
五、导航
5.1 打开⽹站
// 更⻓的⽅法driver.navigate().to( "https://selenium.dev" );// 简洁的⽅法driver.get( "https://selenium.dev" );![]()
5.2 浏览器的前进、后退、刷新
driver.navigate().forward();
driver.navigate().back();
driver.navigate().refresh();
六、 弹窗
driver是在标签页里的,而弹窗是在⻚⾯是找不到任何元素的,这种情况怎么处理?
使⽤selenium提供的Alert接⼝
6.1 警告弹窗+确认弹窗+提示弹窗
//切换弹窗Alert alert = driver.switchTo. alert() ;// 确认alert.accept()// 取消alert.dismiss()Alert alert = driver.switchTo.alert();alert.sendKeys("hello");alert.accept();alert.dismiss()
6.1.1 警告弹窗
6.1.2 确认弹窗
6.1.2 提示弹窗
七、⽂件上传
点击⽂件上传的场景下会弹窗系统窗⼝,进⾏⽂件的选择。selenium⽆法识别⾮web的控件,上传⽂件窗⼝为系统⾃带,⽆法识别窗⼝元素但是可以使⽤sendkeys来上传指定路径的⽂件,达到的效果是⼀样的
WebElement ele = driver.findElement(By.cssSelector("body > div > div > input[type=file]"));ele. sendKeys ("D:\\selenium2html\\selenium2html\\upload.html");
八、 浏览器参数
在之前创建浏览器驱动时,就对浏览器设置了参数
8.1 设置⽆头模式 options.addArfuments("-headless");
电脑上前台是看不到效果的,只是后台运行
因为实际测试,不会人工一直盯着
一般都是无头模式,通过自动化测试结果来分析问题
8.2 设置浏览器加载策略 options.setPageLoadStrategy(PageLoadStrategy.NONE);
options.setPageLoadStrategy(PageLoadStrategy. NONE );不建议使用none通常情况,不要设置页面加载策略,旨在极端情况下,页面元素可见了,但是页面还在请求资源中(以备不时之需)![]()