Testing parts of your app that run on separate threads (isolates) in Flutter can be tricky in widget tests. If you just call a function that runs on an isolate directly in your test, it might not work as expected.
The Challenge
When you run code that uses compute
or other isolate methods directly in a widget test, the test environment might not wait for it properly. This can make your test hang and never finish, giving you a false result or just freezing.
The Hanging Code (DON'T)
testWidgets('upload file and save avatar', (tester) async {
final file = await rootBundle.load('assets/images/splashscreen.png');
final bytes = file.buffer.asUint8List();
final xfile = XFile.fromData(bytes);
final jpgData = await compute(_avatarThumbnail, file);
});
// This test will hang and never return
This happens because the isolate runs outside the main test environment's control.
Why it Fails
The test environment isn't designed to track asynchronous work happening on a separate isolate by default.
The Fix
Flutter provides a way to handle this in widget tests using tester.runAsync()
. Wrapping the asynchronous code that interacts with the isolate inside tester.runAsync()
tells the test framework to let this async work complete.
The Working Code (DO)
testWidgets('upload file and save avatar', (tester) async {
await tester.runAsync(() async { // <--- use this
final file = await rootBundle.load('assets/images/splashscreen.png');
final bytes = file.buffer.asUint8List();
final xfile = XFile.fromData(bytes);
final jpgData = await compute(_avatarThumbnail, file);
});
});
How it Works
tester.runAsync()
sets up a test zone that correctly manages and waits for the asynchronous tasks running on the isolate.
By using tester.runAsync()
, you ensure that your test waits for the isolate work to finish before completing. This makes your tests reliable and accurate, especially when dealing with background processing.