これは、なにをしたくて書いたもの?
先日、ECMAScript Modulesを使ったテストを書く時に、VitestをJestの代わりに使ってみるというエントリーを書きました。
VitestでTypeScript × Node.js(ECMAScript Modules)のテストを書く - CLOVER🍀
今回はもう少しNode.jsに踏み込んだ内容として、Expressを使った簡単なアプリケーションを書いて、テストも書いてみましょう。
ちょうど似たようなエントリーを前に書いていたので、こちらをVitestを使うように書き直してみます。
Expressを使ったアプリケーションのテストを書く?
前に書いたエントリーは3年前のものなので、今の事情はどうなっているんだろうと調べてみましたが、あまり変わっていなさそうです。
Jestのページでは、やはりSuperTestを勧めています。もっとも、リンク先のページはすでに見れなくなっているようなのでメンテナンスされて
いないとも言えますが…。
Testing Web Frameworks / Express.js
Vitestのドキュメントには、バックエンドフレームワークに関する記述はなさそうです。
Getting Started | Guide | Vitest
設定ファイルでのテスト対象環境がnode
であるかどうか(デフォルトはnode
)を気にするくらいでしょうか。
Configuring Vitest / environment
SuperTestはこちら。
内部的にはsuperagentを利用しています。
3年前に自分が書いた時にはExpressは4.xのままでしたが、SuperTestは4.1.x → 7.0.x、supertestは2.0.x → 10.0.xと大幅にバージョンが
進んでいます…。
ひとまず、進めていきましょう。
環境
今回の環境はこちら。
$ node --version v20.16.0 $ npm --version 10.8.1
Node.jsプロジェクトを作成する
まずはNode.jsプロジェクトを作成して、必要な依存関係や設定を作成します。
$ npm init -y $ npm i -D typescript $ npm i -D @types/node@v20 $ npm i -D prettier $ npm i -D vitest $ mkdir src test
ECMAScript Modulesとして、scripts
や依存関係は現時点ではこのように。
"type": "module", ... "scripts": { "build": "tsc --project .", "build:watch": "tsc --project . --watch", "typecheck": "tsc --project ./tsconfig.typecheck.json", "typecheck:watch": "tsc --project ./tsconfig.typecheck.json --watch", "test": "vitest run", "test:watch": "vitest watch", "format": "prettier --write src test" }, ... "devDependencies": { "@types/node": "^20.14.8", "prettier": "^3.3.3", "typescript": "^5.5.4", "vitest": "^2.0.5" }
各種設定ファイル。
tsconfig.json
{ "compilerOptions": { "target": "esnext", "module": "nodenext", "moduleResolution": "nodenext", "lib": ["esnext"], "baseUrl": "./src", "outDir": "dist", "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "skipLibCheck": true, "esModuleInterop": true }, "include": [ "src/**/*" ] }
tsconfig.typecheck.json
{ "extends": "./tsconfig", "compilerOptions": { "baseUrl": "./", "noEmit": true }, "include": [ "src/**/*", "test/**/*" ] }
.prettierrc.json
{ "singleQuote": true, "printWidth": 120 }
vite.config.ts
/// <reference types="vitest" /> import { defineConfig } from 'vite'; export default defineConfig({ test: { environment: 'node', }, });
簡単なExpressアプリケーションを書く
まずは簡単なExpressアプリケーションを書いてみます。
Expressをインストール。
$ npm i express $ npm i -D @types/express
依存関係。
"devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20.14.8", "prettier": "^3.3.3", "typescript": "^5.5.4", "vitest": "^2.0.5" }, "dependencies": { "express": "^4.19.2" }
簡単なGET、POSTメソッドに対するエンドポイントを作成。いわゆるecho的なものです。
src/app.ts
import express from 'express'; import bodyParser from 'body-parser'; export const app = express(); app.use(bodyParser.json()); app.get('/echo', (req, res) => { const message = req.query['message']; res.send(`Hello ${message}!!`); }); app.post('/echo', (req, res) => { const message = req.body['message']; res.send({ message: `Hello ${message}!!` }); });
サーバーの起動は別ファイルにしておきます。これはテストでの構成を意識しています。
src/server.ts
import { app } from './app.js'; const port = 3000; app.listen(port, () => { console.log(`[${new Date().toISOString()}] server[${port}] startup.`); });
動作確認します。ビルド。
$ npm run build
起動。
$ node dist/server.js [2024-08-18T08:24:25.524Z] server[3000] startup.
確認。
## GET $ curl localhost:3000/echo?message=Express Hello Express!! ## POST $ curl -XPOST -H 'Content-Type: application/json' localhost:3000/echo -d '{"message": "Express"}' {"message":"Hello Express!!"}
OKですね。
テストを書く
次はテストを書きます。
SuperTestをインストール。
$ npm i -D supertest @types/supertest
依存関係はこうなりました。
"devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20.14.8", "@types/supertest": "^6.0.2", "prettier": "^3.3.3", "supertest": "^7.0.0", "typescript": "^5.5.4", "vitest": "^2.0.5" }, "dependencies": { "express": "^4.19.2" }
では、テストを書いてみます。
test/app.test.ts
import { expect, test } from 'vitest'; import request from 'supertest'; import { app } from '../src/app.js'; test('get /echo', async () => { const response = await request(app).get('/echo?message=Express'); expect(response.status).toStrictEqual(200); expect(response.text).toStrictEqual('Hello Express!!'); }); test('post /echo', async () => { const response = await request(app) .post('/echo') .set('Content-Type', 'application/json') .send({ message: 'Express' }); expect(response.status).toStrictEqual(200); expect(response.text).toStrictEqual(JSON.stringify({ message: 'Hello Express!!' })); });
確認。
$ npm test > express-vitest-getting-started@1.0.0 test > vitest run RUN v2.0.5 /path/to ✓ test/app.test.ts (2) ✓ get /echo ✓ post /echo Test Files 1 passed (1) Tests 2 passed (2) Start at 17:45:09 Duration 462ms (transform 40ms, setup 0ms, collect 134ms, tests 34ms, environment 0ms, prepare 79ms)
OKですね。
というわけで、特に問題なくさらっと動かせました。
おわりに
Vitestを使って、ECMAScript ModulesとTypeScriptとExpress、SuperTestを使ったアプリケーションのテストを書いてみました。
たぶんそれほど困らないだろうと思っていたのですが、あっさりと動いたのでやっぱりそうかなと。
どちらかというと、このエントリーを書くにあたってNode.jsのテスト関係のライブラリーを調べ直したりするところが復習になりました。
それほど変わってはいませんでしたけどね。
あと、Expressって4系が長いんですね…。