The testWidgets
function is apparently only a subcase of the test
function.
A use case I'm trying to solve right now is to pump the same widget for multiple testWidgets
, a setUp
for multiple testWidgets
. However, how can I do this if it creates a new instance inside each test?
I've tried to initialize a WidgetTester
outside the tests, in the main()
, but WidgetTester
has only a private constructor:
class WidgetTester
extends WidgetController
implements HitTestDispatcher, TickerProvider {
WidgetTester._(TestWidgetsFlutterBinding binding) : super(binding) {
if (binding is LiveTestWidgetsFlutterBinding)
binding.deviceEventDispatcher = this;
}
I don't quite get how the Flutter team made this work, but initializing a WidgetTester
in the same way they did inside the testWidgets
function isn't working for me:
final TestWidgetsFlutterBinding binding
= TestWidgetsFlutterBinding.ensureInitialized()
as TestWidgetsFlutterBinding;
final WidgetTester tester = WidgetTester._(binding);
A simple example would be to try to break down the tests of the Flutter demo that is created with each new Flutter project from flutter create
. In it, we could try to separate the initial setup test of the app from the tapping action test:
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);
});
The idea would be to try to move the await tester.pumpWidget(MyApp());
into a setUp
function.
For that, we will use a widget tester to tap the submit button and Mockito's verify method. The snippet is the same for both create and update tests. final saveButton = find. byKey(const Key('submit-button')); expect(saveButton, findsOneWidget); await tester.
Below is what looks like the current way to solve for this in Flutter.
To summarize:
group(..)
structure inside main()
WidgetTester
instanceasync
testWidgets(..)
await
, so they don't run concurrentlySo far I didn't find a way for the output to indicate each "sub-test" it ran, so just using print(...)
statements for now.
This is a demo for some QR Code logic:
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);
});
});
}
Thanks to: https://iiro.dev/2018/08/22/writing-widget-tests-for-navigation-events/
test/navigation_test.dart
====
And double-thanks, because the navigation testing logic including with this example is thanks to @iiro's post: https://stackoverflow.com/a/51983194/2162226
Here is the appRoutes.dart
file:
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()
};
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With