Unit tests running in parallel in flutter when using GetIt - flutter

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

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: How to use GetIt.I in Integration test

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?

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

How to pass parameters into flutter integration_test?

I used flutter_driver before for integration tests and was able to insert parameters to the test via environment variables from the host, as the test was running from the host.
For another project I am now using the integration_test package.
The test is not running any longer on the host but on the target so when trying to pass arguments via environment variables, the test does not get them.
I saw https://github.com/flutter/flutter/issues/76852 which I think could help but are there other options available right now?
If you're using the integration_test package, the test code can set global variables prior to running your app, and pull them from the environment specified using --dart-define
For example:
// In main.dart
var environment = 'production';
void main() {
if (environment == 'development') {
// setup configuration for you application
}
runApp(const MyApp());
}
// In your integration_test.dart
import 'package:my_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() {
var testingEnvironment = const String.fromEnvironment('TESTING_ENVIRONMENT');
if (testingEnvironment != null) {
app.environment = testingEnvironment;
}
});
testWidgets('my test', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
// Perform your test
});
}
then use the command line flutter test integration_test.dart --dart-define TESTING_ENVIRONMENT=development
Alternately, you could pull them from String.fromEnvironment directly in your app code.
I had the same problem running integration_tests on android emulator. A look at the documentation of bool.fromEnvironment() showed:
/// This constructor is only guaranteed to work when invoked as `const`.
/// It may work as a non-constant invocation on some platforms ...
So this works for me testing with android:
const skipFirst = bool.fromEnvironment('SKIP_FIRST');

Assets are not loaded When I run my test using Flutter driver

When I run my integration test using the Flutter driver I have found that images in my application are not loaded at all. Although If I run my application from flutter run everything works fine.
Here is my Testing code:
// Imports the Flutter Driver API
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
group('login page ignore', () {
// First, define the Finders. We can use these to locate Widgets from the
// test suite. Note: the Strings provided to the `byValueKey` method must
// be the same as the Strings we used for the Keys in step 1.
final ignoreFinder = find.byValueKey('ignore');
final screenFinder = find.byValueKey('child_screen');
FlutterDriver driver;
// Connect to the Flutter driver before running any tests
setUpAll(() async {
driver = await FlutterDriver.connect();
});
// Close the connection to the driver after the tests have completed
tearDownAll(() async {
if (driver != null) {
driver.close();
}
});
test('test',() async {
await driver.waitUntilNoTransientCallbacks();
await driver.waitFor(ignoreFinder);
await driver.tap(ignoreFinder);
print('button clicked');
});
});
}
Expected:When Run using Flutter run
Actual When run test using Flutter driver
You can see DefaultAssetBundle documentation .
it describes using it and a AssetBundle to provide your own assets.
I found the issue. For me it was when I was running the tests inside the lib folder. Once I put the tests inside their own folder on the same level as the lib file, it worked.