Wie setze ich ein Scheindatum in Jest?

111

Ich verwende moment.js, um den größten Teil meiner Datumslogik in einer Hilfedatei für meine React-Komponenten auszuführen, aber ich konnte nicht herausfinden, wie ich ein Datum in Jest a la verspotten kann sinon.useFakeTimers().

Die Jest-Dokumente sprechen nur über Timer-Funktionen wie setTimeout , setIntervaletc , aber nicht Hilfe mit einem Datum festlegen und dann prüfen, ob meine Datumsfunktionen zu tun , was sie tun soll.

Hier sind einige meiner JS-Dateien:

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;

und hier ist, was ich mit Jest eingerichtet habe:

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

  });

});

Jetzt bestehen diese Tests, weil ich moment verwende und meine Funktionen moment verwenden, aber es scheint ein bisschen instabil zu sein und ich möchte das Datum auf eine feste Zeit für die Tests einstellen.

Irgendeine Idee, wie das erreicht werden könnte?

Alengel
quelle

Antworten:

70

MockDate kann in Scherztests verwendet werden, um zu ändern, was new Date()zurückgibt:

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();
eadmundo
quelle
Hat super funktioniert, weil ich andere Funktionen von Datelike verwendet habe valueOf().
Robin Zimmermann
143

Da momentjs Dateintern verwendet wird, können Sie die Date.nowFunktion einfach überschreiben , um immer den gleichen Moment zurückzugeben.

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

oder

Date.now = jest.fn(() => new Date(Date.UTC(2017, 1, 14)).valueOf())
Stereodenis
quelle
34
Hier ist eine etwas schönere Methode, um das tatsächliche Datum festzulegen, das zurückgegeben werden soll:Date.now = jest.fn(() => new Date(Date.UTC(2017, 0, 1)).valueOf());
Entwickeln
4
Oder noch ein bisschen hübscher:Date.now = jest.fn(() => +new Date('2017-01-01');
Mrzmyr
3
ODER:Date.now = jest.fn(() => Date.parse('2017-02-14))
Jeremy Eaton
92

jest.spyOn funktioniert für die Sperrzeit :

let dateNowSpy;

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

afterAll(() => {
    // Unlock Time
    dateNowSpy.mockRestore();
});
Tim Santeford
quelle
3
Tolle Lösung; Keine Abhängigkeiten und das Zurücksetzen macht es einfach, auf einen einzelnen Test anzuwenden.
Caleb Miller
14
Keine dateNowSpyVariable erforderlich , und die mockReset()ist laut jestjs.io/docs/en/mock-function-api.html#mockfnmockrestore redundant . In der afterAllkönnen Sie einfach tunDate.now.mockRestore()
Jimmy
Das ist großartig, dann brauchen Sie keine zusätzlichen Bibliotheken. Aber das wird nur wirklich funktionieren, wenn Sie statische
Datumsmethoden
1
@ Jimmy Date.now.mockRestore();gibt eine Eigenschaft 'mockRestore' existiert nicht auf Typ '() => Nummer' Fehler
Marco Lackovic
3
@Marco sollte es jest.spyOn sein (Datum, "jetzt"). MockRestore ();
Sab
6

jest-date-mock ist ein vollständiges Javascript-Modul, das von mir geschrieben wurde und zum Testen von Date on jest verwendet wird.

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

Verwenden Sie die einzigen 3 APIs für Testfälle.

  • AdvanceBy (ms): Zeitstempel für das Vorabdatum um ms.
  • advancedTo ([Zeitstempel]): Datum auf Zeitstempel zurücksetzen, Standardwert 0.
  • clear (): Schaltet das Mock-System herunter.
ein Werkzeug
quelle
Was ist dein Fall?
Atool
5

Für diejenigen, die Methoden für ein neues Date-Objekt verspotten möchten, können Sie Folgendes tun:

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

afterEach(() => {
    jest.restoreAll()
});
RobotEyes
quelle
Danke, das hat gerade das Problem behoben, das ich hatte.
Grayson Langford
2

Alle Antworten, die nur auf dem Mock of basieren, Date.now()funktionieren nicht überall, da einige Pakete (zum Beispiel moment.js) new Date()stattdessen verwenden.

In diesem Zusammenhang ist die Antwort, die darauf basiert, MockDatemeiner Meinung nach die einzig wirklich richtige. Wenn Sie kein externes Paket verwenden möchten, können Sie direkt in Folgendes schreiben 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;
ClementWalter
quelle
2

Ich möchte einige alternative Ansätze anbieten.

Wenn Sie stubben müssen format()(was vom Gebietsschema und der Zeitzone abhängig sein kann!)

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

Wenn Sie nur stub moment():

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

In Bezug auf den Test für die isDateTodayFunktion oben, glaube ich , die einfachste Art und Weise nicht zu spotten würde momentüberhaupt

David
quelle
2
Für das erste Beispiel bekomme ichTypeError: moment.mockReturnValue is not a function
mkelley33
2
Ist jest.mock("moment")auf dem gleichen Niveau wie Ihre Importanweisungen? Ansonsten können Sie es gerne in Aktion in diesem Projekt sehen
David
1

So habe ich mich über meine Date.now()Methode lustig gemacht, das Jahr für meinen Test auf 2010 zu setzen

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

Hier sind einige lesbare Möglichkeiten für verschiedene Anwendungsfälle. Ich bevorzuge die Verwendung von Spionen gegenüber dem Speichern von Verweisen auf die Originalobjekte, die versehentlich in einem anderen Code überschrieben werden können.

Einmalige Verspottung

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

Ein paar Tests

let dateSpy;

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

afterAll(() => {
  dateSpy.mockRestore();
});
Yangshun Tay
quelle
0

Ich möchte Manual Mocks verwenden, damit es in allen Tests verwendet werden kann.

// <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
codelegant
quelle
0

Ziel ist es, neues Datum () mit einem festen Datum zu verspotten, wo immer es während des Renderns der Komponente zu Testzwecken verwendet wird. Die Verwendung von Bibliotheken ist ein Aufwand, wenn Sie nur das neue Date () fn verspotten möchten.

Die Idee ist, das globale Datum in einer temporären Variablen zu speichern, das globale Datum zu verspotten und dann nach der Verwendung die Temperatur dem globalen Datum neu zuzuweisen.

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


Pranava S Balugari
quelle
Erklären Sie auch Ihre Antwort. Nur Code zu setzen ist nicht der gute Ansatz
Intsab Haider
1
Danke für den Vorschlag. Mit Kommentaren aktualisiert.
Pranava S Balugari
0

Ich wollte mich nur hier einschalten, da keine Antwort das Problem ansprach, wenn Sie sich über das lustig machen wollen Date Objekt nur in einer bestimmten Suite .

Sie können ihn verspotten für jede Suite der Setup - und Teardown - Methoden verwenden, Scherz 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;
});

Hoffe das hilft!

MoMo
quelle
0

Sie können Date-Faker verwenden . Hiermit können Sie das aktuelle Datum relativ ändern:

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