Как установить дату макета в Jest?
Я использую moment.js для выполнения большей части моей логики даты в вспомогательном файле для своих компонентов React, но мне не удалось выяснить, как издеваться над датой в Jest a la sinon.useFakeTimers().
В Jest-документах говорят только о функциях таймера, таких как setTimeout, setInveral и т.д., но не помогают установить дату, а затем проверяют, что мои функции даты выполняют то, что они предназначены.
Вот некоторые из моих JS файлов:
var moment = require('moment');
var DateHelper = {
DATE_FORMAT: 'MMMM D',
API_DATE_FORMAT: 'YYYY-MM-DD',
formatDate: function(date) {
return date.format(this.DATE_FORMAT);
},
isDateToday: function(date) {
return this.formatDate(date) === this.formatDate(moment());
}
};
module.exports = DateHelper;
и вот что я создал с помощью Jest:
jest.dontMock('../../../dashboard/calendar/date-helper')
.dontMock('moment');
describe('DateHelper', function() {
var DateHelper = require('../../../dashboard/calendar/date-helper'),
moment = require('moment'),
DATE_FORMAT = 'MMMM D';
describe('formatDate', function() {
it('should return the date formatted as DATE_FORMAT', function() {
var unformattedDate = moment('2014-05-12T00:00:00.000Z'),
formattedDate = DateHelper.formatDate(unformattedDate);
expect(formattedDate).toEqual('May 12');
});
});
describe('isDateToday', function() {
it('should return true if the passed in date is today', function() {
var today = moment();
expect(DateHelper.isDateToday(today)).toEqual(true);
});
});
});
Теперь эти тесты проходят, потому что я использую момент, а мои функции используют момент, но он кажется немного нестабильным, и я хотел бы установить дату на определенное время для тестов.
Любая идея о том, как это можно сделать?
Ответы
Ответ 1
MockDate можно использовать в тестах шутки, чтобы изменить то, что возвращает new Date()
:
var MockDate = require('mockdate');
// I use a timestamp to make sure the date stays fixed to the ms
MockDate.set(1434319925275);
// test code here
// reset to native Date()
MockDate.reset();
Ответ 2
Так как momentjs использует Date
внутренне, вы можете просто перезаписать функцию Date.now
, чтобы всегда возвращать тот же момент.
Date.now = jest.fn(() => 1487076708000) //14.02.2017
Ответ 3
jest.spyOn работает на время блокировки:
let dateNowSpy;
beforeAll(() => {
// Lock Time
dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => 1487076708000);
});
afterAll(() => {
// Unlock Time
dateNowSpy.mockRestore();
});
Ответ 4
jest-date-mock - это полный модуль javascript, написанный мной, и он используется для проверки даты на jest.
import { advanceBy, advanceTo } from 'jest-date-mock';
test('usage', () => {
advanceTo(new Date(2018, 5, 27, 0, 0, 0)); // reset to date time.
const now = Date.now();
advanceBy(3000); // advance time 3 seconds
expect(+new Date() - now).toBe(3000);
advanceBy(-1000); // advance time -1 second
expect(+new Date() - now).toBe(2000);
clear();
Date.now(); // will got current timestamp
});
Используйте только 3 API для тестовых случаев.
- advanceBy (мс): временная метка даты перехода на мс.
- advanceTo ([timestamp]): сброс даты до отметки времени, по умолчанию 0.
- очистить(): выключить макет системы.
Ответ 5
Все ответы, основанные только на макете Date.now()
, не будут работать везде, так как некоторые пакеты (например, moment.js
new Date()
вместо этого используют new Date()
.
В этом контексте ответ, основанный на MockDate
является, я думаю, единственно верным. Если вы не хотите использовать внешний пакет, вы можете написать прямо в beforeAll
:
const DATE_TO_USE = new Date('2017-02-02T12:54:59.218Z');
// eslint-disable-next-line no-underscore-dangle
const _Date = Date;
const MockDate = (...args) => {
switch (args.length) {
case 0:
return DATE_TO_USE;
default:
return new _Date(...args);
}
};
MockDate.UTC = _Date.UTC;
MockDate.now = () => DATE_TO_USE.getTime();
MockDate.parse = _Date.parse;
MockDate.toString = _Date.toString;
MockDate.prototype = _Date.prototype;
global.Date = MockDate;
Ответ 6
Я хотел бы предложить несколько альтернативных подходов.
Если вам нужна заглушка format()
(которая может зависеть от локали и часового пояса!)
import moment from "moment";
...
jest.mock("moment");
...
const format = jest.fn(() => 'April 11, 2019')
moment.mockReturnValue({ format })
Если вам нужно только заглушить moment()
:
import moment from "moment";
...
jest.mock("moment");
...
const now = "moment(\"2019-04-11T09:44:57.299\")";
moment.mockReturnValue(now);
Что касается теста для функции isDateToday
приведенного выше, я считаю, что самым простым способом было бы вообще не пародировать moment
Ответ 7
Я хотел бы использовать ручные макеты, чтобы его можно было использовать во всех тестах.
// <rootDir>/__mocks__/moment.js
const moment = jest.requireActual('moment')
Date.now = jest.fn(() => 1558281600000) // 2019-05-20 00:00:00.000+08:00
module.exports = moment
Ответ 8
Цель состоит в том, чтобы макетировать новую функцию Date() с фиксированной датой, где бы она ни использовалась во время рендеринга компонента в целях тестирования. Использование библиотек будет непростым делом, если единственное, что вам нужно - это смоделировать new Date() fn.
Идея состоит в том, чтобы сохранить глобальную дату во временной переменной, смоделировать глобальную переменную и затем после использования переназначить временную глобальную дату.
export const stubbifyDate = (mockedDate: Date) => {
/**
* Set Date to a new Variable
*/
const MockedRealDate = global.Date;
/**
* Mock Real date with the date passed from the test
*/
(global.Date as any) = class extends MockedRealDate {
constructor() {
super()
return new MockedRealDate(mockedDate)
}
}
/**
* Reset global.Date to original Date (MockedRealDate) after every test
*/
afterEach(() => {
global.Date = MockedRealDate
})
}
Usage in your test would be like
import { stubbyifyDate } from './AboveMethodImplementedFile'
describe('<YourComponent />', () => {
it('renders and matches snapshot', () => {
const date = new Date('2019-02-18')
stubbifyDate(date)
const component = renderer.create(
<YourComponent data={}/>
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});