如何用Appium Server与Inspector实现Flutter应用WebView切换自动化测试
Flutter Appium WebView上下文切换与登录注册测试解决方案
原代码核心问题
- 语法与结构错误:
- 注释未闭合(
Thread.sleep(3000);*/)导致编译失败 - 变量名大小写不一致(
webviewClickvswebViewClick) - JUnit与TestNG注解混用,
main方法与@Test注解冲突 - 缺失关键类导入(如
Set、TimeUnit、FindsByAndroidUIAutomator等)
- 注释未闭合(
- 上下文逻辑混乱:
- 初始化Driver前就调用
switchContext,此时Driver未实例化 - WebView上下文硬编码,未动态匹配目标WebView
- 在WebView上下文内尝试操作Flutter元素,Flutter元素仅在
NATIVE_APP上下文可见
- 初始化Driver前就调用
- 等待机制不合理:依赖
Thread.sleep,易导致元素未加载完成就执行操作
修正后的测试代码
import Util.TestBase; import appium_flutter_driver.FlutterFinder; import appium_flutter_driver.finder.FlutterElement; import com.relevantcodes.extentreports.LogStatus; import io.appium.java_client.AppiumDriver; import io.appium.java_client.FindsByAndroidUIAutomator; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.annotations.Test; import java.util.Set; import java.util.concurrent.TimeUnit; public class AppiumTestDemo extends TestBase { @Test public void flutterWebViewLoginRegisterTest() { AppiumDriver driver = null; try { // 1. 初始化Driver,默认处于NATIVE_APP(Flutter)上下文 driver = TestBase.initializeDriver(); WebDriverWait wait = new WebDriverWait(driver, 20); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); // 2. 在Flutter原生页面点击进入注册/登录入口(示例为Create your account) WebElement createAccountBtn = wait.until(ExpectedConditions.visibilityOfElementLocated( ((FindsByAndroidUIAutomator) driver).findElementByAndroidUIAutomator( "new UiSelector().text(\"Create your account\")") )); System.out.println(createAccountBtn.getText()); createAccountBtn.click(); // 3. 点击进入WebView页面 WebElement webViewBtn = wait.until(ExpectedConditions.visibilityOfElementLocated( ((FindsByAndroidUIAutomator) driver).findElementByAndroidUIAutomator( "new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector.className(\"android.widget.TextView\").text(\"WebView\"))") )); webViewBtn.click(); // 4. 等待WebView加载,动态获取并切换到WebView上下文 wait.until(d -> !driver.getContextHandles().isEmpty()); Set<String> contextNames = driver.getContextHandles(); String targetWebViewContext = null; for (String context : contextNames) { System.out.println("可用上下文:" + context); // 匹配包含WEBVIEW的上下文,根据实际包名调整判断逻辑 if (context.startsWith("WEBVIEW_")) { targetWebViewContext = context; break; } } if (targetWebViewContext != null) { driver.context(targetWebViewContext); logger = extent.startTest("切换至WebView上下文"); logger.log(LogStatus.PASS, "成功切换至WebView上下文"); // 5. 在WebView中执行注册流程(根据实际页面元素调整选择器) WebElement emailInput = wait.until(ExpectedConditions.visibilityOfElementLocated( org.openqa.selenium.By.cssSelector("input[type='email']") )); WebElement passwordInput = wait.until(ExpectedConditions.visibilityOfElementLocated( org.openqa.selenium.By.cssSelector("input[type='password']") )); WebElement confirmPasswordInput = wait.until(ExpectedConditions.visibilityOfElementLocated( org.openqa.selenium.By.cssSelector("input[name='confirmPassword']") )); WebElement registerBtn = wait.until(ExpectedConditions.elementToBeClickable( org.openqa.selenium.By.cssSelector("button[type='submit']") )); emailInput.sendKeys("test@example.com"); passwordInput.sendKeys("Test1234!"); confirmPasswordInput.sendKeys("Test1234!"); registerBtn.click(); logger = extent.startTest("WebView注册流程执行"); logger.log(LogStatus.PASS, "WebView注册操作完成"); // 若需执行登录流程,替换上述注册元素定位与操作即可 // WebElement signInEmail = wait.until(...); // signInEmail.sendKeys("admin@admin.com"); // ... } else { logger = extent.startTest("WebView上下文切换失败"); logger.log(LogStatus.FAIL, "未找到可用的WebView上下文"); throw new RuntimeException("未找到WebView上下文"); } // 6. 切换回Flutter原生上下文(NATIVE_APP) driver.context("NATIVE_APP"); logger = extent.startTest("切换回Flutter原生上下文"); logger.log(LogStatus.PASS, "成功切换回Flutter原生上下文"); // 7. 验证Flutter页面状态(示例:检查登录/注册后的状态元素) FlutterFinder finder = new FlutterFinder(driver); FlutterElement welcomeText = wait.until(d -> finder.byValueKey("welcome_text")); System.out.println("Flutter页面欢迎文本:" + welcomeText.getText()); logger = extent.startTest("Flutter页面状态验证"); logger.log(LogStatus.PASS, "Flutter页面状态验证通过"); } catch (TimeoutException | InterruptedException e) { logger = extent.startTest("测试流程执行失败"); logger.log(LogStatus.FAIL, "失败原因:" + e.getMessage()); e.printStackTrace(); } finally { if (driver != null) { driver.quit(); } } } }
关键注意事项
- WebView上下文匹配:避免硬编码上下文名称,通过遍历
getContextHandles动态筛选WEBVIEW_开头的上下文,若应用有多个WebView,可根据包名进一步匹配(如context.contains("com.your.app.package")) - Flutter元素操作:必须在
NATIVE_APP上下文内使用Flutter Driver的FlutterFinder定位元素,WebView上下文仅能使用常规Web元素定位方式(CSS、XPath等) - 等待策略:替换
Thread.sleep为WebDriverWait显式等待,提升测试稳定性 - 权限与配置:确保Appium Server配置了正确的
chromedriverExecutable路径(针对Android WebView),且模拟器/真机的Chrome版本与chromedriver版本匹配
内容的提问来源于stack exchange,提问作者thoughtfocus
相关产品推荐
相关产品推荐

