The following assertion was thrown while running async test code: pumpAndSettle timed out - flutter

In flutter test i have error:
The following assertion was thrown while running async test code:pumpAndSettle timed out
My test script:
testWidgets('drawer widget test', (WidgetTester tester) async {
TestWidgetsFlutterBinding.ensureInitialized();
await tester.runAsync(() async {
when(operatorRepository.getCurrentSelectedOperator()).thenAnswer((_) async => operator);
await tester.pumpWidget(MaterialApp(home: ScaffoldDrawerTest(operatorRepository)));
expect(find.text('-1'), findsOneWidget);
expect(find.text(''), findsOneWidget);
expect(find.text('Warszawa'), findsNothing);
await tester.tap(find.byType(IconButton).first);
await tester.pumpAndSettle();
expect(find.text('Warszawa'), findsWidgets);
await tester.pumpAndSettle();
await tester.tap(find.byType(ListTile).at(2));
expect(find.text('Warszawa'), findsNothing);
});
});
I try change pumpAndSettle to for (int i = 0; i < 5; i++) { await tester.pump(Duration(seconds: 1)); }
but then i to receive error
Actual: _TextFinder:<zero widgets with text "Warszawa" (ignoring offstage widgets)>
Which: means none were found but some were expected
In ScaffoldDrawerTest i have Consumer and loading widget, when viewModel is loaded then i show another widgets when is widget text "Warszawa"

Related

The following assertion was thrown running a test: pumpAndSettle timed out

here is a test case which test if i failded to add data to my database it will show
error message in snackbar but my test showing pumpAndSettle timed out error
testWidgets(
'renders error snack bar '
'when status changes to failure', (tester) async {
whenListen<TodosOverviewState>(
todosOverviewBloc,
Stream.fromIterable([
const TodosOverviewState(),
const TodosOverviewState(status: TodosOverviewStatus.failure)
]));
await tester.pumpApp(buildSubject(), todosRepository: todosRepository);
await tester.pumpAndSettle();
expect(find.byType(SnackBar), findsOneWidget);
expect(
find.descendant(
of: find.byType(SnackBar),
matching: find.text('todo status failed')),
findsOneWidget);
});
Error:
remove await tester.pumpAndSettle();
and put await tester.pump();

How to wait until the Finder is visible for next code execution in Flutter integration test?

Information:
I have created a sample Flutter unit test to test the login screen where I have email & password as input field and a login button.
Requirement:
Need to test false cases and for that, I have written code as per the below steps.
Open main.dart
Filled the email & password field
onTap event is done on the login button. Over here API will be called and loader is displayed on the screen until API gets a success or failure response.
Need to check if failure dialog is displayed with a message.
Issue/Query:
Now when the API is calling I want to wait when the loader is visible until the loader is gone. So, as of now I just put a manual delay to execute the next code but I want to make it dynamic. So, let me know how we can put dynamic delay based on the loader visible?
Code:
void main() {
group('App Test', () {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Login Fail Test', (WidgetTester tester) async {
await app.main();
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 2));
final emailField = find.byType(TextFormField).first;
final passwordField = find.byType(TextFormField).last;
final loginButton = find.byType(RaisedButton).first;
await tester.enterText(emailField, 'Test');
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 1));
await tester.enterText(passwordField, 'Test123');
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 1));
await tester.tap(loginButton);
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 3));
final dialog = find.byType(AlertDialog).first;
await tester.element(dialog);
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 1));
final dialogButton = find.byType(FlatButton).first;
await tester.tap(dialogButton);
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 2));
});
}
I have a file called utils.dart for functionality like this. In this case I use the following function which will basically poll until the finder is valid
// utils.dart
Future<void> pumpUntilFound(
WidgetTester tester,
Finder finder, {
Duration timeout = const Duration(seconds: 10),
}) async {
bool timerDone = false;
final timer = Timer(timeout, () => timerDone = true);
while (timerDone != true) {
await tester.pump();
final found = tester.any(finder);
if (found) {
timerDone = true;
}
}
timer.cancel();
}
You can also make it throw an exception if it times out, but the error messages aren't helpful, so I usually follow it up with an expect
It would look like
// my_test.dart
final fab = find.byKey(const ValueKey('fab'));
await pumpUntilFound(widgetTester, fab);
expect(fab, findsOneWidget);
Try wrapping like this:
testWidgets('test',
(WidgetTester tester) async {
await tester.runAsync(() async {
// test code here
});
});
If you use:
await tester.pumpAndSettle();
And then:
final widget = find.byKey(Key('whatever'));
It will find dinamically

Flutter widget tests do not pass when run together

I am trying write to some tests for a simple credit card form widget.
Each test passes when run individually but the second one fails and throws the following exception when I run both of them.
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following IndexError was thrown running a test:
RangeError (index): Index out of range: no indices are valid: 0
I tried different approaches such as resetting testedWidget to null in tearDown hook but nothing worked as intended.
Here is the test file I wrote:
void main() {
final validCardNumber = '1234123412341234';
final validExpirationDate = '11/25';
final invalidExpirationDate = '32/25';
final validCvv = '123';
Widget testedWidget;
setUp(() {
testedWidget = testableAddCardScreen(AddCardFormScreen());
});
testWidgets('Form is submittable if inputs are valid', (
WidgetTester tester,
) async {
await tester.pumpWidget(testedWidget);
await tester.pumpAndSettle();
final inputs = find.byType(TextInput);
await tester.enterText(inputs.at(0), validCardNumber);
await tester.enterText(inputs.at(1), validExpirationDate);
await tester.enterText(inputs.at(2), validCvv);
await tester.pumpAndSettle();
final button = evaluateWidget<Button>(find.byType(Button));
expect(button.isDisabled, false);
});
testWidgets('Form is not submittable if inputs are invalid', (
WidgetTester tester,
) async {
await tester.pumpWidget(testedWidget);
await tester.pumpAndSettle();
final inputs = find.byType(TextInput);
await tester.enterText(inputs.at(0), validCardNumber);
await tester.enterText(inputs.at(1), invalidExpirationDate);
await tester.enterText(inputs.at(2), validCvv);
await tester.pumpAndSettle();
final button = evaluateWidget<Button>(find.byType(Button));
expect(button.isDisabled, true);
});
}
I also use some utils functions to make widgets testable.
Widget testableAddCardScreen(Widget child) {
return testableLocalizedWidgetFactory(
ChangeNotifierProvider(
create: (_) => AddCardProvider(
apiClientService: MockApiClientService(),
successCallback: () {},
failureCallback: () {},
),
child: child,
),
);
}

Test onTap behaviour

I would like to test that when I am tapping a button, the user is routed to the next page. This works in the UI. I introduced the test but I get the following error:
The following TestFailure object was thrown running a test: Expected: exactly one matching node in the widget tree Actual: _WidgetTypeFinder:<zero widgets with type "MyNextView" (ignoring offstage widgets)> Which: means none were found but one was expected
What am I doing wrong?
testWidgets('Button tap routes to next page', (WidgetTester tester) async {
final button = createButton();
await tester.pumpWidget(button);
await tester.tap(find.byWidget(button));
expect(find.byType(MyNextView), findsOneWidget);
});
After >
await tester.tap(find.byWidget(button));
Put this line >
await tester.pumpAndSettle(); // Wait for screen to update
testWidgets('Button tap routes to next page', (WidgetTester tester) async {
final button = createButton();
await tester.pumpWidget(button);
await tester.tap(find.byWidget(button));
await tester.pumpAndSettle(); // Wait for screen to update
expect(find.byType(MyNextView), findsOneWidget);
});

Flutter widget test - wait Future completion

I have a widget test in flutter that pumps the widget under test,
and when it opens, performs a background operation (staticWrapper.requestPermission()) which returns a Future and, based on its result, set a state.
The problem is that the test is not waiting this future to complete, test code:
/// When micro permission denied, should show error message.
testWidgets('When micro permission denied, should show error message.',
(WidgetTester tester) async {
when(staticWrapper.requestPermission(Permission.RecordAudio))
.thenAnswer((_) => Future.value(PermissionStatus.denied));
await tester.pumpWidget(widget);
final loginText = find.text(callScreen_microPermissionDenied);
expect(loginText, findsOneWidget);
});
Affected widget code:
void _requestMicroPermission() async {
final result =
await staticWrapper.requestPermission(Permission.RecordAudio);
debugPrint("Microphone permission status: $result");
if (result == PermissionStatus.authorized) {
native.init();
} else {
setState(() {
_loginText = tnsManager.getText(TranslationsManager.callScreen_microPermissionDenied);
});
}
}
The expect() method is called before the setState() call.
Any help?
Finally I solved the issue using WidgetTester#pumpAndSettle():
testWidgets('When micro permission denied, should show error message.',
(WidgetTester tester) async {
when(staticWrapper.requestPermission(Permission.RecordAudio))
.thenAnswer((_) => Future.value(PermissionStatus.denied));
await tester.pumpWidget(widget);
await tester.pump();
final loginText = find.text(callScreen_microPermissionDenied);
expect(loginText, findsOneWidget);
});