Jest'te nasıl sahte bir tarih belirleyebilirim?


112

React bileşenlerim için bir yardımcı dosyada tarih mantığımın çoğunu yapmak için moment.js kullanıyorum, ancak Jest a la'da bir tarihle nasıl dalga geçeceğimi bulamadım sinon.useFakeTimers().

Jest dokümanlar yalnızca gibi zamanlayıcı işlevleri hakkında konuşmak setTimeout,setInterval vb. ancak bir tarih belirlemeye ve ardından tarih işlevlerimin yapılması gereken şeyi yaptığını kontrol etmeye yardımcı olmaz.

JS dosyamdan bazıları:

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;

ve işte Jest kullanarak kurduğum şey:

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

  });

});

Şimdi bu testler geçiyor çünkü anı kullanıyorum ve fonksiyonlarım an kullanıyor ama biraz dengesiz görünüyor ve testler için tarihi sabit bir saate ayarlamak istiyorum.

Bunun nasıl başarılabileceğine dair bir fikriniz var mı?

Yanıtlar:


70

MockDate , neyin new Date()döndürüldüğünü değiştirmek için jest testlerinde kullanılabilir :

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

Ben diğer işlevlerini kullanırken çünkü çok çalıştı Dategibi valueOf().
Robin Zimmermann

146

Momentjs Datedahili olarak kullandığından , Date.nowher zaman aynı anı döndürmek için işlevin üzerine yazabilirsiniz .

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

veya

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

34
İşte iade edilecektir gerçek tarihini belirleyecek biraz daha güzel bir yöntem vardır:Date.now = jest.fn(() => new Date(Date.UTC(2017, 0, 1)).valueOf());
developering

5
Ya da biraz daha güzel:Date.now = jest.fn(() => +new Date('2017-01-01');
mrzmyr

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

93

jest.spyOn , kilitleme süresi için çalışır:

let dateNowSpy;

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

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

3
Harika çözüm; hiçbir bağımlılık yoktur ve sıfırlanabilir tutmak, tek bir teste başvurmayı kolaylaştırır.
Caleb Miller

14
dateNowSpyDeğişkene gerek yoktur ve jestjs.io/docs/en/mock-function-api.html#mockfnmockrestore'amockReset() göre gereksizdir . İçinde, basitçe yapabilirsinizafterAllDate.now.mockRestore()
Jimmy

bu harika, bu yüzden herhangi bir ek kitaplığa ihtiyacınız olmaz. Ancak bu, yalnızca statik Tarih yöntemlerini kullanıyorsanız gerçekten işe yarayacaktır (çok fazla değil)
hellatan

1
@Jimmy Date.now.mockRestore();bir Özellik verir 'mockRestore' türünde yok '() => sayı' hatası
Marco Lackovic

3
@Marco jest.spyOn (Tarih, "şimdi") olmalıdır. MockRestore ();
sab

6

jest-date-mock , benim yazdığım eksiksiz bir javascript modülüdür ve jest üzerinde Date'i test etmek için kullanılır.

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

Test senaryoları için yalnızca 3 API'yi kullanın.

  • advanceBy (ms): ms cinsinden ilerleme tarihi zaman damgası.
  • advanceTo ([timestamp]): tarihi zaman damgası olarak sıfırlar, varsayılan olarak 0.
  • clear (): sahte sistemi kapatır.

senin davan nedir
atool

5

Yeni bir Date nesnesinde yöntemlerle alay etmek isteyenler için şunları yapabilirsiniz:

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

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

Teşekkürler, bu az önce yaşadığım sorunu çözdü.
Grayson Langford

2

Date.now()Bazı paketler (örneğin moment.js) kullandığından , yalnızca taklidini temel alan tüm yanıtlar her yerde çalışmayacaktır.new Date() onun yerine .

Bu bağlamda temel alınan cevap MockDatebence tek gerçek doğru. Harici bir paket kullanmak istemiyorsanız, doğrudan şuraya yazabilirsiniz 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

Bazı alternatif yaklaşımlar sunmak istiyorum.

Saplama yapmanız gerekiyorsa format()(yerel ayara ve saat dilimine bağlı olabilir!)

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

Yalnızca saplama yapmanız gerekiyorsa moment():

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

İçin testi ile ilgili olarak isDateTodayyukarıdaki fonksiyonu, ben basit yolu taklidinin değil olacağına inanıyorum momenthiç


2
İlk örnek içinTypeError: moment.mockReturnValue is not a function
mkelley33

2
jest.mock("moment")ithalat ifadeleri aynı düzeyde? Aksi takdirde, bunu bu projede
David

1

Testim Date.now()için yılı 2010 olarak belirleme yöntemimle böyle dalga geçtim

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

1

İşte farklı kullanım durumları için birkaç okunabilir yol. Orijinal nesnelere referansları kaydetmek yerine casus kullanmayı tercih ederim, bu da yanlışlıkla başka bir kodda üzerine yazılabilir.

Tek seferlik alay

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

Birkaç test

let dateSpy;

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

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


0

Tüm testlerde kullanılabilmesi için Manuel Mocks kullanmak istiyorum.

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

Hedef, test amacıyla bileşen oluşturma sırasında kullanıldığı her yerde yeni Date () ile sabit bir tarih taklit etmektir. İstediğiniz tek şey new Date () fn ile dalga geçmekse, kitaplıkları kullanmak ek yük olacaktır.

Fikir, global tarihi geçici bir değişkene depolamak, global dae ile alay etmek ve kullanımdan sonra temp'yi global tarihe yeniden atamaktır.

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



Cevabınızı da açıklayın. sadece kod koymak iyi bir yaklaşım değil
Intsab Haider

1
Önerin için teşekkürler. Yorumlarla güncellendi.
Pranava S Balugari

0

DateNesneyi yalnızca belirli bir süitte alay etmek istiyorsanız, hiçbir yanıt sorunu ele almadığından, burada zil çalmak istedim .

Her paket için kurulum ve sökme yöntemlerini kullanarak alay edebilirsiniz, jest dokümanları

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

Bu yardımcı olur umarım!


0

Date-faker'ı kullanabilirsiniz . Mevcut tarihi nispeten değiştirmenize izin verir:

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();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.