Bagaimana cara mengatur tanggal tiruan di Jest?


112

Saya menggunakan moment.js untuk melakukan sebagian besar logika tanggal saya di file helper untuk komponen React saya, tetapi saya belum dapat mengetahui cara membuat tiruan kencan di Jest a la sinon.useFakeTimers().

Dokumen Jest hanya berbicara tentang fungsi pengatur waktu seperti setTimeout, setIntervaldll tetapi tidak membantu dengan mengatur tanggal dan kemudian memeriksa bahwa fungsi tanggal saya melakukan apa yang seharusnya mereka lakukan.

Berikut beberapa file JS saya:

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;

dan inilah yang telah saya siapkan menggunakan 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);
    });

  });

});

Sekarang tes ini lulus karena saya menggunakan momen dan fungsi saya menggunakan momen tetapi tampaknya agak tidak stabil dan saya ingin menyetel tanggal ke waktu tetap untuk pengujian.

Ada gagasan tentang bagaimana itu bisa dicapai?

Jawaban:


70

MockDate dapat digunakan dalam tes lelucon untuk mengubah new Date()hasil:

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

Bekerja dengan baik karena saya menggunakan fungsi Datesuka lainnya valueOf().
Robin Zimmermann

144

Karena momentjs menggunakan secara Dateinternal, Anda bisa menimpa Date.nowfungsi tersebut untuk selalu mengembalikan momen yang sama.

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

atau

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

34
Berikut metode yang sedikit lebih cantik untuk menyetel tanggal sebenarnya yang akan dikembalikan:Date.now = jest.fn(() => new Date(Date.UTC(2017, 0, 1)).valueOf());
mengembangkan

4
Atau bahkan sedikit lebih cantik:Date.now = jest.fn(() => +new Date('2017-01-01');
mrzmyr

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

93

jest.spyOn berfungsi untuk mengunci waktu:

let dateNowSpy;

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

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

3
Solusi bagus; tidak ada ketergantungan dan menjaganya tetap dapat disetel ulang membuatnya mudah untuk diterapkan ke satu pengujian.
Caleb Miller

14
Tidak perlu dateNowSpyvariabel, dan mockReset()itu mubazir menurut jestjs.io/docs/en/mock-function-api.html#mockfnmockrestore . Di afterAll, Anda cukup melakukannyaDate.now.mockRestore()
Jimmy

ini bagus jadi Anda tidak memerlukan perpustakaan tambahan. Tetapi ini hanya akan benar-benar berfungsi jika Anda menggunakan metode Tanggal statis (yang tidak banyak)
hellatan

1
@Jimmy Date.now.mockRestore();memberikan Properti 'mockRestore' tidak ada pada tipe '() => nomor' kesalahan
Marco Lackovic

3
@Marco itu harus jest.spyOn (Tanggal, "sekarang"). MockRestore ();
sab

6

jest-date-mock adalah modul javascript lengkap yang saya buat, dan digunakan untuk menguji 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
});

Gunakan satu-satunya api 3 untuk kasus uji.

  • advanceBy (ms): memajukan cap waktu tanggal dengan ms.
  • advanceTo ([timestamp]): reset tanggal ke timestamp, default ke 0.
  • clear (): matikan sistem tiruan.

apa kasusmu
atool

5

Bagi mereka yang ingin meniru metode pada objek Tanggal baru, Anda dapat melakukan hal berikut:

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

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

Terima kasih, ini baru saja memperbaiki masalah yang saya alami.
Grayson Langford

2

Semua jawaban hanya berdasarkan tiruan Date.now()tidak akan berfungsi di mana-mana karena beberapa paket (misalnya moment.js) digunakan new Date()sebagai gantinya.

Dalam konteks ini jawaban berdasarkan MockDatesaya pikir satu-satunya yang benar. Jika Anda tidak ingin menggunakan paket eksternal, Anda dapat menulis langsung di 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

Saya ingin menawarkan beberapa pendekatan alternatif.

Jika Anda perlu stub format()(yang dapat bergantung pada lokal dan zona waktu!)

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

Jika Anda hanya perlu stub moment():

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

Mengenai pengujian isDateTodayfungsi di atas, saya yakin cara yang paling sederhana adalah dengan tidak mengejek momentsama sekali


2
Untuk contoh pertama, saya mendapatkanTypeError: moment.mockReturnValue is not a function
mkelley33

2
Apakah jest.mock("moment")pada tingkat yang sama dengan pernyataan impor Anda? Jika tidak, Anda dipersilakan untuk melihatnya beraksi dalam proyek ini
David

1

Beginilah cara saya mengejek Date.now()metode saya untuk menyetel tahun ke 2010 untuk pengujian saya

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

1

Berikut beberapa cara yang dapat dibaca untuk berbagai kasus penggunaan. Saya lebih suka menggunakan mata-mata daripada menyimpan referensi ke objek asli, yang secara tidak sengaja dapat ditimpa dalam beberapa kode lain.

Ejekan satu kali

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

Beberapa tes

let dateSpy;

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

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


0

Saya ingin menggunakan Manual Mocks, sehingga dapat digunakan di semua pengujian.

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

Tujuannya adalah untuk meniru Tanggal baru () dengan tanggal tetap di mana pun itu digunakan selama rendering komponen untuk tujuan pengujian. Menggunakan perpustakaan akan menjadi overhead jika satu-satunya hal yang Anda inginkan adalah meniru Date () fn baru.

Ide adalah untuk menyimpan tanggal global ke variabel temp, tiruan dae global dan kemudian setelah penggunaan menetapkan ulang temp ke tanggal global.

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



Jelaskan Jawaban Anda juga. menempatkan kode saja bukanlah pendekatan yang baik
Intsab Haider

1
Terima kasih untuk sarannya. Diperbarui dengan komentar.
Pranava S Balugari

0

Saya hanya ingin berpadu di sini karena tidak ada jawaban yang mengatasi masalah jika Anda ingin mengejek Dateobjek hanya dalam rangkaian tertentu.

Anda dapat memalsukannya menggunakan metode penyiapan dan pembongkaran untuk setiap suite, dokumen bercanda

/**
 * 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;
});

Semoga ini membantu!


0

Anda dapat menggunakan pembuat tanggal . Memungkinkan Anda mengubah tanggal saat ini secara relatif:

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();
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.