Expressでテストを実行させる方法
	今回はNode.jsのフレームワーク「Express」を使って単体テストを実行していく過程をお見せします。
テストフレームワークは「Jest」を使っていきます。最終的にはgithub Actionsを使ってテストを実行できるようにします。
Expressとは
ExpressはWebアプリケーション開発する上で効率化するためのフレームワークです。
Expressで使った具体例としてAPI開発があります。リクエストに対して処理をしてレスポンスとして返すようにするWebアプリケーションを作ることができます。
Jestとは
JestはJavaScriptのテストフレームワークであり、Jestは使いやすく高速なテストが実行できます。モックやスパイの作成が簡単にできテストを効率化をすることができます。
Expressでテストを実行させる方法
- 必要なライブラリをインストールする
 - package.jsonを設定する
 - テストコードを作成する
 - ロジックコードを作成する
 - テストが通ることを確認する
 - github Actionsで実行させる
 
1.必要なライブラリをインストールする
npm install jest supertest axios dotenv express log4js2.package.jsonを設定する
{
・・・
    "main": "server.js",
    "scripts": {
        "start": "node server.js",
        "test": "jest"
    },
・・・
}3.テストコードを作成する
下記のテストコードは「api/root」でリクエストした際のAPIのレスポンスのテストです。
const mockFn = jest.fn();
const request = require('supertest');
const app = require('../app.js');
const address = require('../src/common/address.js');
const spyAddressSearch = jest.spyOn(address, 'search');
const path = '/api/root';
jest.setTimeout(10000);
describe('正常', () => {
    test('レスポンス確認', async () => {
        spyAddressSearch.mockImplementationOnce(() => {
            return {
                results: [
                    {
                        address1: '東京都',
                        address2: '千代田区',
                        address3: '飯田橋',
                        kana1: 'トウキョウト',
                        kana2: 'チヨダク',
                        kana3: 'イイダバシ',
                        prefcode: '13',
                        zipcode: '1020072',
                    },
                ],
                status: 200,
            };
        });
        const result = await request(app)
            .get(path)
            .query({ id: 'ezaki', postCode: '1020072' });
        expect(result.statusCode).toBe(200);
        expect(result.body).toStrictEqual({
            id: 'ezaki',
            address: {
                results: [
                    {
                        address1: '東京都',
                        address2: '千代田区',
                        address3: '飯田橋',
                        kana1: 'トウキョウト',
                        kana2: 'チヨダク',
                        kana3: 'イイダバシ',
                        prefcode: '13',
                        zipcode: '1020072',
                    },
                ],
                status: 200,
            },
        });
    });
});下記のテストコードは「address.js」で郵便番号検索する際のテストコードです。
const mockFn = jest.fn();
jest.mock('axios', () => {
    return {
        create: jest.fn(() => ({
            get: mockFn,
        })),
    };
});
const address = require('../../src/common/address');
jest.setTimeout(10000);
describe('正常', () => {
    test('レスポンス確認 ハイフンなし', async () => {
        mockFn.mockResolvedValue({
            status: 200,
            data: {
                results: [
                    {
                        address1: '東京都',
                        address2: '千代田区',
                        address3: '飯田橋',
                        kana1: 'トウキョウト',
                        kana2: 'チヨダク',
                        kana3: 'イイダバシ',
                        prefcode: '13',
                        zipcode: '1020072',
                    },
                ],
                status: 200,
            },
        });
        const postCode = '1020072';
        const result = await address.search(postCode);
        expect(result.status).toBe(200);
        expect(result.results).toStrictEqual([
            {
                address1: '東京都',
                address2: '千代田区',
                address3: '飯田橋',
                kana1: 'トウキョウト',
                kana2: 'チヨダク',
                kana3: 'イイダバシ',
                prefcode: '13',
                zipcode: '1020072',
            },
        ]);
    });
    test('レスポンス確認 ハイフンあり', async () => {
        mockFn.mockResolvedValue({
            status: 200,
            data: {
                results: [
                    {
                        address1: '東京都',
                        address2: '千代田区',
                        address3: '飯田橋',
                        kana1: 'トウキョウト',
                        kana2: 'チヨダク',
                        kana3: 'イイダバシ',
                        prefcode: '13',
                        zipcode: '1020072',
                    },
                ],
                status: 200,
            },
        });
        const postCode = '102-0072';
        const result = await address.search(postCode);
        expect(result.status).toBe(200);
        expect(result.results).toStrictEqual([
            {
                address1: '東京都',
                address2: '千代田区',
                address3: '飯田橋',
                kana1: 'トウキョウト',
                kana2: 'チヨダク',
                kana3: 'イイダバシ',
                prefcode: '13',
                zipcode: '1020072',
            },
        ]);
    });
});4.テストコードを元にロジックコードを作成する
const app = require('./app');
require('dotenv').config();
const env = process.env;
const port = env.PORT;
app.listen(port, () => {
    console.log(`Listening on port ${port}`);
});routerの設定も行います。
require('dotenv').config();
const express = require('express');
const app = express();
const router = express.Router();
const rootRouter = require('./src/root');
app.use('/api/root', rootRouter);
app.use('/', router);
app.use(express.json());
module.exports = app;ここから実際のロジックを記載していきます。
まずは「root.js」からです。
const express = require('express');
const router = express.Router();
const log = require('./common/log');
const address = require('./common/address');
router.get('/', async (req, res) => {
    const systemLogger = log.getLogger();
    systemLogger.debug(req.query);
    let addressResult = {};
    if (req.query.postCode) {
        addressResult = await address.search(req.query.postCode);
        systemLogger.debug('addressResult:' + addressResult);
    }
    res.status(200).send({
        id: req.query.id,
        address: addressResult,
    });
});
module.exports = router;次に「address.js」です
const log = require('./log');
const axiosBase = require('axios');
const axios = axiosBase.create({
    timeout: 2000,
    headers: {},
    defaults: {},
});
exports.search = async (zipcode) => {
    const systemLogger = log.getLogger();
    let getData;
    try {
        await axios
            .get(`http://zipcloud.ibsnet.co.jp/api/search?zipcode=${zipcode}`)
            .then((res) => {
                if (res && res.status === 200) {
                    systemLogger.debug(res.data.results[0]);
                    getData = res.data;
                }
            })
            .catch((error) => {
                systemLogger.fatal(error.stack);
            });
    } catch (e) {
        systemLogger.fatal(e.stack);
    }
    return getData;
};
5.テストが通ることを確認する
下記のコマンドを実行してJestを起動します
npm run testすべてpassedになっていればテストが通っています。

6.github Actionsで実行させる
下記のコードをgithubに登録することで継続的にJestを実行させることができます。
name: ci
on:
    push:
        branches: [main]
    pull_request:
        branches: [main]
jobs:
    test:
        runs-on: ubuntu-latest
        steps:
            - name: Checkout
              uses: actions/checkout@v2
            - name: Setup Nodejs
              uses: actions/setup-node@v1
              with:
                  node-version: 14
            - name: Unit test
              run: |
                  npm install
                  npm testgithubに登録されると下記のように実行されます。
Jestでエラーがある場合は✕で表示され正常に終了した場合はチェックマークで表示されるので視覚的にわかりやすいと思います。

まとめ
今回はExpressでテストを実行させる方法について解説しました。
jestやgithub actionsを使ってCI/CDのCIの部分を効率化していきましょう。
今回のソースはこちらで公開しています。

