なぜjestを使おうとしたのか?#
今回個人サーバーを構築するにあたり、テスト駆動開発に取り組むことにしました。
本来なら動作確認してどんどん開発を進めればよかったのですが、.spec.tsファイルを作成して一つずつ試していると、関連設定を整えるのに調べる時間がかなり長くなりました。今費やしている時間が後でより速く開発するための方法だと思いながら学んでいます。
jestの利点は他の記事でもたくさん説明されていますが、個人的に思うのは、本当に小さな単位でテストできる環境を作れることが利点だと思います。大規模なサービスで機能を作るには大きな機能から作るのではなく、複数の関数を作って機能を組み立てていくわけですが、その時にjestを使って実装した関数を一つずつ確認するのが本当に理想的な開発だと感じました。ターミナルで確認するCやC++プログラムを作る時と同じ感覚で開発でき、結果の確認が簡単なのが本当に良いと思います。
フロントエンドもバックエンドもフレームワークを使って開発していると、こうした小さなテストをブラウザやPostmanのようなプログラムで確認するようになりがちです。
この開発の慣性を脱しようとしました。忙しいという言い訳で、設定が難しいという理由で先延ばしにしてきましたが、今は開発に専念できる時間があるので挑戦することにしました。
時間設定に成功する前に試した方法#
jestのバージョンは^29.5.0を使用しています。
env値をテスト環境で使う方法、ConfigServiceをテスト環境で使う方法、constants変数をテスト環境で使用する方法、下位serviceをテストする方法と上位サービスで使用する方法など、jestを使う中で学びましたが、今回は時間設定に苦戦したのでこれを記事にしようと思い、この部分だけ説明します。
まずお伝えしたいのは、テスト環境で時間を適用するさまざまな方法を試してもUTC時間でしか表示されなかったため、開発環境でも試してみました。開発環境では.envにTZ=Asia/Seoulを追加してyarn startで実行したところ、韓国時間が適用されて正常に表示されました。しかしテスト環境でだけうまくいかないのを修正したくて調べました。
試したけれどうまくいかなかった方法をお伝えします。やり方が間違っていたのか影響がなかったのか分かりませんが、解決策ではありませんでした。
package.jsonのscriptsに環境変数を追加
package.jsonのtestスクリプトにTZ=Asia/Seoulを追加してみましたが、効果はありませんでした。
{
// ...省略
"test": "TZ=Asia/Seoul jest",
"test:watch": "TZ=Asia/Seoul jest --watch"
}VS Code Extension ortaのjest設定を追加
VS Code Extensionのortaのjestでjestを実行しています。そこで.vscode/settings.jsonに以下のように追加してみましたが、適用されませんでした。
// テストを一つだけ実行したい場合はこの設定を追加してください。
"jest.runMode": {
"type": "on-demand",
},
"jest.nodeEnv": {
"TZ": "Asia/Seoul" // UTC+9に設定するための試み
}package.jsonのjestに設定を追加
この問題をGeminiにも聞いてみたところ、package.jsonのjest項目に以下のコードを追加するよう教えてくれました。しかしこのコードはtimezoneという変数をjestが受け付けないため、警告が出て適用されません。
"jest": {
"testEnvironment": "node", // これはdefaultなので削除しても大丈夫です。
"timezone": "Asia/Seoul"
}テストでは一応解決するがDateオブジェクトに韓国時間を追加
最後に、これはテスト関数のgenerateRandomDateに9を加えるだけの方法でした。
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
}today.setHours(9 + 指定する時間)で解決しましたが、これはこの関数にだけ適用したもので、似たようなケースでもまた追加する必要がありそうでした。そして開発環境ではすでに設定したタイムゾーンのため韓国時間 + 9時間になるのが問題で、別の方法を探しました。
時間を設定する方法#
jestで時間を設定する方法は以下の通りです。テストで時間を確認する必要がある関数が特定のサービスファイルにあったのでbeforeEachに追加しました。関数一つだけで使用する場合は、関数の実行前にこれを追加すれば大丈夫です。
beforeEach(async () => {
const offset = -(new Date().getTimezoneOffset() / 60)
// jest.useFakeTimers();
jest.setSystemTime(new Date().getTime() + offset * 60 * 60 * 1000)
})
afterEach(() => {
// jest.useRealTimers(); 必要な場合は追加してください!
})offsetの値は-9になります。これは現在のローカルタイムゾーンがUTCより9時間先であることを意味します(ただし韓国時間が適用されてnew Date()が返されるわけではありませんでした)。
getTimezoneOffset()の戻り値がUTC時間からローカル時間を引いた値であるため-9になります。setSystemTimeには計算した(現在時刻 + 時間 * 分 * 秒 * ミリ秒)のtimestamp値を入れました。
setSystemTime関数を使用するには、以下の2つの方法のどちらかを行う必要があります。
- コメントアウトしたjest.useFakeTimers関数を先に呼び出す必要があります。
- package.jsonの"jest"に以下のコードを追加する必要があります。
"jest": {
// ...省略
"fakeTimers": {
"enableGlobally": true
}
}時間を直接変える方法と同様に、テスト環境でのみ時間を変更できるようになりました。
見つけた結果はシンプルですが、この解決策を見つけるまでに時間がかかったのが残念です。
JEST extension テストコード実行ショートカットキー#
今回jestを使いながら、毎回すべてのテストが実行されるのが面倒に感じました。そこで一つだけ実行できるようにsettings.jsonのjest.runModeを"type": "on-demand"に変更すれば、好きなものだけ個別に実行できます。
個別実行できるのは良かったのですが、マウスでクリックして一つずつ実行しなければならないのが面倒でした。
テストファイルでテスト一つだけ実行するショートカットキーは
command + ; + cです。
このショートカットキーを使う時の条件があります。
- VS Codeでjest extensionを使用していること。
command + shift + pでJest: Start All Runnersを実行すること。jest.runModeをon-demandに変更すること(通常の自動はすべてのテストを実行するのでこのショートカットキーは不要です)。設定方法は上記のコードに記載しています。- テストファイルで実行したいテスト関数にカーソルがあること。
Reviewing what you have learned and learning anew, you are fit to be a teacher.
— Confucius