tools icon indicating copy to clipboard operation
tools copied to clipboard

Can not override clock.now() for Widget integration tests

Open RustamG opened this issue 4 months ago • 3 comments

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 avatar Aug 08 '25 10:08 RustamG

@RustamG Any update for your issue?

phuc-tranh-otsv avatar Sep 20 '25 07:09 phuc-tranh-otsv

@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.

RustamG avatar Sep 21 '25 17:09 RustamG

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.)

lrhn avatar Sep 26 '25 14:09 lrhn