Flutter testing error: NotInitializedError which involves environment variables (.env) - flutter

To start, I did review this but to no avail.
How to access Flutter environment variables from tests?
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:main.dart' as app;
import 'package:mockito/annotations.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebaseauth;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'basic_app_test.mocks.dart';
#GenerateMocks([firebaseauth.FirebaseAuth])
void main() async {
TestWidgetsFlutterBinding.ensureInitialized();
await DotEnv().load(fileName: ".env");
group('Login Page Tests', () {
testWidgets('Empty Login Box', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle(Duration(seconds: 3));
expect(find.byKey(Key('loginButton')), findsOneWidget);
final Finder loginButton = find.byKey(Key('loginButton'));
await tester.tap(loginButton);
await tester.pumpAndSettle();
expect(
find.text(
'Something went wrong. Please check your email and password and try again.'),
findsOneWidget);
});
testWidgets('Email Login - Proper Credentials',
(WidgetTester tester) async {
app.main();
// Wait for app to reach the login page
await tester.pumpAndSettle(Duration(seconds: 3));
final Finder emailBox = find.byKey(Key('emailBox'));
final Finder passwordBox = find.byKey(Key('passwordBox'));
final Finder loginButton = find.byKey(Key('loginButton'));
final Finder welcomeHeader = find.byKey(Key('welcomeHeader'));
var testPassword = DotEnv().env['TEST_PASSWORD'];
await tester.enterText(passwordBox, testPassword);
expect(find.byKey(Key('loginButton')), findsOneWidget);
await tester.tap(loginButton);
await tester.pumpAndSettle(Duration(seconds: 5));
// SHOULD BE EXPECTING A WIDGET IN THE NEXT WINDOW
expect(welcomeHeader, findsOneWidget);
});
});
}
I have a group of tests. The first one runs just fine. The second one runs into an error when the testPassword variable is created.
Here is the error:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following NotInitializedError was thrown running a test:
Instance of 'NotInitializedError'
When the exception was thrown, this was the stack:
#0 DotEnv.env (package:flutter_dotenv/src/dotenv.dart:42:7)
#1 main.<anonymous closure>.<anonymous closure> (file:///D:/WEBDEV/EndevStudios/MedicalApp/gshDevWork/medical-app-frontend/integration_test/basic_app_test.dart:52:31)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
The test description was:
Email Login - Proper Credentials
The .env file is located at the root.
My pubspec.yaml file:
dependencies:
flutter_dotenv: ^5.0.2
flutter:
#Integration Testing
- .env
What I've tried:
Most solutions involve some variation of TestWidgetsFlutterBinding.ensureInitialized(); or IntegrationTestWidgetsFlutterBinding.ensureInitialized(); as well as await DotEnv().load(fileName: ".env"); and await DotEnv().load();.
Thank you in advance to anyone who can help.

You need to change from
await DotEnv().load(fileName: ".env");
to:
await dotenv.load(fileName: ".env");
using the latest library.

You need to add in youre tests this:
// Loading from a static string.
dotenv.testLoad(fileInput: '''FOO=foo
BAR=bar
''');
or
// Loading from a file synchronously.
dotenv.testLoad(fileInput: File('test/.env').readAsStringSync());

Related

Multiple testWidgets tests are not working in flutter integration test

I'm using google flutter integration driver to automate the flutter mobile app.
I want to write multiple widgetTests in a single main_test.dart file.
First test is running successfully, but when second test starts i see Test starting...(attached screenshot below) screen in emulator, but actually not moving forward to launch the app screen.
And finally, getting below error saying that Bad state: No element as screen is not loaded.!
Failure Details:
Failure in method: check value increments
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞═════════════════
The following StateError was thrown running a test:
Bad state: No element
When the exception was thrown, this was the stack:
#0 Iterable.single (dart:core/iterable.dart:654:25)
#1 WidgetController.element (package:flutter_test/src/controller.dart:114:30)
#2 WidgetController.ensureVisible (package:flutter_test/src/controller.dart:1210:73)
#3 main.<anonymous closure>.<anonymous closure> (file:///Users/tummalacharlajagadeesh/Desktop/Sparrow/synapse-cards-mobile/synapse-cards-mobile/integration_test/sanity_test.dart:87:20)
<asynchronous suspension>
Here is my integration test file main_test.dart:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:integration_test/integration_test.dart';
import 'package:sparrowmobile/flavor/uat.dart' as app;
void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
group('sanity test suite', () {
//runs before each test
setUp(() async {
app.main();
});
//runs after each test
tearDown(() async {
final getIt = GetIt.instance;
await getIt.reset();
});
testWidgets('sample test', (tester) async {
await tester.pumpAndSettle(const Duration(seconds: 5));
// ensure title is visible
await tester.ensureVisible(find.byKey(const ValueKey('startButton')));
await tester.tap(find.byKey(const ValueKey('startButton')));
await tester.pumpAndSettle();
expect(find.text('Home Screen'), findsOneWidget);
expect(find.text('Transactions'), findsOneWidget);
});
testWidgets('check value increments', (tester) async {
await tester.pumpAndSettle(const Duration(seconds: 10));
//ensure title is visible
await tester.ensureVisible(find.byKey(const ValueKey('startButton')));
await tester.tap(find.byKey(const ValueKey('startButton')));
await tester.pumpAndSettle();
});
});
}
Can anybody help here me to run multiple widgetTests in a single file?

Flutter Integration Screenshot not working for Android

I am trying to capture screenshots using integration_test but the
await binding.takeScreenshot('${platform}-1');
Hangs for Android with a message
VMServiceFlutterDriver: request_data message is taking a long time to complete...
The app is the Flutter default app with
import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'dart:io';
import 'package:flutter_stam/main.dart' as app;
void main() {
final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding();
group('Screenshot', () {
testWidgets('Screenshot-1', (WidgetTester tester) async {
String platform;
if (Platform.isAndroid) {
platform = 'android';
} else if (Platform.isIOS) {
platform = 'ios';
} else {
platform = Platform.operatingSystem;
}
app.main();
await tester.pumpAndSettle();
// Verify the counter starts at 0.
expect(find.text('0'), findsOneWidget);
// Finds the floating action button to tap on.
final Finder fab = find.byTooltip('Increment');
// Emulate a tap on the floating action button.
await tester.tap(fab);
// Trigger a frame.
await tester.pumpAndSettle();
await binding.convertFlutterSurfaceToImage();
await binding.takeScreenshot('${platform}-screenshot-1');
// Verify the counter increments by 1.
expect(find.text('1'), findsOneWidget);
});
});
}
script
#!/bin/zsh
flutter drive \
--driver=integration_test/driver.dart \
--target=integration_test/app_test.dart \
-d "iPhone 13 Pro Max"
flutter drive \
--driver=integration_test/driver.dart \
--target=integration_test/app_test.dart \
-d "emulator-5554"
Driver
import 'dart:io';
import 'package:integration_test/integration_test_driver_extended.dart';
Future<void> main() async {
try {
await integrationDriver(
onScreenshot: (String screenshotName, List<int> screenshotBytes) async {
final File image = await File('screenshots/$screenshotName.png')
.create(recursive: true);
image.writeAsBytesSync(screenshotBytes);
return true;
},
);
} catch (e) {
print('An error occurred: $e');
}
}
For iOS, I also had an issue but I managed to solve it
I was getting an error
The following MissingPluginException was thrown running a test:
MissingPluginException(No implementation found for method
captureScreenshot on channel plugins.flutter.io/integration_test)
I changed IntegrationTestPlugin.m
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
[[IntegrationTestPlugin instance] setupChannels:registrar.messenger];
}
Adding an additional tester.pumpAndSettle(); after convertFlutterSurfaceToImage fixed the problem
await tester.pumpAndSettle();
await binding.convertFlutterSurfaceToImage();
await tester.pumpAndSettle();

Run mutliple integration tests in Flutter

I have two integrations test files: login_test.dart and homepage_test.dart (each including single test case with 1 testWidgets method)
I'm using command line to run both tests:
flutter test integration_test/
But the problem is when the test has run and passed login_test . it completely reinstalled my app so second test will fail because have no login credentials (stuck at login screen -> failed)
Any idea how I can solve this ?
Appreciate your helps.
login_test.dart
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('end to end tests', () {
testWidgets('test login', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
final Finder textFieldFinder = find.byType(TextFormField);
expect(textFieldFinder, findsNWidgets(2));
final userName = textFieldFinder.first;
final password = textFieldFinder.last;
await tester.enterText(userName, 'myusername');
await tester.enterText(password, 'mypassword');
expect(find.text('Login'), findsOneWidget);
await tester.tap(find.text('Login'));
await tester.pumpAndSettle();
expect(find.byKey(Key('createTicketBtn')), findsOneWidget);
});
});
}
homepage_test.dart
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('end to end tests', () {
testWidgets('test homepage', (tester) async {
app.main();
await tester.pumpAndSettle();
expect(find.byKey(Key('createTicketBtn')), findsOneWidget);
});
});
}
You can use this package to scan a folder inside the integration_test directory for files named like *_test.dart. This will produce a file with a new main importing all test files, so you can just run this new generated file.
You can also create manually a file like this and launch it:
import homepage_test.dart as homepage;
import login_test.dart as login;
void main() {
homepage.main();
login.main();
}
In any case, each test will start from the beginning so you need to login each time. The app will not lose its data though, unless you reset its state in the tearDown.

Flutter Integration testing failed for multiple test cases in a single file

I have a simple login page with email and password text field. A button to login and another to sign up. I tried to write integration testing for the Sign in page.
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('''could type email and password in text filed''',
(WidgetTester tester) async {
await app.main();
await tester.pumpAndSettle();
final textFieldEmail = find.byType(InputTextWidget).first;
final textFieldPassword = find.byType(InputTextWidget).last;
await tester.enterText(textFieldEmail, "asis.adh#gmail.com");
await tester.pumpAndSettle();
expect(find.text("asis.adh#gmail.com"), findsOneWidget);
});
testWidgets(
'should redirect to Sign Up Page when create an account is tapped',
(WidgetTester tester) async {
await app.main();
await tester.pumpAndSettle();
final createAnAccount = find.text("Create an account");
await tester.tap(createAnAccount);
await tester.pumpAndSettle();
expect(find.byType(SignupPage), findsOneWidget);
expect(find.byType(LoginPage), findsNothing);
});
}
When I execute the test case, it fails with the following error:
The following ArgumentError was thrown running a test: Invalid
argument(s): Object/factory with type AmenitiesProvider is already
registered inside GetIt.
Here is my main.dart file
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
configureInjection(Environment.prod);
/// for registering the factory.
await Future.delayed(const Duration(seconds: 2));
runApp(RoopaApp());
}
I tried with a main_test.dart and adding configureInjection(Environment.test) but nothing changes. I am not sure how to fix the error. Is there a way to clean the app or destroy it before going to new test case. If I combine the both testcase into one then it works without any problem.
Here is configureInjection
#injectableInit
void configureInjection(String environment) {
$initGetIt(getIt, environment: environment);
}
I am using get_it and injection package for dependency injection.
Here is the auto generated initGetIt
GetIt $initGetIt(
GetIt get, {
String environment,
EnvironmentFilter environmentFilter,
}) {
final gh = GetItHelper(get, environment, environmentFilter);
final httpClientInjectableModule = _$HttpClientInjectableModule();
final flutterStorageModule = _$FlutterStorageModule();
gh.lazySingleton<AmenitiesProvider>(() => AmenitiesProvider());
gh.factory<Client>(() => httpClientInjectableModule.client);
gh.lazySingleton<FileProvider>(() => FileProvider());
gh.lazySingleton<FlutterSecureStorage>(
() => flutterStorageModule.secureStorate);
gh.factory<ProfilePageBloc>(() => ProfilePageBloc());
gh.factory<SplashScreenBloc>(() => SplashScreenBloc());
gh.lazySingleton<AuthLocalDataSourceProtocol>(
() => AuthLocalDataSource(secureStorage: get<FlutterSecureStorage>()));
gh.lazySingleton<AuthRemoteDataSourceProtocol>(
() => AuthRemoteDataSource(client: get<Client>()));
return get;
}
In my config page.
#injectableInit
void configureInjection(String environment) {
$initGetIt(getIt, environment: environment);
}
I just created a test environment and added following like, now its working as expected.
#injectableInit
void configureInjection(String environment) {
$initGetIt(getIt, environment: environment);
if (environment == Environment.test) {
getIt.allowReassignment = true;
}
}
tearDown(() async {
final getIt = GetIt.instance;
await getIt.reset();
});

Error with navigation testing using mockito

I'm trying to write a test to debug my app. I want to try out my login page, I have two textfields and one button. When the textfields are filled, then If i tap the button I have a Dio request to Log In and navigate to HomePage if its ok. I have the following test:
testWidgets('Login ok',
(WidgetTester tester) async {
LoginPage loginPage = new LoginPage();
final mockObserver = MockNavigatorObserver();
await tester.pumpWidget(
MaterialApp(
home: loginPage,
navigatorObservers: [mockObserver],
),
);
var app = new MediaQuery(
data: new MediaQueryData(), child: new MaterialApp(home: loginPage));
await tester.pumpWidget(app);
Finder username = find.byKey(new Key('emailField'));
Finder password = find.byKey(new Key('passwordField'));
await tester.enterText(username, "user");
await tester.enterText(password, "pass");
await tester.pump();
Finder formWidgetFinder = find.byType(Form);
Form formWidget = tester.widget(formWidgetFinder) as Form;
GlobalKey<FormState> formKey = formWidget.key as GlobalKey<FormState>;
expect(formKey.currentState.validate(), isTrue);
Finder loginButon = find.byKey(new Key('loginButon'));
await tester.tap(loginButon);
await tester.pumpAndSettle();
verify(mockObserver.didPush(any, any));
expect(find.byType(HomePage), findsOneWidget);
});
But I think that
verify(mockObserver.didPush(any, any));
doesn't check if the app go to new page, because the Dio request fail due to the following error:
DioError [DioErrorType.DEFAULT]: type '_StreamHandlerTransformer<List<int>, Uint8List>' is not a subtype of type 'StreamTransformer<Uint8List, Uint8List>'#0 _MockHttpResponse.transform (package:flutter_test/src/binding.dart:2012:35)
#1 DefaultHttpClientAdapter.fetch (package:dio/src/adapter.dart:165:46)
<asynchronous suspension>
#2 Dio._makeRequest (package:dio/src/dio.dart:739:46)
<asynchronous suspension>
...
I have tried many versions of Dio, 2.0.1, 2.0.9, 2.0.10, 2.0.13 but nothing works. If I run my app on simuator, the Dio request is Ok.
How can I solve this or there is another way to test navigation on Flutter?