クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【JS】Jestでテストを事始める(TDD:テスト駆動開発)

フロントエンド開発環境の続きです。コードチェック(ESLint)、コード整形(Prettier)ときて、次はコードテストです!JSをTestするからJestというわかりやすい名前w テスト駆動開発(TDD)とは何か?それでは行きましょう!

【目次】

※参考:【JS】ESLintで構文チェックを事始める - クモのようにコツコツと

※参考:【コードフォーマッター】PrettierでHTML、CSS、JSのコードを整形してみた - クモのようにコツコツと

Web開発環境まとめ
qiita.com

テスト駆動開発とは何か

Jestは「テスト駆動開発(TDD)」のツールと言われる。ナンチャラ駆動開発はたくさんあって、その中の一つ。

※参考:いろいろな駆動開発 - Qiita

「○○ Driven Development」のDrivenはDriveの過去形。

テスト駆動開発には3つのサイクルが存在します。
レッド → グリーン → リファクタリング
です。

テスト駆動開発(TDD)はレッド(失敗)、グリーン(成功)、リファクタリング(修正)のサイクルを回しながら、一つ一つの部品の安全性を高めて次の工程に進む作り方をするようだ。信号みたいなイメージ。

※参考:テスト駆動開発って何だろう | Developers.IO

フロントエンド開発でテストってどんな物なのだろう。

※参考:フロントエンドでTDDを実践する(理論編) - Qiita

フロントエンドのテストの難しさ。(コードのロジックではなくブラウザに依存する問題など)

※参考:フロントエンドの負債と向き合う - mizchi's blog

とにかくテストがどんなものか体験してみないとイメージしづらかった。ということでJestで体験してみる。

Jestのインストール

Jestも前回のPretterと同じような流れでインストールでけた。

いつものようにNode.jsとnpmは既に入っている前提(ターミナルで下記を叩くとバージョン番号が出る)

node -v
npm -v

Jestを入れるフォルダを作る。今回は「jest_test」とした。

cdコマンドでjest_testフォルダに移動。

cd (フォルダのパス)/jest_test

package.jsonの作成

npm init -y

ローカルにJestをインストールする。

npm install -D jest

package.jsonにjestが記述された!

  "devDependencies": {
    "jest": "^25.1.0"
  }

テストの対象の元ファイルを作成(内税計算)

ではさっそくテストを作成してみよう。公式サイトを参考に進めてみる。

※参考:Getting Started · Jest

まずテストの対象になる元ファイルsum.jsを作る。

元ファイルsum.jsにはやりたい処理のコードを書く。

function uchizei(goukei, zeiritsu) {
    return goukei * zeiritsu / ( 100 + zeiritsu );
  }
 module.exports = uchizei;
  • 関数uchizei()で消費税の内税を計算する処理。
  • module.exportsuchizeiをモジュール(部品)としてエクスポート

消費税の内税の計算式はこちらを参考にした。

※参考:消費税の内税の計算方法、簡単な計算式[8%、10%]|KW BLOG

税率が10%になって外税は計算しやすくなったが、内税は相変わらず計算しづらいよなーと思い。

テストファイルを作る

次にテストを実行するファイルsum.test.jsを作る。

テスト用ファイルは下記の構成で書くようだ。

const hoge = require('./(パス)');

test('テスト名', () => {
  expect(処理).toBe(予想結果);
});
  • 変数hogerequire()で元ファイルを読み込む。
  • test()の中、第一引数はテスト名、第二引数は無名関数
  • 無名関数の中、expect()の引数に処理、toBe()の引数に予想結果を書く

書いてみた。

const uchizei = require('./sum');

test('内税計算', () => {
  expect(uchizei(860, 10)).toBe(78);
});

uchizei()の第一引数(合計)に860円、第二引数(税率)に10%を入れると結果は78円になるはずだよ、と想定している。

テスト実行!するがnpm ERR…

さあ、テスト実行!

npm run test

結果

> jest_test@1.0.0 test /(パス)/jest_test
> echo "Error: no test specified" && exit 1

Error: no test specified
npm ERR! (後略)

おや?エラー?

「テストが指定されていません」となった。その下にnpm ERRが何行か連なっている。

package.jsonのscriptsを書き直す

原因はpackage.jsonにありそう。

自動的に作られたscriptsオプションはこうだった。

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },

公式サイトによるとscriptsはこれだけでいいみたい。

  "scripts": {
    "test": "jest"
  }

上記に書き直す。

初テスト成功!(結果はレッド)

テスト再実行!

npm run test

結果

 FAIL  ./sum.test.js
  ✕ 内税計算 (6ms)

  ● 内税計算

    expect(received).toBe(expected) // Object.is equality

    Expected: 78
    Received: 78.18181818181819

      2 | 
      3 | test('内税計算', () => {
    > 4 |   expect(uchizei(860, 10)).toBe(78);
        |                            ^
      5 | });

      at Object.<anonymous> (sum.test.js:4:28)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.846s
Ran all test suites.
npm ERR! …(後略)

お、テストが実行された!人生初テスト成功です!

f:id:idr_zz:20200128055236j:plain

FAILのところが赤色になっている。レッドはテスト失敗という意味。テスト実行は成功したがテスト結果は失敗という。ややこしやw

また、結果の後にはnpm ERRはまだいくつか続いている。

大元のnpmのバージョンが古いからか?と思いグローバルのnpmを入れ直してみた。

※参考:【初心者】npm ERR! の対処法 - Qiita

結果は解消されず…。うーむ、なんだろう。

コードを書き直してテスト再実行(ついにグリーン!)

とはいえテストの実行結果自体が出たのは前進!

さっそくコードを修正してみよう。先ほどのレッドでは内税の計算結果に78.18181818181819と小数があるのことを指摘された。

Math.floor()関数を使って小数を切り下げに書き直してみる。

function uchizei(goukei, zeiritsu) {
    return Math.floor(goukei * zeiritsu / ( 100 + zeiritsu ));
  }
 module.exports = uchizei;

※参考:【JavaScript】桁指定して四捨五入・切り上げ・切り捨て - Qiita

テスト再実行!

npm run test

おお、今度は通った!

 PASS  ./sum.test.js
  ✓ 内税計算 (2ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.24s
Ran all test suites.

PASSのところが緑色になっている!グリーンは成功!

f:id:idr_zz:20200128055435j:plain

あと、先ほどようなnpm ERRが今回は出なかった。これはテストの結果が失敗すると出るのかな?

単なる足し算に書き直してみる

レッドの時の挙動を確かめるべく、元のコードを単なる足し算に変えててみると…

function uchizei(goukei, zeiritsu) {
    //return Math.floor(goukei * zeiritsu / ( 100 + zeiritsu ));
    return goukei + zeiritsu;
  }
 module.exports = uchizei;

テスト再々実行

npm run test

お、FAIL(レッド)になった。

 FAIL  ./sum.test.js
  ✕ 内税計算 (5ms)

  ● 内税計算

    expect(received).toBe(expected) // Object.is equality

    Expected: 78
    Received: 870

      2 | 
      3 | test('内税計算', () => {
    > 4 |   expect(uchizei(860, 10)).toBe(78);
        |                            ^
      5 | });

      at Object.<anonymous> (sum.test.js:4:28)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        2.153s
Ran all test suites.
npm ERR! …(後略)

またnpm ERRが続いている。

テストの方の予測結果も足し算に変えてみると…

const uchizei = require('./sum');

test('内税計算', () => {
  //expect(uchizei(860, 10)).toBe(78);
  expect(uchizei(860, 10)).toBe(870);
});

今度はPASS(グリーン)。テストが通った。

 PASS  ./sum.test.js
  ✓ 内税計算 (3ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.958s, estimated 1s
Ran all test suites.

そしてnpm ERRはない。やはりレッドの時だけ出るようだ。

debug.logが書き出されている

先ほどのnpm ERRの中にdebug.logというファイルが書き出されている。

npm ERR!     /(パス)/.npm/_logs/2020-01-27T20_56_43_142Z-debug.log

ログを見るとエラー内容の詳細が書き出されている。翻訳してみたがいまいちよくわからず…

そもそもdebug.logとは何か?

「不具合の調査をするときに役に立つかもしれないから、取りあえずメモっておこうかな」な内容が書いてあります。

※参考:https://wa3.i-3-i.info/word1418.html

ふむ、やはりまだ不具合があるのかな?

npmログを切って再実行(--silentオプション)

Jestのdebug.logについて調べるとこちらの記事が同じ状況ぽい。

※参考:npm run testするときはnpmログを切ったほうがいい - Qiita

npm ERR!ってでてるのでシステムエラーかとも思うんですが、よく見るとテストのコマンドのところで、Exit status 1ってなっているのが原因のようです。

確かに最初のpackage.jsonscriptsにもnpm ERRないにもExit statusという記述がある!

Exit statusコマンドは「終了ステータス」のことで成功は0、失敗は1らしい。

※参考:終了ステータス | UNIX & Linux コマンド・シェルスクリプト リファレンス

なるほど。テストが失敗したらエラーログが出る設定になっているんだ。

これを解消する方法、先ほどの記事によると--silentというオプションを付けると出なくなるようだ。

npm run test --silent

公式サイトにも記述があった。

--silent
コンソール経由でメッセージを表示しないようにします。

※参考:Jest CLI Options · Jest

という事、内税計算に戻してから--silentオプション付きテストを実行してみる。

function uchizei(goukei, zeiritsu) {
    return Math.floor(goukei * zeiritsu / ( 100 + zeiritsu ));
    //return goukei + zeiritsu;
  }
 module.exports = uchizei;

どうだ!?

npm run test --silent

おお、今度は結果のみでnpm ERRが出なくなった。

 FAIL  ./sum.test.js
  ✕ 内税計算 (7ms)

  ● 内税計算

    expect(received).toBe(expected) // Object.is equality

    Expected: 870
    Received: 78

      3 | test('内税計算', () => {
      4 |   //expect(uchizei(860, 10)).toBe(78);
    > 5 |   expect(uchizei(860, 10)).toBe(870);
        |                            ^
      6 | });

      at Object.<anonymous> (sum.test.js:5:28)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        2.228s
Ran all test suites.

テスト実行のたびにデバッグログが出るのは少し猥雑なので、必要に応じてということにしようかな(デバッグログの解読能力もまだ足りない…)

最後に

ということで人生初テストが成功しました!

npm ERRが出たのでちょっと戸惑いましたがそれはシステムエラーではなかったようで安心しました。

テストが通ったコードを積み重ねていって全体が完成する頃にはエラーが起こらない形になる、というテスト駆動開発(TDD)。

いつもはブラウザで挙動を確認してからエラーの原因を探すというやり方でした。テストしながら進めると最初からロジックを意識したコードが書けそうに感じました。それではまた!


Web開発環境まとめ
qiita.com