Dates in Jest
Tags: javascript, typescript, testing
Published: 2022-02-18 16:09:12

Many times I have found myself writing a test in which, for whatever reason, I need to *mock* somehow the result of the execution of the `now` method of the `Date` object and almost always I had to resort to Google to find the solution, so I decided to create this short article.

## Jest >= 26

First of all let's see how we can fix things if we are dealing with one of the latest versions of Jest (more specifically if we are working with version 26 or higher). In this case the `jest` object provides us with the `setSystemtime` method that will do precisely what we want: set the value we want the `now` method of the Date object to return.

But the invocation of this method alone is not the only thing we are going to need since we are also going to have to tell Jest that we are going to use the **modern** (current) method to return the *mocked* dates, which we will do by invoking the `useFakeTimers` method of the `jest` object. 

If we put this together what we could do is that in our test suite before any of the tests we need to run we set the *mocked* value for `now` with something like the following:

```js
beforeAll(() => {
  jest.useFakeTimers('modern')
  jest.setSystemTime(new Date(2022, 2, 1))
})
```

If we forget that we will have to restore the object that will generate the dates in the tests once the execution of the suite is finished. We will achieve this by invoking the `useRealTimers` method that also provides us with the `jest` object. 

```js
afterAll(() => {
  jest.useRealTimers()
})
```

In short, that the structure of our test suite should look something like the following:

````js
beforeAll(() => {
  jest.useFakeTimers('modern')
  jest.setSystemTime(new Date(2022, 2, 1))
})

afterAll(() => {
  jest.useRealTimers()
})

// All tests in our suite.
```

#In the versions previous to 26 of Jest we will have to make use of the `spyOn` method that Jest provides us in order to create a **spy** version of the `now` method, I understand that a version of this type is an implementation of the same one with the exception that it is going to return what we want. 

That is why when we are going to use it inside our tests what we would write would be something like the following (first we show it and then the explanation):

```js
test('my test', () => {
  const mockDate = new Date(2022, 2, 1)
  const spy = jest
    .spyOn(global, 'Date'
    .mockImplementation(() => mockDate))
```

What are we doing here? Well first of all we are creating the object that we want to return any call to the `Date` object inside our test assigning it the date that will be the one that will be considered *mocked*. Once this is done what we have to tell Jest is that we are going to create a **spy** on the object `Date` and not only that but thanks to the execution of the `mockImplementation` method what we will be indicating to it is that every time the `Date` function is invoked (that is to say, the function that allows to create a new object) what we want to return will always be our *mocked object.

From here we can continue with our tests without forgetting **never** to restore the implementation of the `Date` object thanks to the invocation of the `mockRestore` method that offers us the **spy** object that we have obtained as a result of the invocation of the `spyOn` method:

```js
spy.mockRestore()
```

In summary that the structure of our test would look something like:

````js
test('my test', () => {
  const mockDate = new Date(2022, 2, 1)
  const spy = jest
    .spyOn(global, 'Date'
    .mockImplementation(() => mockDate))

  // perform test operations....

  spy.mockRestore()
}
```

## What about TypeScript?

The problem with the approaches we have just seen is that from TypeScript's point of view they give us a type error similar to the following:

> Argument of type '() => Date' is not assignable to parameter of type '() => string'. Type 'Date' is not assignable to type 'string'.

What can we do in this case? Here the solution is to make use of a third party library being the most used [mockdate](https://www.npmjs.com/package/mockdate). But how do we apply it once we install it? The first thing we have to know is that this library will provide us with the ``MockDate`` object that will contain all the functionality we need so a first step will always be to import it:

```ts
import MockDate from 'mockdate'
```

And how can we use it? Well it is really simple because this object provides us with two methods to achieve our goal endo the first of them the `set` method that will expect to receive as parameter a `Date` object with the date we want to work with in our tests and the `reset` method that serves to *reset* the system date. This leaves us with that the implementation of one of our tests could be something like the following:

````ts
import MockDate from ``mockdate``.

it('my test', () => {
  MockDate.set(new Date(2022, 2, 1))

  // ... Operations of our test ....

  MockDate.reset()
})
```

Much simpler and clearer to understand!