I have used a Future.delayed
to show a FAB in 1 second using this code:
Future.delayed(const Duration(seconds: 1), () {
setState(() {
_showFab = true;
});
});
Now the most basic smoke test has stopped working:
void main() {
testWidgets('smoke test', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
expect(find.byType(MyHomePage), findsOneWidget);
});
}
This is the error message:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test:
A Timer is still pending even after the widget tree was disposed.
'package:flutter_test/src/binding.dart': Failed assertion: line 933 pos 7:
'_currentFakeAsync.nonPeriodicTimerCount == 0' import 'dart:async';
Here is all the code used:
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _showFab = false;
@override
Widget build(BuildContext context) {
Future.delayed(const Duration(seconds: 1), () {
setState(() {
_showFab = true;
});
});
return Scaffold(
floatingActionButton: AnimatedOpacity(
opacity: _showFab ? 1.0 : 0.0,
duration: Duration(milliseconds: 1400),
child: FloatingActionButton(
onPressed: null,
child: Icon(Icons.add),
),
),
);
}
}
How can I change the unit test to make the test pass?
Creates a future that runs its computation after a delay. The computation will be executed after the given duration has passed, and the future is completed with the result of the computation.
The test framework runs with FakeAsync
by default, that has some limitations and might cause the error you get.
You can explicitly run with async proper like:
void main() {
testWidgets('smoke test', (WidgetTester tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(MyApp());
expect(find.byType(MyHomePage), findsOneWidget);
});
});
}
You might still need suggestions made in other answers.
You need to give the tester enough time to process all the async calls you have scheduled, try change your smoke test to something like this:
void main() {
testWidgets('smoke test', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
// Since you wait 1 second to start the animation and another 1.4 to complete it
await tester.pump(Duration(seconds: 3));
expect(find.byType(MyHomePage), findsOneWidget);
});
}
You will also need to move the Future.delayed
out of the build()
method, because this is causing a cyclic behaviour, every time you call setState()
the build()
is called again, change your state like that:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _showFab = false;
@override
void initState() {
Future.delayed(const Duration(seconds: 1), () {
setState(() {
_showFab = true;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: AnimatedOpacity(
opacity: _showFab ? 1.0 : 0.0,
duration: Duration(milliseconds: 1400),
child: FloatingActionButton(
onPressed: null,
child: Icon(Icons.add),
),
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With