Il y a des situations où new Date()
ou Date.now
est utilisée dans le code de l’application. Ce code doit être testé, et il est toujours difficile de se rappeler comment le moquer.
L’utilisation de Date.now
vs new Date()
#
Date.now()
retourne le temps au format unix, c’est à dire “le nombre de millisecondes écoulées depuis le 1er janvier 1970 00:00:00 UTC”. (voir Date.now
sur MDN).
new Date()
retourne un nouvel objet Date, et se comporte différemment en fonction de l’entrée qui lui est passée. S’il est appelé sans rien, il retourne la date actuelle.
Les valeurs suivantes sont équivalentes, bien que les valeurs stockées dans les deux variables soient strictement différentes.
const now = new Date();
const explicitNow = new Date(Date.now());
Les valeurs sont strictement différentes parce que le “now” est calculé à des moments différents, mais comme le constructeur Date (new Date()) supporte le passage d’un temps unix à celui-ci, les deux sont équivalents.
L’utilisation de la new Date(Date.now())
rend le code beaucoup plus facile à tester. Mocker une fonction qui retourne un nombre (comme Date.now) est beaucoup plus facile que se moquer d’un constructeur.
Par exemple, le code complet du paquet jest-mock-now est le suivant (voir le code sur github.com/mattiaerre/jest-mock-now) :
const { NOW } = require('./constants');
module.exports = date => {
const now = date ? date.getTime() : NOW;
Date.now = jest.spyOn(Date, 'now').mockImplementation(() => now);
return now;
};
Remplacer Date.now
par un substitut#
const literallyJustDateNow = () => Date.now();
test('It should call and return Date.now()', () => {
const realDateNow = Date.now.bind(global.Date);
const dateNowStub = jest.fn(() => 1530518207007);
global.Date.now = dateNowStub;
expect(literallyJustDateNow()).toBe(1530518207007);
expect(dateNowStub).toHaveBeenCalled();
global.Date.now = realDateNow;
});
Ce n’est pas vraiment une astuce spécifique à Jest, nous accédons simplement à l’objet global
de Node et remplaçons Date.now
par un stub.
Espionnez Date.now et ajoutez une implémentation fictive#
Une implémentation simplifiée d’un test similaire serait d’utiliser jest.spyOn(global.Date,'now').mockImplementation()
.
Notre implémentation du mock utilisera une date codée en dur initialisée à l’aide de la new Date('valid-date-string')
et return valueOf()
, qui correspond à l’heure unix de cette date.
const getNow = () => new Date(Date.now());
test('It should create correct now Date', () => {
jest
.spyOn(global.Date, 'now')
.mockImplementationOnce(() =>
new Date('2019-08-24T11:01:58.135Z').valueOf()
);
expect(getNow()).toEqual(new Date('2019-08-24T11:01:58.135Z'));
});
Cela a l’avantage de ne pas avoir à remplacer la date réelle ou à la remettre à plus tard.
Mocker toute la classe Date avec une instance à date fixe#
const getCurrentDate = () => new Date();
let realDate;
test('It should create new date', () => {
// Setup
const currentDate = new Date('2019-08-24T11:01:58.135Z');
realDate = Date;
global.Date = class extends Date {
constructor(date) {
if (date) {
return super(date);
}
return currentDate;
}
};
expect(getCurrentDate()).toEqual(new Date('2019-08-24T11:01:58.135Z'));
// Cleanup
global.Date = realDate;
});
Source Github: “Mocking current time for Date”
Comme nous l’avons mentionné dans l’introduction, se moquer de toute la classe est très brutal.
Espionnez le nouveau constructeur Date() et ajoutez une implémentation fictive#
const getCurrentDate = () => new Date();
test('It should create new date', () => {
jest
.spyOn(global, 'Date')
.mockImplementationOnce(() => new Date('2019-08-24T11:01:58.135Z'));
expect(getCurrentDate()).toEqual(new Date('2019-08-24T11:01:58.135Z'));
});
C’est joli et concis, mais repose sur JavaScript hisser la folie et de savoir que Jest vous permet de simuler global.date.