前言
- 需求
做 WEB 的 UI 自动化练习,其需求为:
访问慕课网的实战页面,获取实战页面的课程列表信息,并逐个点击进入详情并且关闭详情,直到最后一个。
- 环境
- Java 8
- Maven
- Selenium 4.0
- Junit 5
初步代码
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class imoocTest {
private static WebDriver driver;
@BeforeAll
static void setUP() {
// 获取当前文件下所在的目录
String currentDirectory = System.getProperty("user.dir");
// 设置chromeDriver
System.setProperty("webdriver.chrome.driver", currentDirectory + "/src/test/resources/Driver/Chrome/chromedriver");
ChromeOptions options = new ChromeOptions();
//浏览器模拟手机模式
options.addArguments("--disable-web-security");
// 最大化窗口
options.addArguments("--start-maximized");
//解决报 403 问题
options.addArguments("--remote-allow-origins=*");
driver = new ChromeDriver(options);
// 隐式调用
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
@AfterAll
static void tearDown() {
//关闭浏览器
driver.quit();
}
@Test
public void test() throws InterruptedException {
// 打开慕课网实战的页面
driver.get("https://coding.imooc.com/");
// 获取当前页面的句柄
String mainWindowHandle = driver.getWindowHandle();
// 获取下一页按钮的元素
WebElement nextPage = driver.findElement(By.xpath("//a[text()='下一页']"));
// 判断下一页按钮是否存在
while (nextPage != null) {
// 获取当前页面的所有课程名称
List<WebElement> courseNameList = driver.findElements(By.cssSelector(".title.ellipsis2"));
for (int i = 0; i < courseNameList.size(); i++) {
// 获取当前课程并且点击进入详情页面
String courseName=courseNameList.get(i).getText();
System.out.println(i + ":获取到的课程名称1:" + courseName);
driver.findElement(By.xpath("//p[text()='" + courseName + "']")).click();
Thread.sleep(2000);
// 获取浏览器所有的窗口句柄
Set<String> allWindowHandles = driver.getWindowHandles();
for (String windowHandle : allWindowHandles) {
if (!windowHandle.equals(mainWindowHandle)) {
driver.switchTo().window(windowHandle);
break;
}
}
// 关闭详情页面
driver.close();
// 将句柄切换到主页面
driver.switchTo().window(mainWindowHandle);
Thread.sleep(2000);
}
// 点击下一页
nextPage.click();
Thread.sleep(1000);
// 判断下一页元素是否存在,不存在则捕获异常并将下一页元素赋值为null
try {
nextPage = driver.findElement(By.xpath("//a[text()='下一页']"));
} catch (Exception e) {
nextPage = null;
System.out.println("没有下一页了");
}
}
}
}
问题现象
代码完成之后,执行代码。前 12 个元素执行均正常,但是执行到 13 个元素(从0到1落地微前端架构, MicroApp实战招聘网站)时,报NoSuchElementException 错误,即元素信息未找到。
问题分析
看到报错信息是NoSuchElementException,元素找不到。
首先查看报错的行数,第 68 行,即:driver.findElement(By.xpath(“//p[text()='” + courseName + “']”)).click();
由于这个是第 13 个元素信息,前 12 个元素信息均正常执行,无报错。则说明代码上没有问题。
那重点就关注到courseName(课程名字)这个参数。
通过打印信息,代码中获取到 courseName 文案为: 从0到1落地微前端架构, MicroApp实战招聘网站
通过网站的开发者工具,获取到页面中的 courseName 文案为:从0到1落地微前端架构, MicroApp实战招聘网站
通过对比,发现两者的区别在于在 MicroApp 前面,代码中获取到文案只有 1 个空格,而开发者工具获取到的文案有两个空格。
故导致后续的元素定位失败。
问题解决
通过百度等搜索,发现在2022 年 6 月的时候 Selenium 的 github 上就一个这样的 issues:getText() returns inconsistent result across browsers when element text contains redundant whitespaces
此 issues 上遇到的问题,即和我遇到的问题一样,在元素信息中有两个或者两个以上的空格,但是通过 getText()方法获取到的文案,只有一个空格。
看到该issues 在 2022 年 10 月显示关闭,第一想法就是更新下selenium-java的版本。于是将 pom 文件的selenium-java版本从 4.0.0-rc-2 升级到 4.15.0 版本。
PS:
selenium-java 在 4.8.0 版本开始 Java 的版本需要再 Java 11 及其以上版本
Java8 版本是无法运行selenium-java 的 4.8.0 以及以上版本的。
如执行,则会报如下错误:
升级到 4.15.0 版本之后,再次执行代码。发现错误依旧,即issues 关闭了,但是 pr 还没提成功,那没有办法只能尝试其他办法了。
在升级selenium-java版本无效之后,只有尝试万能的 JS 大法。通过 Js 获取到页面元素的文案信息。
即:
String text = (String)js.executeScript(“return arguments[0].innerHTML”, element);
String text = (String)js.executeScript(“return arguments[0].textContent”, element);
代码调整`
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class imoocTest {
private static WebDriver driver;
@BeforeAll
static void setUP() {
// 获取当前文件下所在的目录
String currentDirectory = System.getProperty("user.dir");
// 设置chromeDriver
System.setProperty("webdriver.chrome.driver", currentDirectory + "/src/test/resources/Driver/Chrome/chromedriver");
ChromeOptions options = new ChromeOptions();
//浏览器模拟手机模式
options.addArguments("--disable-web-security");
// 最大化窗口
options.addArguments("--start-maximized");
//解决报 403 问题
options.addArguments("--remote-allow-origins=*");
driver = new ChromeDriver(options);
// 隐式调用
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
@AfterAll
static void tearDown() {
//关闭浏览器
driver.quit();
}
@Test
public void test() throws InterruptedException {
// 打开慕课网实战的页面
driver.get("https://coding.imooc.com/");
// 获取当前页面的句柄
String mainWindowHandle = driver.getWindowHandle();
// 将driver转换为JavascriptExecutor以便执行JavaScript代码
JavascriptExecutor js = (JavascriptExecutor) driver;
// 获取下一页按钮的元素
WebElement nextPage = driver.findElement(By.xpath("//a[text()='下一页']"));
// 判断下一页按钮是否存在
while (nextPage != null) {
// 获取当前页面的所有课程名称
List<WebElement> courseNameList = driver.findElements(By.cssSelector(".title.ellipsis2"));
for (int i = 0; i < courseNameList.size(); i++) {
// 获取当前课程并且点击进入详情页面
String courseName =(String)js.executeScript("return arguments[0].textContent", courseNameList.get(i));
System.out.println(i + ":获取到的课程名称1:" + courseName);
driver.findElement(By.xpath("//p[text()='" + courseName + "']")).click();
Thread.sleep(2000);
// 获取浏览器所有的窗口句柄
Set<String> allWindowHandles = driver.getWindowHandles();
for (String windowHandle : allWindowHandles) {
if (!windowHandle.equals(mainWindowHandle)) {
driver.switchTo().window(windowHandle);
break;
}
}
driver.close();
driver.switchTo().window(mainWindowHandle);
Thread.sleep(2000);
}
nextPage.click();
Thread.sleep(1000);
try {
nextPage = driver.findElement(By.xpath("//a[text()='下一页']"));
} catch (Exception e) {
nextPage = null;
System.out.println("没有下一页了");
}
}
}
}
脚本执行成功,无异常报错。执行结果如下: