Flutter integration test for UI and HTTP call - flutter

I have a simple login screen that calls login API and I trying to wait for the second screen after ProgressIndicator finishes and the new screen comes.
But It shows loading ProgressIndicator then it never goes to the second screen and expects function fails
So what should I do or what is similar to waitFor but for Flutter integration_test library?
My testing code
await tester.tap(find.byKey(const Key('loginButton')));
await tester.pumpAndSettle();
await tester.ensureVisible(find.byKey(const Key('loadingWidget')));
await tester.pumpAndSettle();
await tester.ensureVisible(find.byType(DetailsScreen));
expect(find.text('Log In'), findsNothing);
Here is the login button onTap
BlocProvider.of<LoginBloc>(context).add(
LoginEvent.loginWithEmail(
email: emailController.text,
password: passwordController.text,
),
);

You should use pumpAndSettle method of WidgetTester to wait until animations end. Your code will be like this:
testWidgets("Login test", (tester) async {
...
await tester.tap(find.byKey(ValueKey('Login')));
await tester.pumpAndSettle();
...
}

My goal is integration test so I replaced integration_test with the default Flutter driver and I was able to waitFor while animation by using
await driver.runUnsynchronized(() async {
//wait for something
});
More info: https://github.com/flutter/flutter/issues/36244
And I mock my Dio HTTP client by the following library
http_mock_adapter

Related

SyncFusion Calendar Flutter widget unclear how to test it

I used the widget to build a basic application for self learning purpose. I would like to widget test what I am done but there's something unclear to me on the topic. In the widget test I wrote this:
// Render the widget.
await tester.pumpWidget(MaterialApp(
title: 'Firestore test', home: Calendar(firestore: firestore)));
// Let the snapshots stream fire a snapshot.
await tester.idle();
// Re-render.
await tester.pump();
// Verify the output.
expect(find.text('Appointments'), findsOneWidget);
expect(find.text('No selected date'), findsOneWidget);
With last line that verifies that without any interaction the SyncFusion calendar should display "No selected date" in the agenda. That line fails as no widget are found. I also tried to do a test with the tap to a given date to display the events for a day, but the tap does not work as well.
// pre filled data before
// ...
await tester.pumpWidget(MaterialApp(
title: 'Firestore test', home: Calendar(firestore: firestore)));
// Let the snapshots stream fire a snapshot.
await tester.idle();
// Re-render.
await tester.pump();
await tester.tap(find.text(datetime.day.toString()));
await tester.pumpAndSettle();
await tester.pump();
// Verify the output.
expect(find.text('Appointments'), findsOneWidget);
expect(find.text('Mark', skipOffstage: false), findsOneWidget);
Could you help me understand what I am missing in order to verify the above behaviour?

How can I test that a AlertDialog appeared?

Hello I have a window with a button that should open an AlertDialog. How can I build a test that tells me that the alert opened?
var button = find.byKey(KEY_ADD_WALLET);
await tester.tap(button);
expect(... ??? ...)
I found the answer myself:
var button = find.byKey(KEY_ADD_WALLET);
await tester.tap(button);
await tester.pumpAndSettle();
expect(find.byType(Dialog), findsOneWidget);
The magic is calling tester.pumpAndSettle();
A more specific test could be:
expect(find.descendant(of: find.byType(Dialog), matching: find.text("Add public wallet")),findsOneWidget);

Integration tests work fine from flutter drive, but hang when entering text in Firebase Test Lab

I have the following sign-in test set up for my app:
testWidgets('sign in with email and password', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle(Duration(seconds: 3));
final emailInputFinder = find.byKey(Key('type-email'));
final passwordInputFinder = find.byKey(Key('type-password'));
final emailButtonFinder = find.byKey(Key('enter-email'));
final passwordButtonFinder = find.byKey(Key('enter-password'));
final dashboardFinder = find.byKey(Key('dashboard'));
await tester.enterText(emailInputFinder, 'test.lab.user.01#...');
await tester.pumpAndSettle(Duration(seconds: 3));
await tester.tap(emailButtonFinder);
await tester.pumpAndSettle(Duration(seconds: 3));
await tester.enterText(passwordInputFinder, '...');
await tester.pumpAndSettle(Duration(seconds: 3));
await tester.tap(passwordButtonFinder);
await tester.pumpAndSettle(Duration(seconds: 3));
expect(dashboardFinder, findsOneWidget);
});
The hardcoded 3 second delay isn't ideal, but without it I can't run the tests using the driver.
Running on my local machine with the driver is fine. I use the command
flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart
But when I upload the Espresso test to Firebase Test Lab and review the video, the sign-in screen loads up, but the first enterText() command seems to have no effect. Nothing is added to the text field, and the test just times out.
I have tried various combinations of tester.tap() and tester.showKeyboard(), but so far, nothing is coming up.
How can I get enterText() to work correctly in the Espresso environment supported by TestLab?
Try calling binding.testTextInput.register():
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding;
// Necessary for being able to enterText when not in debug mode
binding.testTextInput.register();
There is an issue in the flutter repo about documenting this. Basically if you're not in debug mode you have to call binding.testTextInput.register() in order for tester.enterText to work.

widget test, testing a button with CircularProgressIndicator

I have two tests that after a click on a button I want to test if a CircularProgressIndicator will appear on the button.
The problem is after the tap and the tester.pump(), it can't find the CircularProgressIndicator causing the test to fail, I tried with pumpAndSettle() and also add a Duration(seconds:x) but got the same error
It's weird because the tests were ok before I update the flutter sdk to 1.22.0, upgrade my flutterFire packages and migrate to use the Android embbeding V2.
testWidgets("Should show loading button when click on request button ",
(WidgetTester tester) async {
await tester.pumpWidget(
_buildWidget(
home: FinancialDialogBill(
paymentId: "fakePaymentId",
additionalValueType: AdditionalValueType.fee,
billValues: billValues,
installmentList: installments,
),
),
);
expect(find.byKey(financialDialogBillRequestButton), findsOneWidget);
expect(find.byKey(financialDialogBillRequestButtonLoading), findsNothing);
await tester.tap(find.byKey(financialDialogBillRequestButton));
await tester.pump();
expect(
find.byKey(financialDialogBillRequestButtonLoading), findsOneWidget);
expect(find.byKey(financialDialogBillRequestButton), findsNothing);
});
});
A bit late, but i ran into this issue as well, the shortest workaround i found i just to try/catch the timeout error...
try {
await tester.pumpAndSettle();
} catch (err) {}
expect(find.byKey(financialDialogBillRequestButtonLoading), findsOneWidget);
expect(find.byKey(financialDialogBillRequestButton), findsNothing);
Happy if it can help someone before a better solution is found.

Returning value printed by button during Flutter integration test?

I have a simple app that has a screen with a button. When this button is pressed the login screen pops up; It has two text fields (one for email and one for password), and a button. When the button is pressed, the credentials are authenticated and the json containing the info is printed to the console (I haven't implemented the whole functionality of the button yet since this will take a long time).
I'm running a group of integration tests to see if the app runs normally, which includes having the json printed to the console. That, however, doesn't happen, but it does when I debug the app. Is there any way to print that json to the console during an integration test?
These are my tests:
test('Button renders', () async {
expect(await driver.getText(enterTextFinder), "Enter");
});
test('Button taps', () async {
await driver.tap(enterButtonFinder);
expect(await driver.getText(acceptTextFinder), "Accept");
});
test('Enter credentials', () async {
await driver.tap(emailFieldFinder);
await driver.enterText(email);
await driver.tap(passFieldFinder);
await driver.enterText(password);
});
test('Perform login', () async {
await driver.tap(acceptButtonFinder);
});