Flutter widget testing: Button tap won't work - flutter

I'm using widget testing in Flutter. I'm using the game template from here
https://github.com/flutter/samples/tree/main/game_template
and my test doesn't work as expected:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:escape_team/main.dart';
void main() {
testWidgets('Main menu can be started', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp(
inAppPurchaseController: null,
playerProgressPersistence: null,
settingsPersistence: null,
));
expect(find.text('PLAY'), findsOneWidget);
await tester.tap(find.text('PLAY'));
await tester.pumpAndSettle();
expect(find.text('MISSION SELECT'), findsOneWidget);
});
}
results in:
00:00 +0: Main menu can be started
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _TextFinder:<zero widgets with text "MISSION SELECT" (ignoring offstage widgets)>
Which: means none were found but one was expected
However, when I manually tap the 'PLAY' button in my app, the "MISSION SELECT" screen appears. How can I find out what's going on - and wrong?
UPDATE:
I did some more research, I guess it's caused by a JSON that's being loaded by my app before the main menu is started.
missionLoader
.readJson('assets/json/missions_en.json')
.then((value) => GoRouter.of(context).go('/play'));
This calls
final String response = await rootBundle.loadString(jsonFile);
... and rootBundle file loading isn't supported out of the box in Flutter tests, as I see it?
I tried adding
WidgetsFlutterBinding.ensureInitialized();
to my app's main() method, as well as to the test's main method, but both did not help. Any further leads would be greatly appreciated! Thank you!

Related

Flutter integration_test loads forever

I am trying to run a very simple integration_test but it is loading forever. Any ideas about what is going wrong here?
import 'package:advicer/2_application/pages/advice/widgets/advice_field.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:advicer/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('end-to-end', () {
testWidgets('tap on custom button, verify advice will be loaded',
(widgetTester) async {
app.main();
await widgetTester.pumpAndSettle();
//verify that no advice has been loaded
expect(find.text('Your advice is waiting for you'), findsOneWidget);
//find custom button
final customButtonFinder = find.text('get advice');
//emulate a tap on the custom button
await widgetTester.tap(customButtonFinder);
//Trigger a frame and wait until its settled
await widgetTester.pumpAndSettle();
//verify the advice is loaded
expect(find.byType(AdviceField), findsOneWidget);
});
});
}
softPug#liruhuadeMacBook-Pro Dart_Flutter_zero-master % cd advicer
softPug#liruhuadeMacBook-Pro advicer % flutter test integration_test00:00 +0: loading /Users/softPug/Documents/GitHub/Dart_Flutter_zero-master/advicer/integration_test/advicer_app_test.dart                 R06:33 +0: loading /Users/softPug/Documents/GitHub/Dart_Flutter_zero-master/advicer/integration_test/advicer_app_test.dart                ⣽
Trouble shoots my code to successfully run integration test

Flutter tests - Having two Integration tests in a same group ()

I'm trying to run two tests in a same group in one test file.
If I split them into two separate test files, they are working fine.
But if I add them in a group, the 2nd test fail with the error saying "TypeAdapter is already registered for the given id".
My app uses HiveDB and it sets up Hive boxes in the main.dart before launching the App. I understand that the 2nd test is also trying the same and fails because the setup is already done.
I followed this doc to setup and write integration tests.
Most of the youtube tutorials, medium articles, and other online resources explain so well on how to run one single test in a test file and they're outdated. I never found a resource having two tests in the same file yet.
Here's my main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await HiveDB.setup();
runApp(App());
}
test_driver/integration_test.dart
import 'package:integration_test/integration_test_driver.dart';
Future<void> main() => integrationDriver();
test/app_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:integrationtestdemo/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Home screen tests', () {
tearDown(() async {
print("TearDown() -- called");
await HiveDB.close();
});
testWidgets('Scenario # - loreum ipsum ...', (tester) async {
// ARRANGE
await app.main();
await tester.pumpAndSettle();
// ACT
// ASSERT
expect(1 + 3, 4);
});
testWidgets('Scenario #2', (tester) async {
// ARRANGE
await app.main();
await tester.pumpAndSettle();
// ACT
// ASSERT
expect(2 + 2, 4);
});
});
}
The command I use to execute tests:
flutter drive --driver=.\test_driver\integration_test.dart --target=.\test\app_test.dart
Error I get:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞═════════════════
The following HiveError was thrown running a test:
There is already a TypeAdapter for typeId 1.
When the exception was thrown, this was the stack:
#0 TypeRegistryImpl.registerAdapter (package:hive/src/registry/type_registry_impl.dart:104:11)
#1 HiveDB.setup (package:occasionly/src/services/hive_db.dart:80:10)
<asynchronous suspension>
Could someone please help me here? I'm stuck on this for a long time and couldn't figure out a way to isolate the state between tests?
I have found a dirty trick to solve "TypeAdapter is already registered for the given id" this error
try to put it like this, and the error will not appear anymore
Hive.registerAdapter(YourAdapter(), override: true);
But I have no idea what is the possible impact could this argument do
I also faced the same issue, but I found a workaround by referring to this ERROR in flutter: widget_test.dart cannot detect MyApp(), you could try it.
testWidgets('Scenario #1', (tester) async {
// ARRANGE
await app.main();
...
});
testWidgets('Scenario #2', (tester) async {
// ARRANGE
await tester.pumpWidget(MyApp());
});

Flutter Widget Test passes on device but not without one

I have a widget that displays a list of data from an api and I'm trying to write tests for it's various states starting with it's empty state.
Currently my test pumps the widget in question, the widget makes a network call which I'm mocking and then it checks to see if the empty state text is shown.
This test passes when I run the test on a device using
flutter run --flavor myTestApp -t test/booking/booking_list_widget_test.dart
But fails when I run it from the IDE (IntelliJ) the failure exception is:
The following TestFailure object was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _TextFinder:<zero widgets with text "You're all caught up." (ignoring offstage widgets)>
Which: means none were found but one was expected
It seems to not be waiting for the Duration given to tester.pump and I've tried wrapping it in a tester.runAsync and using pump and settle etc but i cannot get it to pass.
Any ideas welcome I cant share the widget tree but I can share the test
void main() {
setupFirebaseAuthMocks();
setUpAll(
() async {
SharedPreferences.setMockInitialValues(
{
Constants.USER_ID: '1234567890',
},
);
},
);
testWidgets(
'emptyListTest',
(tester) async {
await _initializeDependencies(tester);
final dio = di.getIt.get<Dio>();
final dioAdapter = DioAdapter(dio: dio);
dioAdapter.onGet(
'/url/user/1234567890',
(server) => server.reply(
200,
BookingResponse(
(b) => b
..data = <Booking>[].toBuiltList().toBuilder()
..pagination = Pagination((b) => b
..last = ''
..first = ''
..next = ''
..skip = 0
..count = 0).toBuilder(),
),
),
);
final testApp = await tester.runAsync(
() => wordskiiTestApp(
widgetUnderTest: BookingView(),
),
);
await tester.pumpWidget(testApp!);
await tester.pump(const Duration(seconds: 1));
expect(find.text('AVAILABLE'), findsOneWidget);
// debugDumpApp();
expect(
find.text(
'You\'re all caught up.',
),
findsOneWidget,
);
},
);
}
Future<void> _initializeDependencies(WidgetTester tester) async {
await tester.runAsync(di.getIt.reset);
await tester.runAsync(di.initTesting);
await tester.runAsync(di.getIt.allReady);
}
On which Widget is 'You\'re all caught up' expected? I had similar issues encountered previously when I tried find.textContaining() on a ListView item. I was able to solve my issue by finding out which widget the Text should appear.
On mine, I had to use find.widgetWithText(InkWell, String). To find out the which Widget it is, you can tap on the widget displayed on screen when you've run the test (i.e. on an emulator). The logs should display the tapped Widget.

Flutter - Widget Test throws "None were found" where widgets do exist

I tried to test my Flutter app using flutter test like this
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fnb/ui/page/standard/login.dart' as app;
void main() {
group("Login test: ", () {
testWidgets("Ensure there are 2 text form, and button widget",
(WidgetTester tester) async {
app.LoginPage();
await tester.pumpAndSettle();
expect(find.byType(TextFormField), findsNWidgets(2));
expect(find.byKey(Key('Buttonlogin')), findsWidgets);
});
});
}
The result is i got like this:
The following TestFailure object was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _WidgetTypeFinder:<zero widgets with type "TextFormField" (ignoring offstage widgets)>
Which: means none were found but one was expected
But, when i try to open the page that i want to test, there are the existence of these widget like this:
So, whats wrong with my test code?

flutter test finds two `SnackBar`s with the same key, when there is only one built

I am widget-testing the app in flutter, there is 3 conditions under the block listener on the gui side of the app.
two of the three conditions determine the building of the two different SnackBars, those two SnackBars has different Keys (error-snackbar and success-snackbar), when test is run it says:
2 of the widget with the same key<'error-snackbar'> was found.
because of this I need to expect two widget with findsNWidget(2)
this is the code of the test:
testWidgets("Expects email to be invalid and shows error SnackBar",
(WidgetTester tester) async {
await tester.runAsync(() async {
//build the tree of widgets
final widget = _buildTestableStateWidget();
await tester.pumpWidget(widget);
await tester.pumpAndSettle();
accountBloc.emit(AccountState(
event: event,
error: ErrorModel(key: 'ERROR_GENERAL'),
loading: true,
));
await tester.pumpAndSettle();
expect(find.byKey(Key("error-snackbar")), findsNWidgets(2));
});
});
Any Idea why this is happening ?
just comment if you need more code and I will provide it.
"Keys must be unique amongst the Elements with the same parent."
https://api.flutter.dev/flutter/foundation/Key-class.html
Since you are trying to find 2 widgets with the same key, do they share a common parent? Then it's not allowed. You should try to build the finder in another way, Keys are made to find a specific widget.