自主的20%るぅる

各々が自主的に好き勝手書くゆるふわ会社ブログ

express と jest だけでサックリとテストを書く!(superagent とか使わない)

こんにちは、江嵜です。

みなさん、テスト書いてますか?

ぶっちゃけ私はテスト書くのあんまり好きではありません。

なんというか、作業感あるんですよね。テストって。

まぁ、そんなこと言っても、実際テストは必要ですし、
何度もテストに助けられているので、書くんですけど。

今回は、express で作っている API に対して、どうやったら簡単にテストをかけるかなと思って考えてみました。

大体こういうの調べると、実際に express 動かして superagent でリクエスト流して…
みたいなことしていますが、それは面倒なので、今回は superagent なしでやってみようかと。

とりあえずテストされる環境準備

今回は express と jest を使うので、この二つを準備しましょう。

ちなみに jest は

Jest is a delightful JavaScript Testing Framework with a focus on simplicity.

公式ページにも書かれていますが、
とても簡単にテストが書けるフレームワークです。

npm install --save express
npm install --save-dev express

サーバーのコードはとりあえず簡単にこんな感じで。

const express = require(’express’)
const app = express()
const router = express.Router()

router.get(’/’, (req, res) => {
  if (req.query.id) {
    return res.status(200).send(`Hi! ${req.query.id}`)
  }

  res.status(400).send(’Who are you?’)
})

app.use(’/’, router)

app.listen(3000)

サーバーを node index.js で起動して、 Postman でリクエスト叩いてみると…

きちんと id に設定した名前で返答してくれています。
問題なさそうですね。

jest でテストを書く

では、このサーバーに対してテストを書きましょう。

ここで最終的にテストしたいのは、一言で言えば
/ (ルート) にアクセスしたときに、 id が含まれていれば名前を含む文字列を返す」
ということでしょうが、これはいわゆる結合テストですね。
この場合は実際に起動したサーバーに対して superagent などでリクエストするしかないですが、
基本はもっと細かい粒度でやりたいところかなと思います。

ということで、ルーティングは外して、
アクセスされたときの挙動 (「id が含まれていれば名前を含む文字列を返す」 の部分) だけテストしてみましょう。

ルーティングと処理を切り離す

まずは最初に、ルーティングの部分と、実際の処理を切り離す必要があります。

JavaScript なら簡単に別ファイルに切り出せますね。

index.js

const express = require(’express’)
const app = express()
const router = express.Router()
const { Get } = require(’./root’)

router.get(’/’, Get)

app.use(’/’, router)

app.listen(3000)

root.js

exports.Get = (req, res) => {
  if (req.query.id) {
    return res.status(200).send(`Hi! ${req.query.id}`)
  }

  res.status(400).send(’Who are you?’)
}

処理の方を、root へのリクエストなので root.js として切り出しました。

これで、 root.jsGet に対してテストを書くことで
ルーティング部分を無視してテストができますね。

本当はもう一つ理由があるのですが、それは後ほど。

テストを書く

では実際にテストを書いていきましょう。

今回は最初にお話した通り、 jest を使っていきますよ。
__test__ ディレクトリを作成して、その中にテストを格納します。

root.test.js

const { Get } = require(’../root’)

test(’status 200 with id’, () => {
  // ここにテストを書く
})

こんな感じですね。

さて、ここでどのようにテストを書くか、という事になります。
とりあえず id が格納されてれば 200 のステータスコードが格納されることを確認したいですね。
今回はサーバーを実際に立てるわけではないので、リクエストをして
ステータスコードを確認することはできません。

ここで、元の root.js を確認してみると、
引数から渡された res オブジェクトの status() に渡された値がステータスコードですね。
ということで、これを確認して、 200 が引数として渡されたことを確認できればよい、ということです。

さて、ここで先ほど、処理をルーティングから分けたもう一つの利点が発揮されます。

処理を分けたことで、今回テストする部分は単純な関数になっていますね!
ということで、 req, res に当たるオブジェクトのモックを作って、
単純に関数に渡すだけでテストが出来る!という寸法ですね。

そして、 jest には強力なモック作成機能がありますので、
これを使えば…

const { Get } = require(’../root’)

test(’status 200 with id’, () => {
  const req = {
    query: {
      id: ’ezaki’
    }
  }

  const res = {
    status: jest.fn().mockReturnThis(),
    send: jest.fn().mockReturnThis()
  }

  Get(req, res)

  expect(res.status.mock.calls[0][0]).toBe(200)
})

request の方は、単純に query に格納されたオブジェクトを参照しているだけなので、カンタンですね。
response の方は jest.fn() を使ってモックを作成しています。
mockReturnThis() を指定しておけば、 this を返却してくれるので、
express のように res.status().send() といった感じでチェーンメソッドで
プログラムを書くことにも簡単に対応できます。

で、実際のテストは

expect(res.status.mock.calls[0][0]).toBe(200)

ここですね。

詳しい説明は公式ドキュメントを参照してもらうとして、
カンタンに話すと、呼び出されたモックに対して mock.calls[n][m] で、

  • n 番目の引数に対して
  • m 回目の呼び出しで

指定された引数を取り出します。

今回は、引数 1 つ、呼び出した回数も 1 回なので、 mock.calls[0][0]
ステータスコードとして指定した 200 が格納されているのですね。

と、こんな感じでテストを書いて、 package.json の scripts に

”test”: ”jest”

として、 npm test を実行すると…

バッチリですね!

ちなみに request の id を空にしてみると…

きちんとテスト失敗していますね。

JavaScript でもサクサクテストかこう!

いかがでしょうか。
express のサーバー起動して…とかってテストすると大変ですが、
切り分ければ結構簡単にテストかけるんだなーって感じですよね。

この簡単さは jest のモック機能によるところが大きいので、
jest の便利さも分かってもらえたんじゃないかなと思います。

それではみなさん!
JavaScript でも楽しいテスト生活しましょうね!

Let’s share this article!

{ 関連記事 }

{ この記事を書いた人 }

アバター画像
takato_ezaki
記事一覧