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

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());
});

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 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!

Firebase crashlytics is not reporting anything on Flutter

I've initialized and configured Firebase for my Flutter project using this documentation and the instructions given by the Firebase Console. After that, I followed this crashlytics documentation for adding crashlytics to my app. But no matter what I've tried, I couldn't see any reports on the Firebase console. Still seeing this;
My main function looks like this:
Future main() async {
runZonedGuarded(
() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
runApp(const App());
},
(error, stack) async {
await FirebaseCrashlytics.instance.recordError(error, stack);
},
);
}
I've tried to;
crash my app using FirebaseCrashlytics.instance.crash()
throw an exception and record it by the onError callback of the runZonedGuarded
manually record an exception using FirebaseCrashlytics.recordError(error, stack)
setting fatal: true or using FirebaseCrashlytics.instance.recordFlutterFatalError
Is there something that I'm missing? I'm really stuck at this point.

Handeling and reporting errors that thrown inside runZonedGuarded callback [Crashlytics, Flutter]

I am implementing Crashlytics with flutter,
and following this docs Using Firebase Crashlytics
I was wondering what would actually happen if an error is thrown inside runZonedGuarded's
error callback: (see the arrow in the code)
void main() async {
runZonedGuarded<Future<void>>(() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
runApp(MyApp());
}, (error, stackTrace) async {
***** some buggy code here ******. <----------
await crashlytics.handleNonFlutterErrors(error,stackTrace);
});;
}
so I have tried it, and to my surprise it is still swallowed by flutter framework and stacktrace was dumped to console - I was expecting the app to crash but it didn't...
does any-body knows how to handle this types of errors?

Unit tests running in parallel in flutter when using GetIt

I am writing an flutter App and I am using GetIt for dependency injection.
When I write unit tests, I do something like this:
void main() {
group("tests", () {
test("test1", () {
GetIt.I.reset();
GetIt.I.registerSingleton(MyDependency());
// Some code using GetIt.I<MyDependency>()
});
test("test2", () {
GetIt.I.reset();
GetIt.I.registerSingleton(MyDependency());
// Some code using GetIt.I<MyDependency>()
});
});
}
When I run the tests one after the other, it works perfectly.
When I run all test at once, I get errors on the line GetIt.I<MyDependency>()... like this:
'package:get_it/get_it_impl.dart': Failed assertion: line 372 pos 7: 'instanceFactory != null': Object/factory with type HttpActions is not registered inside GetIt.
So my guess is, that the tests run in parallel and the setup of one test with GetIt.I.reset() conflicts with the setup of the other test.
So how can I solve this?
Can I somehow tell the tests, that they just cannot run in parallel?
Or is there some way of safely using GetIt in this context?
I think your issue is not about running your tests in parallel but related to the fact that GetIt.I.reset() is an asynchronous method:
Implementation
Future<void> reset({bool dispose = true});
And as mentionned in the documentation of the package for this method:
[...] As dispose functions can be async, you should await this function.
Code Sample
I've made a new test sample based on the code you've provided and inspired by the unit tests written for the package and it seems to be working properly.
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:so_tests/my_dependency.dart';
void main() {
// This method provided by `flutter_test` is called before each test.
setUp(() async {
// Make sure the instance is cleared before each test.
await GetIt.I.reset();
});
group('tests', () {
test('test1', () {
final getIt = GetIt.instance;
getIt.registerSingleton(MyDependency());
// Some code using getIt<MyDependency>()
});
test('test2', () {
final getIt = GetIt.instance;
getIt.registerSingleton(MyDependency());
// Some code using getIt<MyDependency>()
});
});
}