超今さらExpress触ってみた - その4 やっとテスト書けるよ -

超今さらExpress触ってみた - その3 Cloud Functions Emulatorでも動くようにする - の続き。

Request Handlerを直接テストする

今回の目的は express アプリの end-to-end テストじゃないので、Request Handler である以下のコードだけをテストする。

module.exports.hello = (req, res) => {
  res.send('Hello Express')
}

さて道具立てはいつもの mocha, power-assert に加えて

howardabrams/node-mocks-http: Mock 'http' objects for testing Express routing functions

を使うことにする。

今回面倒くさいのは、単なる function のテストなのに function は何も return してくれなくて、引数に与えた res が変化するのでそれを見ろ、というところ。基本的に callback 地獄を避けてきたので、この辺のテストの知見がない。素直に function は return してくれとは思うが、そうもいかないので上のライブラリを利用する。

こんな感じ。

test/index.spec.js

const httpMocks = require('node-mocks-http')
const assert    = require('power-assert')

const {hello: IndexHandler} = require('../index')

describe('IndexHandler', () => {
  it('can handle request', () => {
    const req = httpMocks.createRequest({url: '/'})
    const res = httpMocks.createResponse()

    IndexHandler(req, res)

    assert.equal(200, res._getStatusCode())
  })
})

httpMocks.createResponse() で得られるオブジェクトは Express の response とは構造はだいぶ違うけれども、status や body など、欲しい情報を取得してテストを書くには十分機能してくれる感じがする。

似た機能を持つライブラリは実はたくさんあるんだけど、あまりダウンロード数がなくて作った本人とごく一部の人しか使っていないのでは?と思われたり、 Sinon に寄せすぎて素直にデータを取り出すことができなかったりするので、これくらいのものでよしとすることにした。

ということで、ここまででやっかいな Request Handler に対してテストを書けるようになった。あとは Google Cloud API を使わない部分に関しては普通の class や function と同じようにやればよいはず。

つまり、テスト書けるようになった!

Googleのユニットテストのドキュメントと比較

テストと CI/CD | Cloud Functions のドキュメント | Google Cloud

これを読むと Google さんは全部 Sinon の Mock で処理しろと書いてるんだけど、Mock って使いすぎると結局中の動作を別な形で書き換えただけになりかねなくて重い1ので、できれば request と response の対応を、インターフェイスをテストする形にしておきたいので、上のような方法を選んだ。(こうしておけば中の処理が変わっても大丈夫。)

参考

Unit testing Express route handlers | Shesh's blog

  1. 動作として重いということでなく、だいぶホワイトボックステスト寄りになって変更に弱くなってしまうので、存在として、資産というか負債というか、とにかく重い 

More