如何设置WidgetTester进行Flutter的多重测试

e5njpo68  于 2023-02-25  发布在  Flutter
关注(0)|答案(2)|浏览(136)

1.问题

testWidgets函数显然只是test函数的一个子用例。
我现在尝试解决的一个用例是为多个testWidgets和多个testWidgets使用同一个widget,但是,如果它在每个测试中创建一个新示例,我该如何做呢?
我试过在main()中的测试之外初始化一个WidgetTester,但是WidgetTester只有一个私有构造函数:

class WidgetTester 
  extends WidgetController 
    implements HitTestDispatcher, TickerProvider {
  WidgetTester._(TestWidgetsFlutterBinding binding) : super(binding) {
    if (binding is LiveTestWidgetsFlutterBinding)
      binding.deviceEventDispatcher = this;
}

我不太明白Flutter团队是如何做到这一点的,但是用他们在testWidgets函数中所做的相同方式初始化WidgetTester对我来说是行不通的:

final TestWidgetsFlutterBinding binding 
  = TestWidgetsFlutterBinding.ensureInitialized() 
    as TestWidgetsFlutterBinding;
final WidgetTester tester = WidgetTester._(binding);

2.示例

一个简单的例子是尝试分解使用flutter create中的每个新Flutter项目创建的Flutter演示的测试,在其中,我们可以尝试将应用的初始设置测试与点击动作测试分开:

testWidgets('Initial setup', (WidgetTester tester) async {
  await tester.pumpWidget(MyApp());

  expect(find.text('0'), findsOneWidget);
  expect(find.text('1'), findsNothing);
});

testWidgets('Increment the counter on tap', (WidgetTester tester) async {
  await tester.pumpWidget(MyApp());

  await tester.tap(find.byIcon(Icons.add));
  await tester.pump();

  expect(find.text('0'), findsNothing);
  expect(find.text('1'), findsOneWidget);
});

我们的想法是尝试将await tester.pumpWidget(MyApp());移动到setUp函数中。

v440hwme

v440hwme1#

下面是目前在Flutter中解决这个问题的方法。
总结如下:
1.在main()中创建group(..)结构

  • 从该结构内部创建您自己的私有方法,用于您想要的每一组测试。对于这些私有方法中的每一个:
  • 传入WidgetTester示例
  • 让它们成为async
  • 然后,您应该只有一个对testWidgets(..)的调用
  • 在此方法中,是调用为分发测试逻辑而设置的私有方法的位置
  • 使用await调用其中的每一个,这样它们就不会并发运行

到目前为止,我还没有找到一种方法让输出指示它运行的每个"子测试",所以现在只使用print(...)语句。
这是一些QR码逻辑的演示:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:qr_code_demo/app/appRoutes.dart';
import 'package:qr_code_demo/view/appHome.dart';
import 'package:qr_code_demo/view/qrScanner.dart';

class MockNavigatorObserver extends Mock implements NavigatorObserver {}

void main() {
  group('MainPage navigation tests', () {
    NavigatorObserver mockObserver;

    _loadAppHomeScreen(WidgetTester tester) async {
      await tester.pumpWidget(
        MaterialApp(
          routes: AppRoutes.getRouteMap(),
          home: AppHomeScreen(),
          navigatorObservers: [mockObserver],
        ),
      );
    }

    setUp(() {
      mockObserver = MockNavigatorObserver();
    });

    Future<Null> _verifyLayoutElements(WidgetTester tester) async {
      print('_verifyLayoutElements');
      expect(find.byIcon(Icons.scanner), findsOneWidget);
      expect(find.byType(FloatingActionButton), findsOneWidget);
      expect(find.byType(RaisedButton), findsOneWidget);
    }

    Future<Null> _navigateToQrScannerScreen(WidgetTester tester) async {
      print('_navigateToQrScannerScreen');

      await tester.tap(find.byIcon(Icons.scanner));
      await tester.pumpAndSettle();

      verify(mockObserver.didPush(any, any));

      expect(find.byType(AppHomeScreen), findsNothing);
      expect(find.byType(QrScannerScreen), findsOneWidget);
    }

    testWidgets('AppHomeScreen WidgetTester', (WidgetTester tester) async {
      await _loadAppHomeScreen(tester);

      await _verifyLayoutElements(tester);
      await _navigateToQrScannerScreen(tester);
    });
  });
}

感谢:https://iiro.dev/2018/08/22/writing-widget-tests-for-navigation-events/

  • 滚动到此文件的代码:test/navigation_test.dart

====
再次感谢,因为这个例子中包含的导航测试逻辑要感谢@iiro的帖子:https://stackoverflow.com/a/51983194/2162226
下面是appRoutes.dart文件:

import 'package:qr_code_demo/view/appHome.dart';
import 'package:qr_code_demo/view/qrScanner.dart';  

class AppRoutes {

  static const String AppHome = 'AppHome';
  static const String QrScanner = 'QrScanner';

  static String initialRoute() {
    return AppHome;
  }
  
  static getRouteMap() {
    
    return {
      AppRoutes.AppHome: (context) => AppHomeScreen(),
      AppRoutes.QrScanner: (context) => QrScannerScreen()
    };
    
  }
}
wztqucjr

wztqucjr2#

答案是setUpAll()函数。首先在main方法中使用它,在group之外,它只运行一次。

相关问题