Why I Wanted to Use Jest#
While building my personal server this time, I decided to try test-driven development. Normally I'd just check if things work and keep pushing forward, but once I started creating .spec.ts files and trying things one by one, I ended up spending a lot of time figuring out the related configurations. I'm learning with the mindset that the time I invest now will help me develop faster later.
There are plenty of articles out there explaining the benefits of jest, but in my opinion, the biggest advantage is being able to create an environment where you can test really small units. When building features in a large-scale service, you wouldn't start with the big features right away — you'd create multiple functions and build up the feature step by step. Using jest to verify each implemented function one by one felt like truly ideal development. It's like developing a C or C++ program where you check results in the terminal — I really love how easy it is to see the output.
When developing with frameworks for both frontend and backend, I noticed I ended up verifying these small tests through the browser or tools like Postman. I wanted to break that habit. I kept putting it off with excuses like being too busy or finding setup too difficult, but right now I have plenty of time to devote to development, so I decided to give it a shot.
What I Tried Before Successfully Setting the Time#
I'm using jest version ^29.5.0. While using jest, I learned how to use env values in the test environment, how to use ConfigService in tests, how to use constants variables in tests, and how to test child services and use them in parent services. But this time, I struggled with time settings, so I decided to write about it — and that's what I'll focus on here.
First, let me share that I tried multiple approaches to apply the timezone in the test environment, but it always showed UTC time. So I also tested it in the development environment. In development, adding TZ=Asia/Seoul to .env and running yarn start correctly applied Korean time. But I wanted to fix it specifically for the test environment, so I kept looking.
Here are the methods I tried that didn't work. I'm not sure if I did something wrong or if they simply have no effect, but they weren't the solution.
Adding environment variables to scripts in package.json
I tried adding TZ=Asia/Seoul to the test scripts in package.json, but it had no effect.
{
// ...omitted
"test": "TZ=Asia/Seoul jest",
"test:watch": "TZ=Asia/Seoul jest --watch"
}Adding orta's jest extension settings in VS Code
I'm running jest through orta's jest extension in VS Code. So I tried adding the following to .vscode/settings.json, but it didn't apply either.
// Add this setting if you want to run only one test at a time.
"jest.runMode": {
"type": "on-demand",
},
"jest.nodeEnv": {
"TZ": "Asia/Seoul" // Attempt to set UTC+9
}Adding settings to jest in package.json
I asked Gemini about this problem, and it told me to add the code below to the jest section in package.json. But jest doesn't accept the timezone variable, so it shows a warning and doesn't apply.
"jest": {
"testEnvironment": "node", // This is default, so you can remove it.
"timezone": "Asia/Seoul"
}A workaround that works in tests but adds Korean time offset to the Date object
Lastly, this one just adds 9 to the generateRandomDate function being tested.
const generateRandomDate = (hour: number) => {
const today = new Date()
const minute = Math.floor(Math.random() * 59)
const second = Math.floor(Math.random() * 59)
today.setHours(9 + hour, minute, second, 0)
return today
}Using today.setHours(9 + desired hour) solved it, but this only applies to this function, and I'd have to add it again in similar cases. Also, in the development environment, the already-set timezone would result in Korean time + 9 hours, which was a problem, so I looked for another way.
How to Set the Time#
Here's how to set the time in jest. Since the functions I needed to test with time were in a specific service file, I added it to beforeEach. If you only need it for a single function, just add it before the function call.
beforeEach(async () => {
const offset = -(new Date().getTimezoneOffset() / 60)
// jest.useFakeTimers();
jest.setSystemTime(new Date().getTime() + offset * 60 * 60 * 1000)
})
afterEach(() => {
// jest.useRealTimers(); Add if needed!
})The offset value comes out as -9. This means the current local timezone is 9 hours ahead of UTC (though Korean time wasn't actually applied to new Date()). Since getTimezoneOffset() returns the value of UTC minus local time, it gives -9. For setSystemTime, I passed the calculated timestamp value (current time + hours * minutes * seconds * milliseconds).
To use the setSystemTime function, you need to do one of two things:
- Call the commented-out jest.useFakeTimers function first.
- Add the following code to the "jest" section in package.json.
"jest": {
// ...omitted
"fakeTimers": {
"enableGlobally": true
}
}Similar to manually changing the time, you can now change the time only in the test environment. The solution was simple, but it's a shame it took so long to find it.
JEST Extension Test Code Shortcut#
While using jest this time, I found it cumbersome that all tests ran every time. By changing jest.runMode to "type": "on-demand" in settings.json, you can run only the ones you want. Running them individually was great, but having to click with the mouse to run each one was still annoying.
The shortcut to run a single test in a test file is
command + ; + c.
There are conditions for using this shortcut:
- You need to have the jest extension installed in VS Code.
- You need to run
Jest: Start All Runnersviacommand + shift + p. - You need to change
jest.runModetoon-demand(the automatic mode runs all tests, making this shortcut unnecessary). The setup is described in the code above. - Your cursor needs to be on the test function you want to run in the test file.
Reviewing what you have learned and learning anew, you are fit to be a teacher.
— Confucius