I am writing tests for a Widget that makes use of a Floor database to store and retrieve data. The test cases use a inMemory database instead of a mocked one. This means all the data operations are genuinely async in the tests as well. The problem I am facing is that my async method calls are being completed well after the tests completed.
I have the following unit test:
testWidgets('test add new set', (WidgetTester tester) async {
await pumpApp(tester, exercisesProvider, setRepository, exerciseRepository);
await tester.runAsync(() async {
var bench = find.text('Bench Press');
expect(bench, findsOneWidget);
await tester.tap(bench);
await tester.pump();
await tester.pump();
// Should be found if content is loaded
expect(find.text('Clear'), findsOneWidget);
});
The test fails with the following output:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown while running async test code:
Expected: exactly one matching node in the widget tree
Actual: _TextFinder:<zero widgets with text "Clear" (ignoring offstage widgets)>
Which: means none were found but one was expected
...
The exception was caught asynchronously.
════════════════════════════════════════════════════════════════════════════════════════════════════
FETCH COMPLETED
The widget under test:
@override
Widget build(BuildContext context) {
// This async method is executed too late
Provider.of<SetProvider>(context, listen: false)
.fetchGroupedSetsByExercise(widget.selectedExercise.id!);
return Column(
children: [
Expanded(
child: Consumer<SetProvider>(builder: (context, setProvider, chil
final groupedSets = setProvider.groupedSets;
if (setProvider.groupedSetsState == IOState.loading ||
setProvider.groupedSetsState == IOState.initial) {
return ...;
} else if (setProvider.groupedSetsState == IOState.error) {
return ...;
} else if (groupedSets.isEmpty) {
return ...;
}
// Contains the clear button and more
return buildSetList(groupedSets, setProvider);
}),
),
],
);
The provider:
Future<void> fetchGroupedSetsByExercise(int exerciseID,
{bool? reverse}) async {
groupedSets = groupSetsByDate(
await fetchSetsByExercise(exerciseID, reverse: reverse));
lastSetEntry = groupedSets.values.lastOrNull?.lastOrNull;
groupedSetsState = IOState.success;
notifyListeners();
print("FETCH COMPLETED");
}
As you can see in the test output fetchGroupedSetsByExercise is always executed after the test is already over. I am aware that you need to use await to wait for a async function to complete however, how do I test my widget? My widget keeps track of the fetch state itself (in groupedSetsState) and I cannot await anything, rather the provider will notify the widget once the data arrived.
I have also tried using:
FutureBuilder- Awaiting multiple
pumpcalls - Awaiting
pumpAndSettle - Removing the
tester.runAsyncline
All with the same result, the test fails because the async method is being executed too late. How do I write genuine async tests like in my case?
Turns out my overall setup was correct but the
Floorpackage requires you to add this line to the test whenever you need toawaitsomeasyncFlooroperations:The entire code looks like this now:
I found this info in the unit tests of their example project.