I have a greeting component that updates the time of day. Depending on the time of day the greeting will print
I have function called getTimeOfDay
and test that this function is working. If you're interested view my tests here.
In my component I have a timer that checks every minute to see if the time of day message should update.
const [date, setDate] = useState(new Date())
const timeOfDay = getTimeOfDay(date)
useEffect(() => {
const timer = setInterval(() => {
setDate(new Date())
}, 60000)
return () => {
clearInterval(timer)
}
}, [])
I have been going back and forth on whether to test that this message changes correctly as the time passes. I know that testing implementation details is bad practice and was not sure if this was an implementation detail or a feature that should be tested.
If this is something I should test, I can't seem to easily implement a timer in jest that checks the message and then speeds up the timer 8 hours. What are your thoughts?
My final tests are as followed:
describe('Greeting', () => {
const name = 'Jack'
it('renders component as expected', () => {
const wrapper = mount(<Greeting name={name} /> )
expect(wrapper.text().includes(name)).toBe(true)
})
it('Should update message after time', () => {
jest.useFakeTimers()
setMockDate(new Date('Feb 22, 2021 11:59:00'))
const wrapper = mount(<Greeting name={name} />)
const greetingText = wrapper.text()
setMockDate(new Date('Feb 22, 2021 12:00:00'))
jest.advanceTimersByTime(60000)
expect(wrapper.text()).not.toBe(greetingText)
})
it('Should clear interval on unmount', () => {
const spyOnSetInterval = jest.spyOn(window, 'setInterval')
const spyOnClearInterval = jest.spyOn(window, 'clearInterval')
spyOnSetInterval.mockReturnValueOnce((33 as unknown) as NodeJS.Timeout)
const wrapper = mount(<Greeting name={name} />)
wrapper.unmount()
expect(spyOnClearInterval).toHaveBeenCalledWith(33)
})
})
I created a helper function to mock the date. The final version is:
/**
* @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