Can not override clock.now() for Widget integration tests
Hi,
I'm trying to implement golden tests (compare app screenshots) for a project where the UI depends on the current date/time. I believe the most suggested solution to mock DateTime.now() is to use clock package. However, I can't get it to work inside the widgets.
Minimal reproduction code repo (no golden tests, though): link.
Code snippet
main.dart
import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
const kCurrentYearKey = 'current_year_key';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _currentYear = 0;
@override
void initState() {
_currentYear = clock.now().year;
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Mock current year'),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Current year is: '),
Text(
'$_currentYear',
key: const ValueKey(kCurrentYearKey),
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
);
}
}
integration_test/widget_test.dart
import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:mock_current_date_in_integration_tests/main.dart';
const mockedCurrentYear = 1999;
final mockedClock = Clock.fixed(DateTime(mockedCurrentYear));
void main() async {
await withClock(mockedClock, () async { // wrap 1
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
await withClock(mockedClock, () async { // wrap 2
testWidgets('Current year is mocked to be 1999', (WidgetTester tester) async {
await withClock(mockedClock, () async { // wrap 3
expect(clock.now().year, 1999); // passes. Thanks to 'wrap 3'
await tester.pumpWidget(const MyApp());
final finder = find.byKey(const ValueKey(kCurrentYearKey));
var currentYearText = finder.evaluate().single.widget as Text;
expect(currentYearText.data, '1999'); // fails
});
});
});
});
}
Issue which might be related: https://github.com/flutter/flutter/issues/96939
Checked on the latest Flutter (v3.32.8) in iOS simulator with latest clock (v1.1.2).
Please let me know if there is a solution for this or I'd better post it somewhere else.
@RustamG Any update for your issue?
@phuc-tranh-otsv Currently I just draw rectangles that overlay widgets depending on current date/time before passing to goldens comparator. So these widgets are actually not covered by golden tests.
A quick look at your test file makes me think that "wrap 2" is unnecessary, and "wrap 3" is necessary because the test framework just doesn't understand zones. (It uses them, but it uses them badly.)
(I really, really hope that will get fixed at some point, but that requires someone who understands both zones and the test framework. I only understand zones.)