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

Github Actions中Flutter Drive执行集成测试时挂起报错求助

问题:Flutter Web集成测试在Github Actions中挂起并抛出DriverError

现象

本地执行Flutter Web集成测试可正常完成,但在Github Actions CI环境中,测试启动后永久挂起,20分钟后抛出以下错误:

DriverError: Error while reading FlutterDriver result for command: window.$flutterDriver('{"command":"request_data","timeout":"1200000"}')
Original error: Exception: Expected: not null
  Actual: <null>

Original stack trace:
#0      _matcherExpect (package:webdriver/support/async.dart:92:3)
#1      Clock.waitFor (package:webdriver/support/async.dart:60:11)
<asynchronous suspension>
#2      FlutterWebConnection.sendCommand (package:flutter_driver/src/driver/web_driver.dart:327:30)
<asynchronous suspension>
#3      WebFlutterDriver.sendCommand (package:flutter_driver/src/driver/web_driver.dart:123:14)
<asynchronous suspension>
#4      FlutterDriver.requestData (package:flutter_driver/src/driver/driver.dart:549:39)
<asynchronous suspension>
#5      integrationDriver (package:integration_test/integration_test_driver.dart:80:29)
<asynchronous suspension>

Github Actions配置

name: Flutter Integration Tests

on:
  pull_request:
    branches:
      - '*'

jobs:
  run-tests:
    runs-on: macos-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
    
    - name: Create secrets config file
      run: |
        touch secrets.json && echo ${{ secrets.SECRETS_JSON }} | base64 -d > secrets.json
        
    - name: Cache Chromedriver
      uses: actions/cache@v4
      with:
        path: /usr/local/bin/chromedriver
        key: chromedriver-${{ runner.os }}-v123

    - name: install chromedriver
      uses: nanasess/setup-chromedriver@v2
      with:
        # Optional: do not specify to match Chrome's version
        chromedriver-version: '124.0.6367.60'

    - name: Run chrome driver
      run: |
        chromedriver --version
        chromedriver --port=4444 &
    
    - name: Setup Flutter SDK
      uses: flutter-actions/setup-flutter@v3
      with:
        channel: stable
        version: 3.19.0
    - run: which flutter

    - name: flutter steps
      run: |
        dart pub global activate flutter_cors
        fluttercors --disable
        flutter config --enable-web
        flutter pub get

    - name: Run Tests with Chrome
      run: |
        flutter drive \
          --driver=test_driver/integration_test.dart \
          --target=integration_test/main_test.dart \
          --dart-define=E2E_TESTS=true \
          --dart-define-from-file="secrets.json" \
          -d web-server \
          --browser-name=chrome \
          --web-browser-flag="--no-sandbox" \
          --web-browser-flag="--disable-audio-input" \
          --web-browser-flag="--use-fake-device-for-media-stream" \
          --web-browser-flag="--use-fake-ui-for-media-stream" \
          --web-browser-flag="--use-file-for-fake-audio-capture=test/testing_audio.wav"

测试代码

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:scribe/components/buttons.dart';
import 'package:scribe/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  const String automationPassword =
      String.fromEnvironment('AUTOMATION_PWD', defaultValue: "");

  String generateRandomSuffix(int length) {
    const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
    final random = Random();
    return String.fromCharCodes(Iterable.generate(
        length, (_) => chars.codeUnitAt(random.nextInt(chars.length))));
  }

  void disableOverflowErrors() {
    FlutterError.onError = (FlutterErrorDetails details) {
      final exception = details.exception;
      final isOverflowError = exception is FlutterError &&
          !exception.diagnostics.any((e) =>
              e.value.toString().startsWith("A RenderFlex overflowed by"));

      if (isOverflowError) {
        print(details);
      } else {
        FlutterError.presentError(details);
      }
    };
  }

  Future<void> desktopLoginFlow(WidgetTester tester) async {
    final toggleButton = find.byKey(const ValueKey('desktop_login_button'));
    await tester.tap(toggleButton);
    await tester.pumpAndSettle();
    final emailInput = find.byKey(const ValueKey('email_input'));
    await tester.enterText(emailInput, "automated_user@yahoo.com");
    final passwordInput = find.byKey(const ValueKey('password_input'));
    await tester.enterText(passwordInput, automationPassword);
    final confirmLoginButton = find.descendant(
      of: find.byKey(const ValueKey('confirm_login_button')),
      matching: find.byType(InkWell),
    );
    await tester.tap(confirmLoginButton);
    await tester.pumpAndSettle(const Duration(seconds: 2));
    expect(find.text("Start recording"), findsOne);
  }

  testWidgets("Create main Scribe recording", (WidgetTester tester) async {
    disableOverflowErrors();
    app.main();
    // Trigger a frame.
    await tester.pumpAndSettle();
    expect(find.text('Login'), findsOne);
    await desktopLoginFlow(tester);
  });
}

问题原因

该问题仅出现在CI环境的核心原因包括:

  1. Chrome与Chromedriver版本不匹配:CI环境预装的Chrome版本可能与指定的Chromedriver版本不一致,导致驱动通信失败。
  2. 无头环境缺失必要参数:Github Actions运行在无头模式下,未配置正确的Chrome无头参数会导致页面渲染或驱动通信异常。
  3. 资源限制:macos-latest虚拟机资源有限,页面加载或测试执行速度慢,触发超时。
  4. Flutter驱动与Web环境通信超时:CI环境网络或性能问题导致request_data命令无法及时获取返回值,最终超时返回null。

解决方案

1. 确保Chrome与Chromedriver版本匹配

去掉setup-chromedriver中的指定版本,让工具自动匹配CI环境的Chrome版本:

- name: install chromedriver
  uses: nanasess/setup-chromedriver@v2
  # 移除chromedriver-version配置,自动匹配当前Chrome版本

2. 添加Chrome无头模式参数

在测试命令中添加无头模式及相关优化参数:

flutter drive \
  # 原有参数...
  --web-browser-flag="--headless=new" \
  --web-browser-flag="--disable-gpu" \
  --web-browser-flag="--window-size=1920,1080" \
  # 原有其他参数...

3. 优化Chromedriver启动流程

确保Chromedriver完全启动后再执行测试,添加等待逻辑:

- name: Run chrome driver
  run: |
    chromedriver --version
    chromedriver --port=4444 &
    # 等待Chromedriver启动完成
    sleep 3
    # 可选:检查端口是否监听
    lsof -i :4444 || exit 1

4. 切换到更轻量的CI环境

将runs-on改为ubuntu-latest,减少资源消耗并提升执行速度:

jobs:
  run-tests:
    runs-on: ubuntu-latest

5. 调整测试超时时间

在test_driver/integration_test.dart中增加超时配置:

import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver(
      timeout: const Duration(minutes: 10), // 延长超时时间
    );

6. 替换CORS禁用方式

移除flutter_cors工具,直接修改web/index.html添加CORS允许:

<meta http-equiv="Access-Control-Allow-Origin" content="*">

内容的提问来源于stack exchange,提问作者Jhonycage

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 13:40:09