Flutter: How to use GetIt.I in Integration test - flutter

I am trying to use GetIt in my integration test but I am having some difficulties.
When trying to access registered singletons in GetIt, I am always facing this error:
_AssertionError ('package:get_it/get_it_impl.dart': Failed assertion: line 370 pos 7: 'instanceFactory != null': Object/factory with type DrawScreenState is not registered inside GetIt.
(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;
Did you forget to register it?))
I have a function to register my singletons:
// Initialize GetIt by initializing and registering all the instances for it
Future<void> initGetIt() async {
...
// draw screen services
GetIt.I.registerSingleton<DrawScreenState>(DrawScreenState(
Strokes(), KanjiBuffer(), DrawingLookup(), DrawScreenLayout.Portrait)
);
...
}
This function is called when creating my app instance. However, when I call my function, the singletons are not registered in GetIt and the above Exception is thrown. My code looks like this:
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets("DrawScreen test", (WidgetTester tester) async {
IS_TESTING_DRAWSCREEN = true;
// create app instance and wait until it finished initializing
app.main(); // <-- this calls initGetIt()
print(GetIt.I<DrawScreenState>()); // <-- this raises the exception
await tester.pumpAndSettle(Duration(seconds: 2));
// check that the app does not show any predictions on start up
List<String> preds = (tester.widgetList(find.byType(PredictionButton))).map((e) => (e as PredictionButton).char).toList();
...
}
}
In the README integration tests are mentioned, therefore I assume that it should be possible to access GetIt in them. This leads me to the question what am I doing wrong?

Related

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

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

Flutter: pause code execution to initialize SharedPreferences on startup

I read other answers about initializing SharedPreferences in Flutter app (e.g. this one), but I want to take another route: initialize SharedPreferences object once at the very beginning, and then just pass it around to specific clients as required (a.k.a. dependency injection).
I tried to make my main method async and then use await:
void main() async {
var prefs = await SharedPreferences.getInstance();
runApp(MyApp(prefs));
}
Intuitively, I expected that the execution will halt until prefs is initialized, and then proceeds to runApp. Unfortunately, I get a white screen instead of my UI, so I guess I'm missing something here.
I also tried to use then:
void main() async {
SharedPreferences.getInstance().then((prefs) => runApp(MyApp(prefs)));
}
Same result: white screen.
Is there a way to initialize SharedPreference in this manner in Flutter?
add this before you preference instance
WidgetsFlutterBinding.ensureInitialized();

Flutter's GetIt with the app in background

I'm having issues with using GetIt while the app is in background. With the app is in focus everything works great but the moment I switch focus everything breaks. What's my error here?
I'm using GetIt 6.1.1, flutter 2.0.5, here's the stacktrace
I/flutter ( 5125): FlutterFire Messaging: An error occurred in your background messaging handler:
I/flutter ( 5125): 'package:get_it/get_it_impl.dart': Failed assertion: line 315 pos 7: 'instanceFactory != null': Object/factory with type NotificationCubit is not registered inside GetIt.
I/flutter ( 5125): (Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;
I/flutter ( 5125): Did you forget to register it?)
Here's my GetIt setup
get_it.dart
GetIt getIt = GetIt.instance;
void setupGetIt() {
getIt.registerSingleton<NotificationCubit>(NotificationCubit());
}
main.dart
void main() async {
// some other methods
setupGetIt();
runApp(MyApp());
}
And the method that throws the error.
void _handleMessage(RemoteMessage message) {
getIt
.get<NotificationCubit>()
.addNotification(NotificationClassName.fromRemoteMessage(message));
_displaySnackBar(message);
}
You need to call setupGetIt(); again in your _handleMessage, the code becomes:
void _handleMessage(RemoteMessage message) {
//Add it here
setupGetIt();
getIt
.get<NotificationCubit>()
.addNotification(NotificationClassName.fromRemoteMessage(message));
_displaySnackBar(message);
}
The error is thrown because when _handleMessage method is called, the compiler fails to find getIt.registerSingleton<NotificationCubit>(NotificationCubit()); which registers NotificationCubit>.

Flutter, testing future that throws an Error makes the test unloadable

I'm trying to run a flutter test where a widget displays an error page when the Future provided to it throws an error (via FutureBuilder).
However the line where I create the future seems to be making the test fail.
final futureError = Future.delayed(Duration(milliseconds: 20))
.then((value) => throw Error());
with the message
Failed to load "D:\Projects\flutter\....dart": Instance of 'Error'
Putting it inside the test function resolved the issue
testWidgets('...',
(WidgetTester tester) async {
await tester.runAsync(() async {
final futureError = Future.error('error');
// ...
(it was in the group method prior to this)