0

I have function called setMockDate that looks like:

/**
 * @param {Date} expected
 * @returns {Function} Call to remove Date mocking
 */
const setMockDate = (expected: Date): AnyObject => {
  const RealDate = Date
  function MockDate(mockOverride?: Date | number) {
    return new RealDate(mockOverride || expected)
  }

  MockDate.UTC = RealDate.UTC
  MockDate.parse = RealDate.parse
  MockDate.now = () => expected.getTime()
  MockDate.prototype = RealDate.prototype

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  global.Date = MockDate as any

  return () => {
    global.Date = RealDate
  }
}

export default setMockDate

and used like:

test('Should change date', () => {
  const mockDate = new Date('Feb 22, 2021 11:59:00')
  setMockDate(mockDate)
  expect(new Date()).toEqual(mockDate)
})

I wanted to change the MockDate function in setMockDate() to use an arrow function like:

const MockDate = (mockOverride?: Date | number) => {
  return new RealDate(mockOverride || expected)
}

However I get TypeError:

TypeError: Date is not a constructor

Why I am getting this error when changing to use an arrow function?

Jamie
  • 1,909
  • 3
  • 22
  • 47
  • Does this answer your question? [Javascript ES6, why I can not use \`new\` with arrow function?](https://stackoverflow.com/questions/37587834/javascript-es6-why-i-can-not-use-new-with-arrow-function) – jonrsharpe Mar 23 '21 at 15:45
  • This implementation seems complex and fragile - it's much easier to make time a dependency than try to patch all the possible ways people can interact with the global `Date` (see e.g. https://stackoverflow.com/a/65130857/3001761 for a concrete application). Also the `mockDate` in your test isn't actually a mock, it's a real `Date` (because you haven't yet `setMockDate`). – jonrsharpe Mar 23 '21 at 15:48

2 Answers2

1

MockDate is supposed to mimic new Date() which is a constructor and arrow function cannot be used as a constructor

More information here:

Jamie
  • 1,909
  • 3
  • 22
  • 47
0

I updated my helper function to:

import { AnyObject } from 'typings/custom'

/**
 * @param {Date} expected
 * @returns {Function} Call to remove Date mocking
 */
const setMockDate = (expected: Date): AnyObject => {
  const RealDate = Date

  function MockDate(mockOverride?: Date | number) {
    return new RealDate(mockOverride || expected)
  }

  MockDate.now = () => expected.getTime()
  MockDate.prototype = RealDate.prototype

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  global.Date = MockDate as any

  return () => {
    global.Date = RealDate
  }
}

export default setMockDate

Using this helper looks like:

/**
 * Testing a component that changes text based on time of day
 * ie: good morning, good afternoon & good evening
 */
describe('Greeting', () => {
  it('Should update message after time', () => {
    jest.useFakeTimers()
    setMockDate(new Date('Feb 22, 2021 11:59:00'))
    const wrapper = mount(<Greeting />)

    const greetingText = wrapper.text()
    setMockDate(new Date('Feb 22, 2021 12:00:00'))
    jest.advanceTimersByTime(60000)
    expect(wrapper.text()).not.toBe(greetingText)
  })

})
Jamie
  • 1,909
  • 3
  • 22
  • 47