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环境的核心原因包括:
- Chrome与Chromedriver版本不匹配:CI环境预装的Chrome版本可能与指定的Chromedriver版本不一致,导致驱动通信失败。
- 无头环境缺失必要参数:Github Actions运行在无头模式下,未配置正确的Chrome无头参数会导致页面渲染或驱动通信异常。
- 资源限制:macos-latest虚拟机资源有限,页面加载或测试执行速度慢,触发超时。
- 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
相关产品推荐
相关产品推荐

