Как высмеять конструктор, как новый Date()

У меня есть метод, который зависит от new Date, чтобы создать объект даты, а затем манипулировать им. Я тестирую, что манипуляции работают так, как ожидалось, поэтому мне нужно сравнить возвращаемую дату с ожидаемой датой. Для этого мне нужно убедиться, что new Date возвращает то же значение в тесте и в тестируемом методе. Как я могу это сделать?

Есть ли способ на самом деле высмеять возвращаемое значение функции-конструктора?

Я мог бы создать модуль, который может потребоваться с помощью функции, которая предоставляет объект даты и может быть издевательством. Но это похоже на ненужную абстракцию в моем коде.

пример функции, подлежащей тестированию...

module.exports = {
  sameTimeTomorrow: function(){
    var dt = new Date();
        dt.setDate(dt + 1);
    return dt;
  }
};

как мне высмеять возвращаемое значение new Date()?

Ответ 1

Вы можете использовать jasmine spyOn (шутка построена на жасмине), чтобы продумать прототип даты для getDate следующим образом:

spyOn(Date.prototype, 'setDate').and.returnValue(DATE_TO_TEST_WITH);

SpyOn также будет очищаться после него сам и будет продолжаться только в рамках теста.

Ответ 2

Вы можете переопределить конструктор Date с помощью фиктивной функции, которая возвращает созданный вами объект Date с указанным вами значением даты:

var yourModule = require('./yourModule')

test('Mock Date', () => {
  const mockedDate = new Date(2017, 11, 10)
  const originalDate = Date

  global.Date = jest.fn(() => mockedDate)
  global.Date.setDate = originalDate.setDate

  expect(yourModule.sameTimeTomorrow().getDate()).toEqual(11)
})

Вы можете проверить пример здесь: https://repl.it/@miluoshi5/jest-mock-date

Ответ 3

Вы можете заменить конструктор Date тем, что всегда возвращает жестко запрограммированную дату, и после этого верните его в нормальное состояние.

var _Date = null;

function replaceDate() {
  if (_Date) {
    return
  };

  _Date = Date;

  Object.getOwnPropertyNames(Date).forEach(function(name) { 
    _Date[name] = Date[name] 
  });

  // set Date ctor to always return same date
  Date = function() { return new _Date('2000-01-01T00:00:00.000Z') }

  Object.getOwnPropertyNames(_Date).forEach(function(name) { 
    Date[name] = _Date[name] 
  });  
}

function repairDate() {
  if (_Date === null) {
    return;
  }

  Date = _Date;
  Object.getOwnPropertyNames(_Date).forEach(function(name) { 
    Date[name] = _Date[name] 
  });  

  _Date = null;
}

// test that two dates created at different times return the same timestamp
var t0 = new Date();

// create another one 100ms later
setTimeout(function() {
  var t1 = new Date();

  console.log(t0.getTime(), t1.getTime(), t0.getTime() === t1.getTime());

  // put things back to normal when done
  repairDate();
}, 100);

Ответ 4

Я только что написал тест на шутку и смог заглушить new Date() с global.Date = () => now

Ответ 5

Вы можете смоделировать конструктор, например, new Date(), используя jest.spyOn, как показано ниже:

test('mocks a constructor like new Date()', () => {
  console.log('Normal:   ', new Date().getTime())

  const mockDate = new Date(1466424490000)
  const spy = jest
    .spyOn(global, 'Date')
    .mockImplementation(() => mockDate)

  console.log('Mocked:   ', new Date().getTime())
  spy.mockRestore()

  console.log('Restored: ', new Date().getTime())
})

И результат выглядит так:

Normal:    1566424897579
Mocked:    1466424490000
Restored:  1566424897608

Ответ 6

Вот что я делаю сейчас, и это работает и не загромождает мою подпись метода.

newDate.js

module.exports = function(){
  return new Date();
};

someModule.js

var newDate = require('newDate.js');
module.exports = {
  sameTimeTomorrow: function(){
    var dt = newDate();
        dt.setDate(dt.getDate() + 1);
    return dt;
  }
};

someModule-test.js

jest.dontMock('someModule.js');

describe('someModule', function(){

  it('sameTimeTomorrow', function(){
   var newDate = require('../../_app/util/newDate.js');
       newDate.mockReturnValue(new Date(2015, 02, 13, 09, 15, 40, 123));

   var someModule = require('someModule.js');

   expect(someModule.sameTimeTomorrow().toString()).toBe(new Date(2015, 02, 14, 09, 15, 40, 123).toString());
  });

});