-
Notifications
You must be signed in to change notification settings - Fork 0
Node.js テストの書き方
Komine Shunta edited this page Sep 24, 2020
·
2 revisions
- 必要なものをインストールしておく
yarn add supertest chai jsdom snap-shot-it @types/supertest @types/chai @types/jsdom @types/mocha ts-mocha mocha typescript
-
app
をエクスポートする
export default app
# または
module.exports = app;
-
package.json
のscripts
以下にテストスクリプトを追加
"test": "NODE_ENV=test ts-mocha test.ts",
"update": "SNAPSHOT_UPDATE=1 yarn test"
import { testOverwrites } from './testOverwrites';
import request from 'supertest';
import { assert } from 'chai';
import { JSDOM } from 'jsdom';
import snapshot from 'snap-shot-it';
import app from './app';
const agent = request.agent(app);
describe('/test', () => {
testOverwrites(); // 外部へのリクエストや、SQLなどを監視したいときに使う
it('should response something', async () => {
const response = await agent
.get('/test')
.query({})
.send({});
snapshot(JSON.parse(response.text));
});
});
-
overwrites.ts
をコピペ
type FuncType = (...args: any) => any;
export function overwriteCallback<T, K extends keyof T>(
self: T,
fnName: K,
hook: (...args: any[]) => any[]
) {
const original = self[fnName];
if (typeof original !== 'function') {
throw new Error(`overwrite error: ${fnName} is not a function`);
}
self[fnName] = ((...args: any[]) => {
const newArgs = args.slice(0, args.length - 1);
const lastArg = args[args.length - 1];
if (typeof lastArg !== 'function') {
throw new Error(
`overwrite error: last arg of ${fnName} is not a function`
);
}
newArgs.push((...callbackArgs: any[]) => {
const newCallbackArgs = hook(...callbackArgs);
lastArg(...newCallbackArgs);
});
const result = original.apply(self, newArgs);
return result;
}) as any;
return () => {
self[fnName] = original;
};
}
export function overwrite<T, K extends keyof T, Fn extends T[K]>(
self: T,
fnName: K,
hook: Fn extends FuncType
? (result: ReturnType<Fn>, ...args: Parameters<Fn>) => void
: never
) {
if (!self) return;
const original = self[fnName];
if (typeof original !== 'function') return;
self[fnName] = ((...args: any[]) => {
const result = original.apply(self, args);
hook(result, ...args);
return result;
}) as any;
return () => {
self[fnName] = original;
};
}
-
testOverwrites.ts
を作る
import snapshot from 'snap-shot-it';
import { overwrite, overwriteCallback } from './overwrite';
let timeline = [];
export function testOverwrites() {
beforeEach(() => {
timeline = [];
});
afterEach(() => {
snapshot(timeline);
});
}
-
fetch.ts
を作り、アプリケーション内でのnode-fetch
のimport
をすべて置き換える
import fetch from 'node-fetch';
export default { fetch };
-
textOverwrites.ts
に以下を追記
import nodeFetch from './fetch';
overwrite(nodeFetch, 'fetch', (result, url) => {
timeline.push(['fetch', url]);
});
-
textOverwrites.ts
に以下を追記
import Axios, { AxiosResponse } from 'axios';
for (const method of ['get', 'post', 'delete', 'put']) {
overwrite(
Axios,
method as any,
(async (result: Promise<AxiosResponse<any>>, url: string) => {
timeline.push(['axios', url, (await result).data]);
}) as any
);
}
-
textOverwrites.ts
に以下を追記
// TODO: axiosみたいにする
import agent from 'superagent';
for (const method of ['get', 'post'] as ('get' | 'post')[]) {
overwrite(agent, method, (_, url) => {
timeline.push(['agent', method, url]);
});
}
-
textOverwrites.ts
に以下を追記
// TODO: axiosみたいにする
import request from 'request';
for (const method of ['get', 'post'] as ('get' | 'post')[]) {
overwrite(request, method, (_, url) => {
timeline.push(['request', method, url]);
});
}
-
textOverwrites.ts
に以下を追記
import mysql from 'mysql';
overwrite(mysql, 'createPool', (pool) => {
overwriteCallback(pool, 'getConnection', (err, connection) => {
overwrite(connection, 'query', (_, query) => {
timeline.push('query', query);
});
return [err, connection];
});
});
-
textOverwrites.ts
に以下を追記
import mysql2 from 'mysql2/promise';
overwrite(mysql2, 'createPool', (pool) => {
overwrite(pool, 'getConnection', async (connection) => {
overwrite(await connection, 'beginTransaction', async () => {
timeline.push('beginTransaction');
});
overwrite(await connection, 'commit', async () => {
timeline.push('commit');
});
overwrite(await connection, 'query', async (result, query) => {
timeline.push('query', query);
});
});
});
上を見ろ
左を見ろ