jestとは
Node.jsをテストするためのライブラリ
<公式ドキュメント> https://jestjs.io/docs/en/getting-started
※Node.jsのテストアプリとしては、他にもmocha(https://mochajs.org/ )が有名。
テストコードが必要な理由
・開発の時間短縮。コードを変更した後でも、テストコマンドを打つだけで自動的にテストしてくれる
・より信頼性の高い(バグの少ない)コーディングができる
・リファクタリングや機能の追加・削除が簡単にできる
アプリをテストするための準備
●jestのインストール
npm i jest
※npmでのjestのページ ( https://www.npmjs.com/package/jest )
●テスト用の環境を設定する
※本番用の環境がすでに「dev.env」にまとめられている場合
・ファイル構成
└── App
├── config //環境を保存するフォルダ
| ├── dev.env //本番用環境のための環境変数を格納しているファイル
| └── test.env //テスト環境のための環境変数を格納しているファイル
・test.envの設定
PORT=3010
MONGODB_URL=mongodb://127.0.0.1:27017/task-manager-api-test
※テスト用のデータベースと接続させるようにMongoDBの設定を記述する
●package.jsonの設定
"scripts": {
"start": "node src/index.js",
"dev": "env-cmd -f ./config/dev.env nodemon src/index.js",
"test": "env-cmd -f ./config/test.env jest --watch" //test時に動かす環境を記述。
},
"jest": {
"testEnvironment": "node" //jestで nodeのコードをテストすると記述
},
※「scripts」に「test」を記述することで、ターミナルに「npm test」でテスト(ファイル名にtestと含まれているもの)を動かすようになる。
※「test」に「--watch」で、「npm test」後に、ターミナルでjestが開きっぱなしになる
expressをテストするためのnpmパッケージ
「super test」 https://www.npmjs.com/package/supertest
・テストコード
const app =require('./app') //portをlistenする以外のアプリの処理を読み込み
request(app)
.expect(...) //アプリが立ち上がっていなくてもexptectでテストできる
→super testパッケージを用いると、アプリが立ち上がっていなくてもテストを行うことができる
通常のテスト
・testのためのファイル構成
└── App
├── test //テストを保存するフォルダ
| └── fixture //テスト環境のための環境変数を格納しているファイル
| ├── db.js //テスト用データをデータベースに格納するためのファイル
| └── async.test.js //テストコードを記述するファイル
●通常のテスト
・first.test.js
test('テストケース名',() => { テストしたい関数 }) //正常なら処理完了するテスト
test('テストケース名', ()=> {
throw new Error ('エラー') //正常ならエラーを返したいテスト項目
})
jestでは通常、上記の書き方でテストコードを書く。
「npm test」でテスト開始。ファイル名に「test」が含まれているテストファイルが実行される。
非同期処理のテスト
・async.test.js
test('Async test demo', (done) => { //引数にdoneを設定
setTimeout(() => {
expect(1).toBe(2)
done() //非同期処理(ここではsetTimeout)が終わるタイミングでdoneを呼び出す
}, 2000)
})
↓
今回のテストでは、「setTimeout」をすると「1」が「2」になるかテストしている
↓
2秒後にエラーが返る。
※「toBe」について: jest公式ドキュメント
非同期処理のテストは、非同期処理が終わった時点で引数doneを呼び出すように設定しないと、ちゃんとテストされない(非同期処理を待たずにテストをパスしてしまう)
●Promiseを使うコードのテスト
const add = (a, b) => {
return new Promise ((resolve, reject) => {
setTimeout(() => {
if(a < 0 || b < 0){
return reject('Number must be non-negative')
}
resolve(a + b)
}, 2000)
})
}
上記のコードをテストする場合
・promise.test.js
test('addは機能しているか', (done)=> {
add(2, 3).then((sum) => { //thenを使ってチェインさせる
expect(sum).toBe(5)
done()
})
})
async/awaitを使ってテストする場合
test('addは機能しているか、async/awaitで', async() => {
const sum = await add(11, 22)
expect(sum).toBe(33)
})
テストの前に行う処理・テストの後に行う処理を記述する
「Setup and Teardown」を用いる。
beforeEach(() => {
initializeCityDatabase();
});afterEach(() => {
clearCityDatabase();
});
・beforeEach:テストの前に行う処理を記述する
・afterEach:テストの後に行う処理を記述する
例)テストの前に、テスト用データベース内の全ての項目を消す
・users.test.js
const User = require('../src/models/user')
const userOne = { //テストデータ用のデータベースの中身
neme:'aaa',
password: '12345'
}
beforeEach(async() => { //beforeEachは非同期処理
await User.deleteMany() //データベースの中身を削除
await new User(userOne).save() //空になったデータをデータベースに保存
})
ライブラリのmockを作成する
・jest公式ドキュメント
Mocking Node Modulesを参照
・mockライブラリを作成する場合のファイル構成
└── App
├── test //テストを保存するフォルダ
| └── fixture //テスト環境のための環境変数を格納しているファイル
| | ├── db.js //テスト用データをデータベースに格納するためのファイル
| | └── test.js//テストコードを記述するファイル
└── __mocks__ //モックファイルを記述
└── sendgrid //モックを作成するAPI名
└── test.js
・test.js
module.exports = {
send(){
}
}
↓
今回はsendgridという、メール送信のためのライブラリ(https://sendgrid.com/docs/api-reference/ )のモックを作成している。
コード中に「sgMail.send()」でメール送信をする箇所があった場合、テスト環境では作成したモックのsendgridが実行されるため、「send()」の処理は上記のファイルに書かれた通りのもの(今回の場合は何もしないこと)になる。