Làm cách nào để đặt một ngày giả trong Jest?


111

Tôi đang sử dụng moment.js để thực hiện hầu hết logic ngày tháng của mình trong tệp trợ giúp cho các thành phần React của mình nhưng tôi không thể tìm ra cách giả lập ngày trong Jest a la sinon.useFakeTimers().

Các tài liệu của Jest chỉ nói về các chức năng hẹn giờ như setTimeout, setIntervalv.v. nhưng không giúp đặt ngày và sau đó kiểm tra xem các chức năng ngày của tôi có làm đúng những gì chúng cần làm hay không.

Đây là một số tệp JS của tôi:

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;

và đây là những gì tôi đã thiết lập bằng 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);
    });

  });

});

Bây giờ các bài kiểm tra này vượt qua vì tôi đang sử dụng thời điểm và các chức năng của tôi sử dụng thời điểm nhưng nó có vẻ hơi không ổn định và tôi muốn đặt ngày thành thời gian cố định cho các bài kiểm tra.

Bất kỳ ý tưởng về cách có thể được thực hiện?

Câu trả lời:


70

MockDate có thể được sử dụng trong các thử nghiệm jest để thay đổi những gì new Date()trả về:

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();

Hoạt động tốt vì tôi đang sử dụng các chức năng khác của Datelike valueOf().
Robin Zimmermann

143

Vì momentjs sử dụng Datenội bộ, bạn chỉ có thể ghi đè Date.nowhàm để luôn trả lại khoảnh khắc tương tự.

Date.now = jest.fn(() => 1487076708000) //14.02.2017

hoặc là

Date.now = jest.fn(() => new Date(Date.UTC(2017, 1, 14)).valueOf())

34
Đây là một phương pháp đẹp hơn một chút để đặt ngày thực tế sẽ được trả lại:Date.now = jest.fn(() => new Date(Date.UTC(2017, 0, 1)).valueOf());
phát triển

4
Hoặc thậm chí một chút đẹp hơn:Date.now = jest.fn(() => +new Date('2017-01-01');
mrzmyr

3
HOẶC:Date.now = jest.fn(() => Date.parse('2017-02-14))
Jeremy Eaton

93

jest.spyOn hoạt động để khóa thời gian:

let dateNowSpy;

beforeAll(() => {
    // Lock Time
    dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => 1487076708000);
});

afterAll(() => {
    // Unlock Time
    dateNowSpy.mockRestore();
});

3
Giải pháp tuyệt vời; không có phụ thuộc và giữ nó có thể đặt lại giúp dễ dàng áp dụng cho một thử nghiệm duy nhất.
Caleb Miller

14
Không cần dateNowSpybiến và biến mockReset()là dư thừa theo jestjs.io/docs/en/mock- Chức năng-api.html#mockfnmockrestore . Trong afterAll, bạn có thể đơn giản làmDate.now.mockRestore()
Jimmy

điều này thật tuyệt nên bạn không cần thêm bất kỳ thư viện nào. Nhưng điều này sẽ chỉ thực sự hiệu quả nếu bạn đang sử dụng các phương thức Ngày tĩnh (không nhiều)
hellatan 12/1218

1
@Jimmy Date.now.mockRestore();đưa ra một tài sản 'mockRestore' không tồn tại trên loại '() => số' lỗi
Marco Lackovic

3
@Marco nó phải là jest.spyOn (Ngày, "bây giờ"). MockRestore ();
sab

6

jest-date-mock là một mô-đun javascript hoàn chỉnh do tôi viết và nó được sử dụng để kiểm tra Date on 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
});

Sử dụng 3 api duy nhất cho các trường hợp thử nghiệm.

  • AdvanceBy (ms): dấu thời gian ngày trước tính bằng ms.
  • AdvanceTo ([timestamp]): đặt lại ngày thành dấu thời gian, mặc định là 0.
  • clear (): tắt hệ thống giả lập.

trường hợp của bạn là gì
atool

5

Đối với những người muốn mô phỏng các phương thức trên một đối tượng Date mới, bạn có thể làm như sau:

beforeEach(() => {
    jest.spyOn(Date.prototype, 'getDay').mockReturnValue(2);
    jest.spyOn(Date.prototype, 'toISOString').mockReturnValue('2000-01-01T00:00:00.000Z');
});

afterEach(() => {
    jest.restoreAll()
});

Cảm ơn, điều này vừa khắc phục sự cố tôi đang gặp phải.
Grayson Langford

2

Tất cả câu trả lời chỉ dựa trên mô hình của Date.now()sẽ không hoạt động ở mọi nơi vì một số gói (ví dụ moment.js) sử dụng new Date()thay thế.

Trong bối cảnh này, câu trả lời dựa trên MockDatelà tôi nghĩ duy nhất thực sự chính xác. Nếu bạn không muốn sử dụng gói bên ngoài, bạn có thể viết trực tiếp trong 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;

2

Tôi muốn đưa ra một số cách tiếp cận thay thế.

Nếu bạn cần khai báo format()(có thể phụ thuộc vào ngôn ngữ và múi giờ!)

import moment from "moment";
...
jest.mock("moment");
...
const format = jest.fn(() => 'April 11, 2019')
moment.mockReturnValue({ format })

Nếu bạn chỉ cần sơ khai moment():

import moment from "moment";
...
jest.mock("moment");
...
const now = "moment(\"2019-04-11T09:44:57.299\")";
moment.mockReturnValue(now);

Về bài kiểm tra cho isDateTodaychức năng ở trên, tôi tin rằng cách đơn giản nhất sẽ không phải là giả momentcả


2
Đối với ví dụ đầu tiên, tôi nhận đượcTypeError: moment.mockReturnValue is not a function
mkelley33

2
jest.mock("moment")cùng cấp với các câu lệnh nhập của bạn không? Nếu không, bạn có thể thấy nó hoạt động trong dự án này
David

1

Đây là cách tôi chế nhạo Date.now()phương pháp của mình để đặt năm là 2010 cho bài kiểm tra của tôi

jest
  .spyOn(global.Date, 'now')
  .mockImplementationOnce(() => new Date(`2010`).valueOf());

1

Dưới đây là một số cách có thể đọc được cho các trường hợp sử dụng khác nhau. Tôi thích sử dụng gián điệp hơn là lưu các tham chiếu đến các đối tượng gốc, có thể vô tình bị ghi đè trong một số mã khác.

Chế giễu một lần

jest
  .spyOn(global.Date, 'now')
  .mockImplementationOnce(() => Date.parse('2020-02-14'));

Một vài thử nghiệm

let dateSpy;

beforeAll(() => {
  dateSpy = jest
    .spyOn(global.Date, 'now')
    .mockImplementation(() => Date.parse('2020-02-14'));
});

afterAll(() => {
  dateSpy.mockRestore();
});

0

Tôi muốn sử dụng Mô hình thủ công, vì vậy nó có thể sử dụng trong tất cả các thử nghiệm.

// <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

0

Mục tiêu là giả lập Date () mới với một ngày cố định ở bất kỳ nơi nào nó được sử dụng trong quá trình hiển thị thành phần cho mục đích thử nghiệm. Việc sử dụng các thư viện sẽ trở nên tốn kém nếu điều duy nhất bạn muốn là giả lập Date () fn mới.

Ý tưởng là lưu trữ ngày toàn cầu vào một biến tạm thời, giả lập dae toàn cầu và sau đó sau khi sử dụng, gán lại tạm thời cho ngày toàn cầu.

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();
    });
});



Giải thích câu trả lời của bạn là tốt. chỉ đặt mã không phải là cách tiếp cận tốt
Intsab Haider

1
Cám ơn vì sự gợi ý. Cập nhật với các bình luận.
Pranava S Balugari

0

Tôi chỉ muốn nói ở đây vì không có câu trả lời nào giải quyết vấn đề nếu bạn muốn chế nhạo Dateđối tượng chỉ trong một bộ cụ thể.

Bạn có thể mô phỏng nó bằng cách sử dụng các phương pháp thiết lập và xé nhỏ cho từng bộ, jest docs

/**
 * Mocking Date for this test suite
 */
const globalDate = Date;

beforeAll(() => {
  // Mocked Date: 2020-01-08
  Date.now = jest.fn(() => new Date(Date.UTC(2020, 0, 8)).valueOf());
});

afterAll(() => {
  global.Date = globalDate;
});

Hi vọng điêu nay co ich!


0

Bạn có thể sử dụng date-faker . Cho phép bạn thay đổi ngày hiện tại một cách tương đối:

import { dateFaker } from 'date-faker';
// or require if you wish: var { dateFaker } = require('date-faker');

// make current date to be tomorrow
dateFaker.add(1, 'day'); // 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond'.

// change using many units
dateFaker.add({ year: 1, month: -2, day: 3 });

// set specific date, type: Date or string
dateFaker.set('2019/01/24');

// reset
dateFaker.reset();

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.