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

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?

Related

Flutter widget testing: Button tap won't work

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!

How to restart a Flutter app from the beginning of the main()?

I want to restart my app at some point from the beginning of the main() function and not just rebuild the whole widget tree.
Here is an example of my code, the thing is I want to call init() when restart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await init();
runApp(
const MyApp(),
);
}
I have tried to wrap the widget tree with a Restart widget and tried phoenix package
But both of them just rebuild the widget tree regardless of what is above in main(). I even tried to call the main() directly but it didn't work as expected.
Is there any solution for this case?
I think you can try "Restart App" module
Instal from your teminal:
flutter pub add restart_app
Import from your dart file:
import 'package:restart_app/restart_app.dart';
Call "Restart App" from main():
onPressed: () {
Restart.restartApp();}
Have a nice day...
You runApp(YourApp()) again where you want to restart.1

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 testing - how to execute test to "Go Back to Prevoius Screen"

I am writing widget tests in Flutter. I am not able to find any documentation to execute "Go Back to Prevoius Screen" as I do not find any method for that. Please help with some sample "test" code for the same.
You have to mock navigation observer.
First create class class MockNavigatorObserver extends Mock implements NavigatorObserver
Declare final mockObserver = MockNavigatorObserver(); in your test file.
Put your widget inside MaterialApp and add property navigatorObservers: [mockObserver],
Finally inside your testWidgets block {} mock like this:
final mockObserver = MockNavigatorObserver();
final backIcon = find.byIcon(Icons.arrow_back_ios);
expect(backIcon, findsOneWidget);
await tester.tap(backIcon);
verify(mockObserver.didPop(any, any));
await tester.pumpAndSettle();