'How to test code that uses DateTime.now in Flutter?

I have this class:

import 'package:flutter/material.dart';

class AgeText extends StatelessWidget {
  final String dateOfBirth;

  const AgeText({Key key, @required this.dateOfBirth}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final age = _calculateAge();
    return Text(age.toString());
  }

  int _calculateAge() {
    final dateOfBirthDate = DateTime.parse(dateOfBirth);
    final difference = DateTime.now().difference(dateOfBirthDate);
    final age = difference.inDays / 365;

    return age.floor();
  }
}

I'd like to test that it produces the correct age when a date of birth is passed into it. What is the best way to do this in Flutter?


SOLUTION: For those interested, here's the solution using @Günter Zöchbauer's suggestion of the clock package.

My widget class:

import 'package:flutter/material.dart';
import 'package:clock/clock.dart';

class AgeText extends StatelessWidget {
  final String dateOfBirth;
  final Clock clock;

  const AgeText({Key key, @required this.dateOfBirth, this.clock = const Clock()}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final age = _calculateAge();
    return Text(age.toString());
  }

  int _calculateAge() {
    final dateOfBirthDate = DateTime.parse(dateOfBirth);
    final difference = clock.now().difference(dateOfBirthDate);
    final age = difference.inDays / 365;

    return age.floor();
  }
}

and my test class:

import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/age.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets("shows age 30 when date of birth is 30 years ago", (WidgetTester tester) async {
    final mockClock = Clock.fixed(DateTime(2000, 01, 01));
    final testableWidget = MaterialApp(
      home: AgeText(
        dateOfBirth: "1970-01-01T00:00:00",
        clock: mockClock,
      ),
    );

    await tester.pumpWidget(testableWidget);

    expect(find.text("30"), findsOneWidget);
  });
}


Solution 1:[1]

If you use the clock package for code depending on DateTime.now() you can easily mock it.

I don't think there is a good way without a wrapper around DateTime.now() licke the clock package provides.

Solution 2:[2]

As Günter said, the clock package, maintained by the Dart team, provides a very neat way to achieve this.

Normal usage:

import 'package:clock/clock.dart';

void main() {
  // prints current date and time
  print(clock.now());
}

Overriding the current time:

import 'package:clock/clock.dart';

void main() {
  withClock(
    Clock.fixed(DateTime(2000)),
    () {
      // always prints 2000-01-01 00:00:00.
      print(clock.now());
    },
  );
}

I wrote about this in more detail on my blog.

For widget tests, you need to wrap pumpWidget, pump and expect in the withClock callback.

Solution 3:[3]

As mentioned here: https://stackoverflow.com/a/63073876/2235274 implement extension on DateTime.

extension CustomizableDateTime on DateTime {
  static DateTime _customTime;
  static DateTime get current {
    return _customTime ?? DateTime.now();
  }

  static set customTime(DateTime customTime) {
    _customTime = customTime;
  }
}

Then just use CustomizableDateTime.current in the production code. You can modify the returned value in tests like that: CustomizableDateTime.customTime = DateTime.parse("1969-07-20 20:18:04");. There is no need to use third party libraries.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Günter Zöchbauer
Solution 2
Solution 3