Jest : Javscript の テスト・フレームワークを使って非同期の関数をテストする

2020年7月30日プログラミング

Jest : Javscript の テスト・フレームワークを使って非同期の関数をテストする

こんにちは!

 

職場はみんなJava やKotlinを使っているけど、私の週末プログラミングは、なぜかJavascriptです、でおなじみの、しずかなかずしです。

 

Javascript、楽しんでますか??

 

私は、週末プロジェクトでJavascriptを使ったNode.jsのアプリを作るようになって、1年以上になります。

仕事では、テストが重要!評価効率を上げよう!などと息巻いていますが、私自身は仕事としてテスト・フレームワークなるものを使ってテストコードを書いたことがありません。

 

そうすると、開発現場でいくら自動化テストで効率アップ、と言っても説得力がない。この状況は、いかがなものか。

 

そう思い始めたのは、つい最近のこと。

 

という訳で、本日は、週末のJavascriptプロジェクトを自動化すべく、「Jest」というフレームワークを使ってみました。

 

Jestの導入は簡単

Node.jsでは、パッケージのインストールにはnpmを使うのが一般的。そして、Jestもその手順でインストール可能です。

とても便利ですね。

$ npm install --save-dev jest

 

最近のバージョンの npm には、 npx というプログラムが付いてきているのをつい最近知りました。

 

以前は、マシン全体に-gオプションでインストールしなければ実行しにくかったようなバイナリ形式のプログラムも、npxを使うとローカルインストールでも使用可能なのです。

 

ですので、以下のコマンドを実行すると、いきなりプロジェクトのルート・フォルダからテストが記載されているファイルを検索して実行を開始します。

 

$ npx jest

 

ABCDEFG.test.jsのように、自動テストのコードの記載されたファイルがあれば検索され実行されるようです。

test.jsでなければ、’__tests__’というフォルダにある .jsファイルも検索されるようですね。

以下では、前者のファイル名(.test.js) をつけてテストを作っていきます。

 

本家のGetting startedに手順があるので非常にわかりやすいです。

 

技術の進歩やトレンドの移り変わりが激しいWebプログラミングの世界。単にライブラリやツールそのものの技術的な良し悪し以上に、このような丁寧で整頓された文書化は普及するための1つのカギといえるでしょうね。

 

テストされるプログラムはもちろんHello, World!

さて、プログラミングの世界で何か新しいことを始めたら、一番最初にすることはなんでしょう?

 

はい、そうです。

 

Hello, worldですね。

 

先日、さまざまなプログラミング言語でHello, worldを実行するビデオをこのブログでも紹介しました。Hello, world!は、まさに、王道です。

 

しかし、単純に Hello, worldをプリントアウトするのはつまらない。今回は、せっかくなので、Promiseを使ったJavascriptのプログラムに挑戦します。

 

ということで、テストされるプログラム(hello_world.js)は以下のようなもの。

 

function hw_good() {
  return new Promise(function(resolve, reject) {
    resolve('hello, world!');
  });
}

function hw_bad() {
  return new Promise(function(resolve, reject) {
    resolve('hello, world?');
  });
}

module.exports = {
  good: hw_good,
  bad: hw_bad,
}

 

上記の hello_world.js はモジュール化したプログラムです。

一番最後のmodule.exportsで記載しているように、モジュールを使う側からみると、good()とbad()という関数があるようなモジュールです。

 

それぞれ、hw_good()関数は、hw_bad()関数にコードが実装されています。これらの関数は、呼び出されると非同期のPromiseを返し、処理を実行する順番が回ってきたら’hello, world!’、または、’hello, world?’ をresolve()で返して呼び出し元に処理が渡る、というもの。

 

このモジュールは、以下のように使います。使用例です。hello_world.main.jsとします。

 

var hello_world = require('./hello_world');

hello_world.good().then(function(good_text) {
  console.log('good_text: ' + good_text);
});

hello_world.bad().then(function(bad_text) {
  console.log('bad_text: ' + bad_text);
});

 

Node.jsのrequire()関数でモジュールをロードして、オブジェクトのgood()とbad()を呼び出して、非同期に結果のテキストを受取コンソールに表示するものです。ポイントは、.then()ですね。good()関数もbad()関数もPromiseが戻ります。ですので、結果はthen()で登録した関数に「非同期」に返ります。

 

このプログラムを実行すると、こんな感じに出力されます。(これだけみると「非同期感」は、まるで無い…)

 

$ node hello_world.main.js
good_text: hello, world!
bad_text: hello, world?

 

 

Promiseに関しては、以前の記事に色々とうんちくを書いておりますので、こちらもどうぞ。

 

非同期(Promise)をテストする

自動テストを実装してみます。

実装方法ですが、まず、

test()という関数の第1引数に、文字列を登録します。この文字列は後でこのテストが何をするものなのか?がわかるようなテストのタイトルになるものです。

test()の第2引数は、実行するテストを記載します。ここでは、テストしたい関数の実装をexpect()という関数に渡し、toBe()関数に期待する結果を記載します。

 

テストされるプログラムは2つあります。

  1. hello_world.good()に対しては、文字列’hello, world!’が正しい結果です。
  2. hello_world.bad()に対しては、文字列’hello, world?’が正しい結果です。

この2点をテストコードに落とします。以下のようなコード(hello_world.test.js)です。

const hello_world = require('./hello_world');

test ('test GOOD "hello, world"', () => {
  return hello_world.good().then(result => {
    expect(result).toBe('hello, world!');
  });
});

test ('test BAD "hello, world"', () => {
  return hello_world.bad().then(result => {
    expect(result).toBe('hello, world?');
  });
});

 

ポイントは、test()関数でテストの実装をするわけですが、まず非同期の関数を呼び出します。hello_world.good()のような呼び出しです。

先程の使用例で見たように、結果は非同期のthen()関数に渡ってきますので、その結果を expect().toBe()で確認する、という感じになります。

Promiseを使った非同期の関数のテストは本家のドキュメントにも記載があります。

 

npx jest hellow_world.test.js という形で単一のファイルを指定して自動テストを実行できます。

結果は以下のような感じですね。

$ npx jest hello_world.test.js
PASS./hello_world.test.js
✓ test GOOD “hello, world" (1 ms)
✓ test BAD “hello, world" (6 ms)
Test Suites: 1 passed, 1 total
Tests:   2 passed, 2 total
Snapshots:  0 total
Time:    0.839 s, estimated 1 s
Ran all test suites matching /hello_world.test.js/i.

 

結果はすべてパス!

大変よくできました〜


created by Rinker
¥4,620 (2021/07/24 07:58:33時点 Amazon調べ-詳細)

created by Rinker
オライリージャパン
¥4,620 (2021/07/23 19:53:25時点 Amazon調べ-詳細)