Multiple testWidgets tests are not working in flutter integration test - flutter

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?

Related

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 testing error: NotInitializedError which involves environment variables (.env)

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

Flutter integration_test - initialising and disposing dependencies

I’m initializing the app like
void main() async {
await Initializer.init();
runApp(MyApp());
}
Initializer.init() initializes all dependencies like Firebase Analytics and register things like Hive TypeAdapters.
My sample tests look like:
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets(
'Sample',
(WidgetTester tester) async {
await Initializer.init();
await tester.pumpWidget(MyApp());
await tester.tap(find.text('Something'));
expect(find.text('180 Results'), findsOneWidget);
},
);
testWidgets(
'Sample 2',
(WidgetTester tester) async {
await Initializer.init();
await tester.pumpWidget(MyApp());
await tester.tap(find.text('Something 2'));
expect(find.text('180 Results'), findsOneWidget);
},
);
}
My problem is that after executing the first test, things that were initialized by Initializer.init() aren’t disposed correctly.
Is there a way to tell integration_test to reset the whole environment every time test is reloaded?
Shall I initialize things differently than static Initializer.init();
Shall I explicitly dispose/unregister all dependencies (sth like Initializer.dispose() at the end of every test)? That solution seems to be a nightmare since I'm using many 3rd parties that are not allowing explicit disposing.

How to wait scheduled future with flutter_test?

My widget execute an asynchronous task and I want to check whether it is complete or not.
But it seems widget tester terminates the test before the task is executed. Whole code is here.
class MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
test();
}
Stream<int?> stream(Future<void> Function() worker) async* {
Stream<int?> work() async* {
await worker();
yield 3;
}
yield* work();
}
Future<void> sampleWorker() async {
throw Error();
}
Future<void> test() async {
print('start');
try {
await for (final v in stream(sampleWorker)) {
print(v);
}
print('point 1');
} catch (error, stack) {
print(stack);
}
print('done');
}
}
void main() {
testWidgets('Widget test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
await tester.pumpAndSettle(const Duration(milliseconds: 1000));
});
test('Plain test', () async {
await MyHomePageState().test();
});
}
I want both tests print stack trace, but only 'Plain test' shows expected behavior. The following is the log.
start
✓ Widget test
start
#0 MyHomePageState.sampleWorker (package:bug_report/main.dart:48:5)
#1 MyHomePageState.stream.work (package:bug_report/main.dart:40:19)
<asynchronous suspension>
#2 StackZoneSpecification._registerUnaryCallback.<anonymous closure> (package:stack_trace/src/stack_zone_specification.dart)
<asynchronous suspension>
done
✓ Plain test
How to make first test prints stack trace? I should not await test() because it should be widget's internal task.
Try WidgetTester's runAsync function to await an arbitrary amount of time for an internal widget's async task to complete.

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?