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 でも楽しいテスト生活しましょうね!

Takato Ezaki

Takato Ezaki小中高の塾講師からエンジニア

投稿者プロフィール

福岡で Web 系のエンジニアをしています。

中高の理科教師免許を取り、起業に 2 年間トライした後エンジニアの道へ入りました。

化学反応の中では Belousov-Zhabotinsky 反応が大好きです。

この著者の最新の記事

関連記事

コメントは利用できません。

募集中!(o゜▽゜)o

エンジャパン
求む、社長!
follow us in feedly

コッチもヨロシク!





最近のネタ!

  1. 2019-4-17

    第8回 麻雀部活動記録~まるで個室のような雀卓で~

    3月末の桜が満開な季節に恒例の麻雀部を開催してきました! 例によって活動記録を書いていこうと思いま…
  2. 2019-4-15

    JetBrains の IDE に新色登場! IntelliJ でも PhpStorm でも新しい色を体験しよう!

    JetBrains の IDE に新しいテーマが作られていました! こんにちは、江嵜です。 みな…
  3. 2019-4-8

    やっぱり JavaScript (TypeScript) でも アノテーション (Decorator) 使いたい!

    JavaScript でもアノテーションしたい! こんにちは、江嵜です。 JavaScript …
  4. 2019-4-3

    2019年03月度社員総会&懇親会@Tokyo☆

    2019年3月15日(金)に、エージェントグロー東京オフィスの社員総会&懇親会が開催されまし…
  5. 2019-4-2

    第2回 倶楽部ぽじてぃぶ ~ボードゲーム~

    2019年2月23日に、第2回となる倶楽部ぽじてぃぶの活動が開催されました! 今回は、その様子をお…
ページ上部へ戻る

当サイトに掲載されているコンテンツ(文書、画像等)は、許可なく複製・転用等する事を禁じます。

「フェアネス方式」は、株式会社エージェントグローが日本国内において出願中の商標です。

当サイトでは最低限必要と考えられる場合において、会社名/サービス名/商品名などを記載している場合があります。
これらはあくまでも説明の必要性に応じて用いているものであり、各社の権利等を侵害を目的とするものではございません。
不適切と考えられる場合には、当社お問い合わせフォームよりご連絡ください。

当サイトでは®や™などの表記を省略させていただいております。