# Controlling time in Dart unit tests, the better way

Source: https://iiro.dev/controlling-time-with-package-clock/
Published: 2020-11-14

How to control DateTime.now() in Dart unit tests - the better way.

---

In Dart, we can get the current date and time by calling `DateTime.now()`.

That's easy enough.
But what's not that easy is testing it.
Since there's no built-in way to override what it returns, any Dart code that directly depends on `DateTime.now()` is not testable.

> If you'd want that to be a part of the Dart SDK, there's an open [GitHub issue][gh:datetime-not-mockable] you might want to give a thumbs up on.

However, there's a very neat way to tackle this, and it's not what you might think.

## Why testing DateTime.now() is hard

Let's imagine that we're making an app that tells the current day of the week.

We have a class called `Greeter` that returns the appropriate greeting for each day.


**File:** `greeter.dart`


```dart
import 'package:intl/intl.dart';

final _format = DateFormat('EEEE');

class Greeter {
  String greet() {
    final now = DateTime.now();
    final dayOfWeek = _format.format(now);
    return 'Happy $dayOfWeek, you!';
  }
}
```


For example, if it's Tuesday, `Greeter().greet()` returns _"Happy Tuesday, you!"_.

> I actually have a colleague who wishes _"Happy &lt;current day&gt;!"_ every time he comes to the office.
>
> Yes, even on Mondays. To him, every day seems to be a happy day.

### Writing some failing tests

We want to be sure nobody introduces bugs to the very important greeting functionality, so we write a test case:


**File:** `greeter_test.dart`


```dart
void main() {
  test('returns a proper greeting on a Tuesday', () {
    final greeting = Greeter().greet();

    // How on Earth do we make this expectation pass
    // if it's not Tuesday when we run this test?
    expect(
      greeting,
      'Happy Tuesday, you!',
    );
  });
}
```


The problem with this test is that it passes only once a week.

Unless our team agrees to only run tests on Tuesdays, that test case is not very useful. 
To make it pass every day of the week, we need to be able to control the current time somehow.

### Making it testable - the usual way

We could refactor `Greeter` to something like this:


**File:** `greeter.dart`


```dart
DateTime _getSystemTime() => DateTime.now();

class Greeter {
  Greeter([this._getCurrentTime = _getSystemTime]);
  final DateTime Function() _getCurrentTime;

  String greet() {
    final now = _getCurrentTime();
    final dayOfWeek = _format.format(now);
    return 'Happy $dayOfWeek, you!';
  }
}
```


The class now has an optional constructor parameter: a function called `_getCurrentTime()`.

By default, it returns the current time, but the behavior can be overridden.
In our tests, we pass `Greeter` a function that returns a fixed `DateTime` instead.


**File:** `greeter_test.dart`


```dart
void main() {
  test('returns a proper greeting on a Tuesday', () {
    final greeting = Greeter(() => DateTime(2020, 09, 01)).greet();
    expect(greeting, 'Happy Tuesday, you!');
  });
}
```


With these changes, our tests now pass 7 days a week!

## The end?

That approach works for the very simple use case we had, but it's not a fun thing to do at scale.

We would need to do this dance with every single class or widget that needs the current datetime.
This inevitably results in "prop drilling" when dealing with widgets and other nested classes.
That's not fun.

> _Prop drilling_ is a term that comes from the ReactJS world, but also applies to Flutter.
>
> It means having to pass the same arguments again and again down to child widgets in highly nested widget trees.

> Well, what about InheritedWidget - or better yet, provider?

We could come up with a class called `Clock` that encapsulates the behavior:


```dart
DateTime _defaultTime() => DateTime.now();

class Clock {
  Clock([this.now = _defaultTime]);
  final DateTime Function() now;
}
```


We could then pass `Clock` down with an `InheritedWidget` or [provider][pub:provider] to make it available for a subtree.

This would avoid the problem of prop drilling and still keep the logic testable.
However, we wouldn't be able to access current time without a `BuildContext`.
It would also complicate accessing the `Clock` in `initState()`, since `context` is not available there yet.

Also, if we have a lot of Dart classes depending on the current datetime that are not Flutter widgets, we'd still have to pass the `Clock` as a constructor argument over and over again.

There's got to be a better way!

### What about just making Clock a singleton?

That would work.

I did it years back myself with inKino.
You can see [the source code for Clock here][gh:inKino-clock-code], and [here's an example test case][gh:inKino-clock-test], although it's a bit specific to Redux.

However, I can't help but feel a bit off with this one.

It's global behavior that can be overridden from anywhere.
This could result in frustrating, hard to notice bugs.

## The better way - controlling time with package:clock {#using-clock}

Even though there's no built-in way to mock `DateTime.now()`, there's a good alternative that comes pretty close.

It's [a package called clock][pub:clock], and it's maintained by the Dart team.

The package exposes a top-level variable called `clock`.
To get the current time, we call `clock.now()`:


```dart
import 'package:clock/clock.dart';

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


In order to override the current time, we wrap any code that calls `clock.now()` with a function called `withClock`:


```dart
import 'package:clock/clock.dart';

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


The `withClock` function takes in two arguments:
1. a `Clock` - in our case, a clock with a fixed date set to 2000/01/01.
2. a function, inside of which the `clock` getter always returns what we passed in as the first argument.

That's pretty neat.

It also only requires minimal changes to existing code.
And there's no need to add yet another constructor parameter and pass stuff around.

### How does it work?

Short answer: it leverages [Zone-local values][zone-local-values].

Here's how the `withClock` function looks like:


```dart
class Clock {
  // ...
}

// An unique object to use as a key for the clock.
final _clockKey = Object();

T withClock<T>(Clock clock, T Function() callback) {
  return runZoned(
    callback,
    zoneValues: {
      // Override _clockKey with the provided clock.
      _clockKey: clock,
    },
  );
}
```


> Why is the key a new instance of an `Object()` instead of a unique string?
>
> Well, since `Object() != Object()`, creating an `Object()` and using that as the key is the simplest way to create a unique key for the clock.

It runs the passed anonymous function in a new `Zone`, setting a Zone-local value for the `_clockKey` to contain the provided clock.

Any code inside `callback` that queries `_clockKey` from the current Zone will receive an overridden `Clock`.

You might see where this is going.


```dart
// Look up if the current Zone has a clock assigned, and
// return that. If not, return a fresh new instance of Clock.
Clock get clock => Zone.current[_clockKey] as Clock? ?? Clock();
```


The top-level `clock` getter checks for the same `_clockKey` in the current Zone.

If a `Clock` already exists in the current Zone (=we have wrapped that piece of code with the `withClock` function), it returns the provided `Clock`.

Otherwise, it returns a fresh instance of `Clock` that defaults to returning `DateTime.now()`.

## Fixing our tests

Now that we have this brand new trick up our sleeves, we'll finally make our sample test case work properly.

Let's implement this in our `Greeter` class. To make it work, we  only need to make one tiny modification to greeter.dart:


**File:** `greeter.dart`


```dart
import 'package:clock/clock.dart';

class Greeter {
  String greet() {
    // ...
    final now = clock.now();
    // ...
  }
}
```


After that, we wrap our tests with the `withClock()` function:


**File:** `greeter_test.dart`


```dart
void main() {
  test('returns a proper greeting on a Tuesday', () {
    final greeting = withClock(
      Clock.fixed(DateTime(2020, 09, 01)),
      () => Greeter().greet(),
    );

    expect(greeting, 'Happy Tuesday, you!');
  });
}
```


Now the test passes on every day of the week - not just Tuesday!

## Other use cases

Aside from overriding current time in just unit tests, depending on your app, you might benefit from this in your manual testing too.

For example, in Reflectly, we have some things that should be only loaded once a day.
In order to test this manually, I just wrap `runApp` with `withClock` that starts with the current date.

Then I bump the day by one to simulate tomorrow, the day after, and so on.

[gh:datetime-not-mockable]: https://github.com/dart-lang/sdk/issues/28985
[gh:inKino-clock-code]: https://github.com/roughike/inKino/blob/64bc20dfb5bfb31e3fd3b623e6b07500dd6e6c0f/core/lib/src/utils/clock.dart
[gh:inKino-clock-test]: https://github.com/roughike/inKino/blob/64bc20dfb5bfb31e3fd3b623e6b07500dd6e6c0f/core/test/redux/show_middleware_test.dart#L164-L189
[pub:clock]: https://pub.dev/packages/clock
[pub:built_value]: https://pub.dev/packages/built_value
[pub:freezed]: https://pub.dev/packages/freezed
[pub:provider]: https://pub.dev/packages/provider
[zone-local-values]: https://dart.dev/articles/archive/zones#storing-zone-local-values
