You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Appium Server与Inspector实现Flutter应用WebView切换自动化测试

Flutter Appium WebView上下文切换与登录注册测试解决方案

原代码核心问题

  1. 语法与结构错误:
    • 注释未闭合(Thread.sleep(3000);*/)导致编译失败
    • 变量名大小写不一致(webviewClick vs webViewClick)
    • JUnit与TestNG注解混用,main方法与@Test注解冲突
    • 缺失关键类导入(如Set、TimeUnit、FindsByAndroidUIAutomator等)
  2. 上下文逻辑混乱:
    • 初始化Driver前就调用switchContext,此时Driver未实例化
    • WebView上下文硬编码,未动态匹配目标WebView
    • 在WebView上下文内尝试操作Flutter元素,Flutter元素仅在NATIVE_APP上下文可见
  3. 等待机制不合理:依赖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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 08:00:57