Recipes for unit tests when working with date and time in React/JavaScript
In one of our last major projects at my company, we had to deal with real-time flight data. This concluded into processing and calculating with a lot of date and time values. Tests helped, as always, to run everything smoothly and precisely. Now in the aftermath, I want to show you what helped us to write meaningful unit tests.
Tools
- jest is a beautiful test framework for JavaScript
- testing-library/react and testing-library/react-hooks for all the react things
- mockdate to, yes, mock the date
These tools help you, but it also important to bring in a proper mindset when testing date and time.
Mindset
- handle only one format in your application. It doesn't really matter if you decide to work with JavaScript Date Objects (the value of
new Date()
) or its static methodDate.now()
which returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC. Just stick to one and re-format when needed (with date-fns since moment stopped service) - always stub or mock real date and time functions. Never rely on any system time, this will get you into trouble, I promise.
Examples
Mock the date
No matter what you want to test, mock before you start. With jest, it often makes sense to use the beforeEach/All
and afterEach/All
hooks.
import mockdate from "mockdate"const mockdateInit = 1580782960 // 2020-02-04T02:22:40describe("yourTest", () => {
beforeEach(() => {
mockdate.set(mockdateInit)
}) afterAll(() => {
mockdate.reset()
})
})
As said before, we use mockdate
here, which will for now always return mockdateInit
for a new Date()
call.
Sorting
If you now want to sort by date or time, let's say make groups of future or past dates, this is fairly easy. Just remember you are now in the time of mockdateInit
. You can enhance the previous setup like this:
...
const anyOtherDate = 1580782970
......
it("should be in the future", () => {
const result = anyOtherDate > mockdateInit expect(result).toBeTruthy()
})
...
Of course, any other calculation based tasks can be tested the same way.
Timelapse
Sometimes you want to test a timelapse. Let's say, you want to check a state now and any changes in the next 5 minutes. Like the flight data we processed. For that, since you don't really want to wait 5 minutes while your tests are running (and sincerely your co-workers or CI neither), Jests' timer mocks with their advanceTimersByTime
method is here to help. Here is a unit test that utilizes the @testing-library/react-hooks
and updateEveryMinute hook, which internally updates every full minute.
...
const mockDateInit = 1580782960 // 2020-02-04T02:22:40
const mockDateNextFullMinute = 1580782980 // 2020-02-04T02:23:00
const mockDateMinuteAfterNext = 1580783040 // 2020-02-04T02:24:00
const mockDateAfterAfter3Minutes = 1580783100 // 2020-02-04T02:25:00
......
jest.useFakeTimers() // make sure Jest uses them
...it("should update every full minute after first update", () => {
const { result } = renderHook(() => useCurrentTime()) Mockdate.set(mockdateMinuteAfterNext)
act(() => jest.advanceTimersByTime(100000)) expect(result.current).toEqual(Math.floor(mockdateMinuteAfterNext / 1000)) Mockdate.set(mockdateAfterAfter3Minutes)
act(() => jest.advanceTimersByTime(60000))expect(result.current).toEqual(Math.floor(mockdateAfterAfter3Minutes / 1000))
})
As you can see: We test and then update mockdate
so it returns a new date mock it the next iteration.
Wrap it up
So I hope, that gave you some ideas, when you write unit tests for a JavaScript app that uses a date and time-relevant data.
If you have a relevant use case and don't know how to test it – leave a comment!