Angular应用中Selenium无法调用sendKeys方法的问题排查
Angular应用中Selenium无法定位搜索输入框并调用sendKeys的问题
问题概况
搭建自动化测试框架时,在Angular应用内无法调用sendKeys()方法向搜索输入框输入内容,尝试多种元素定位方式均失败。
目标输入框HTML结构
<div class="mat-form-field-wrapper ng-tns-c183-3"> <div class="mat-form-field-flex ng-tns-c183-3"> <div class="mat-form-field-infix ng-tns-c183-3"> <input _ngcontent-ibj-c243="" maxlength="800" matinput="" formcontrolname="filterValue" class="mat-input-element mat-form-field- autofill-control ng-tns-c183-3 ng-pristine ng-valid cdk-text-field- autofill-monitored ng-touched" ng-reflect-name="filterValue" ng-reflect- maxlength="800" id="mat-input-0" aria-invalid="false" aria- required="false"> <span class="mat-form-field-label-wrapper ng-tns-c183-3"><label class="mat-form-field-label ng-tns-c183-3 mat-empty mat-form-field-empty ng-star-inserted" ng-reflect-disabled="true" id="mat-form-field-label-3" ng-reflect-ng-switch="true" for="mat-input-0" aria-owns="mat-input-0"> <mat-label _ngcontent-ibj-c243="" class="ng-tns-c183-3 ng-star- inserted">Search</mat-label><!--bindings={ "ng-reflect-ng-switch-case": "true" }--></label><!--bindings={ "ng-reflect-ng-if": "true" }--></span></div></div><div class="mat-form-field-underline ng-tns-c183-3 ng-star-inserted"><span class="mat-form-field-ripple ng-tns-c183-3"></span></div><!--bindings={ "ng-reflect-ng-if": "true" }--><div class="mat-form-field-subscript-wrapper ng-tns-c183-3" ng-reflect-ng-switch="hint"><!--bindings={ "ng-reflect-ng-switch-case": "error" }--><div class="mat-form-field-hint-wrapper ng-tns-c183-3 ng-trigger ng-trigger-transitionMessages ng-star-inserted" style="opacity: 1; transform: translateY(0%);"><!--bindings={ "ng-reflect-ng-if": "" }--><div class="mat-form-field-hint-spacer ng-tns-c183-3"></div></div><!--bindings={ "ng-reflect-ng-switch-case": "hint" }--> </div> </div>
尝试过的定位方式(均失败)
//driver.findElement(By.xpath("//*[@id=\"search-field\"]")).sendKeys("1000000"); //driver.findElement(By.id("mat-input-0")).sendKeys("1000000"); //driver.findElement(By.xpath("//input[@class='mat-input-element mat-form-field-autofill-control ng-tns-c183-3 ng-pristine ng-valid cdk-text-field-autofill-monitored ng-touched']")).sendKeys("1000045949"); //driver.findElement(By.class("mat-input-element")).sendKeys("1000000");
报错信息
WARNING: Unable to find an exact match for CDP version 111, so returning the closest version found: 110 Exception in thread "main" org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"#mat\-input\-0"} (Session info: chrome=111.0.5563.147) Build info: version: '4.8.1', revision: '8ebccac989' System info: os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '19.0.2' Driver info: org.openqa.selenium.chrome.ChromeDriver Command: [ac239ea7458d50777e81aa5e2d1214ba, findElement {using=id, value=mat-input-0}] Capabilities {acceptInsecureCerts: false, browserName: chrome, browserVersion: 111.0.5563.147, chrome: {chromedriverVersion: 111.0.5563.64 (c710e93d5b63..., userDataDir: C:\Users\ne1723\AppData\Loc...}, goog:chromeOptions: {debuggerAddress: localhost:62610}, networkConnectionEnabled: false, pageLoadStrategy: normal, platformName: WINDOWS, proxy: Proxy(), se:cdp: ws://localhost:62610/devtoo..., se:cdpVersion: 111.0.5563.147, setWindowRect: true, strictFileInteractability: false, timeouts: {implicit: 0, pageLoad: 300000, script: 30000}, unhandledPromptBehavior: dismiss and notify, webauthn:extension:credBlob: true, webauthn:extension:largeBlob: true, webauthn:extension:minPinLength: true, webauthn:extension:prf: true, webauthn:virtualAuthenticators: true} Session ID: ac239ea7458d50777e81aa5e2d1214ba at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:67) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:484) at org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.createException(W3CHttpResponseCodec.java:200) at org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:133) at org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:53) at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:184) at org.openqa.selenium.remote.service.DriverCommandExecutor.invokeExecute(DriverCommandExecutor.java:167) at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:142) at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:543) at org.openqa.selenium.remote.ElementLocation$ElementFinder$2.findElement(ElementLocation.java:162) at org.openqa.selenium.remote.ElementLocation.findElement(ElementLocation.java:60) at org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:352) at org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:344) at fgi.fgiCreateCode.main(fgiCreateCode.java:35)
完整测试代码
package fgi; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; public class fgiCreateCode { public static void main(String[] args) throws InterruptedException { System.setProperty("webdriver.chrome.driver", "xxx"); ChromeOptions options=new ChromeOptions(); options.addArguments("--remote-allow-origins=*"); //Launching the browser WebDriver driver = new ChromeDriver(options); driver.get("xxx"); driver.manage().window().maximize(); Thread.sleep(3000); driver.switchTo().frame(0); driver.findElement(By.id("username")).sendKeys("xxx"); driver.findElement(By.id("password")).sendKeys("xxx"); driver.findElement(By.xpath("//button[@class='btn btn-primary']")).click(); Thread.sleep(5000); //driver.findElement(By.xpath("//*[@id=\"search-field\"]")).sendKeys("xxx"); //driver.findElement(By.id("mat-input-0")).sendKeys("xxx"); //driver.findElement(By.xpath("//input[@class='mat-input-element mat-form-field-autofill-control ng-tns-c183-3 ng-pristine ng-valid cdk-text-field-autofill-monitored ng-touched']")).sendKeys("xxx"); driver.close(); } }
解决办法
1. 处理iframe切换问题
登录操作在iframe中完成,登录后页面可能回到主文档,需先切换回默认上下文:
// 登录后切换回主文档 driver.switchTo().defaultContent();
2. 使用显式等待替代Thread.sleep
Thread.sleep()稳定性差,改用WebDriverWait等待元素可交互,确保元素加载完成:
import org.openqa.selenium.support.ui.WebDriverWait; import org.openqa.selenium.support.ui.ExpectedConditions; import java.time.Duration; // 登录后添加 driver.switchTo().defaultContent(); WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); WebElement searchInput = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input[@formcontrolname='filterValue']"))); searchInput.sendKeys("1000000");
3. 使用更稳定的定位器
推荐以下几种定位方式,优先级从高到低:
- 通过Angular formcontrolname定位:该属性为Angular表单控件名称,通常不会随意变更
By searchLocator = By.xpath("//input[@formcontrolname='filterValue']"); - 通过关联标签文本定位:利用搜索框的标签文本“Search”关联到输入框
By searchLocator = By.xpath("//mat-label[text()='Search']/ancestor::div[@class='mat-form-field-infix']/input"); - 通过matinput属性定位:Angular Material输入框的特有属性
By searchLocator = By.cssSelector("input[matinput]");
4. 处理Shadow DOM(如果存在)
若输入框位于Shadow DOM内,需使用JavaScriptExecutor定位:
import org.openqa.selenium.JavascriptExecutor; WebElement searchInput = (WebElement) ((JavascriptExecutor) driver).executeScript( "return document.querySelector('input[formcontrolname=\"filterValue\"]');" ); searchInput.sendKeys("1000000");
5. 消除CDP版本警告
下载与Chrome版本(111.0.5563.147)匹配的ChromeDriver,替换现有驱动,消除版本不匹配警告。
内容的提问来源于stack exchange,提问作者jetswept
相关产品推荐
相关产品推荐

