こまぶろ

技術のこととか仕事のこととか。

環境構築弱者でも簡単に始められるテスト駆動開発〜mocha + power-assert でJavaScriptのテストを書く〜

環境構築弱者でもテスト駆動開発がしたい!

JavaScriptテスト駆動開発を始めてみる

みなさん、テスト書いてますか?僕は書いてません。

f:id:ky_yk_d:20180623082423p:plain

はい、すみません。

というわけで今回は、JavaScriptテスト駆動開発を始めてみたという記事になります。具体的には、

テストを実行するための、環境構築とテストの書き方をまとめました。

power-assert.jsは、和田卓人(@t_wada)さんが作成したライブラリです。その特質は下記のスライドをご覧ください。

www.slideshare.net

mocha.jsのGitHub

github.com

power-assert.jsのGitHub

github.com

【補足】「環境構築むずかしいのでは?」とブレーキをかける認知を補正する

最初、普通に環境構築してテスト書いて〜〜という記事を書いていたのですが、この記事を書くモチベーションについて思うところがあったので少し文章を。

テスト駆動開発に限らないのですが、何か新しいことを始めるときに、環境構築で転ける経験をしている方は多いのではないでしょうか。僕もそうで、いくつか転けてきた経験により何を始めるにも「よくわからないけど環境構築むずかしそう・・・」と二の足を踏んでしまうことがよくあります。

しかし、すでに始めている人からすると実は簡単なんだよというものは多いんじゃないかとも思っていますし、僕自身、何らかのきっかけで踏み出してみて、「なんだ、意外と簡単じゃん」と感じることもあります。

今回に関しても、「やってみたら意外と簡単だった」というのが結果としての感想なのですが、そういう記事が増えてくれるとどんどん新しいことを試していけるなぁと思いまして、とりあえず過去の自分に対しては「簡単なんだよ」と言えるように今回の記事を書きました。

そういうわけで、テスト駆動開発の中身(レッド・グリーン・リファクタリング)については特に記載していません。t_wadaさんの下記スライドをご覧ください。

環境構築

こちらの記事を参考にさせてもらいました。

コマンドラインからの環境構築

npmは入っている前提です。ちなみにMacです(たぶん関係ないと思う)。

cd [プロジェクトのディレクトリ]
npm init
mkdir test
npm install --save-dev mocha # たぶん
npm install --save-dev intelli-espower-loader # この辺は 
npm install --save-dev power-assert # 一気にやっていい

以上でコマンドラインからの環境構築は終了です。少し前まではnpm installと出てくると頭が痛かったのですが、VueやらWebpackやらと戯れているうちに大分慣れてきたように感じます。

npm run mochaで実行できるようにする

上記の設定だけを行い、テストを実行するコマンドmocha --require intelli-espower-loaderを叩いたところ、「mochaなんてコマンド知らねーよ」と怒られてしまいました。--save-devオプションをつけたmochaにはパスが通っていないので、mochaコマンドでは呼べません。

この事象に対処する一つの手段は、npm install -g mochaを実行することです。グローバルインストールすることで、パスが通ってmochaコマンドが使えるようになります。しかし、何でもかんでもグローバルインストールするのがダメだということは何となくわかります。

というわけで、下記の記事を参考にpackage.jsonを修正し、ローカルのものを使用するように変更しました。

package.json

{
  .
  .
  .
  "scripts": {
    "mocha": "mocha --require intelli-espower-loader"
  },
  .
  .
  .
}

以上の設定をすることで、npm run mochaでテストを実行することができました。いちいちオプションを指定しなくてもいいのも便利ですね。これで環境構築はできました。意外と簡単ですね。

※グローバルインストールしたmochaをnpm uninstall -g mochaでアンインストールしてみたところ、mochaコマンドは使えなくなりましたが、npm run mochaは問題なく使えたので意図した通り、ローカルのmochaを使えるようになったようです。

テストを書いて実行してみる

どう書くのか/どんな表示が出るのか

いよいよテストを書いて実行していきます。JavaScriptも初心者なのでツッコミどころがあったらGitHubなりTwitterなり(匿名がいい方は)質問箱なりに投げていただければ幸いです。

全て3の配列の2番目の要素(=3)と、1~5までの配列の2番目の要素(=2)を比較するテスト(落ちるはず)を書きました。下記の2つのコード(実装/テスト)を作成し、npm run mocha ./test/sample-test.jsコマンドを叩きます。さぁ、どんな結果が出るでしょうか。

./src/sample.js

exports.getActual = () => {
  let actual = [];
  [...Array(5)].map((_,i) => {
    actual.push(i + 1);
  });
  return actual;
};

./test/sample-test.js

const assert = require('power-assert');
const myModule = require('../src/sample');

describe('サンプルテスト', () => {
  it('落ちるテスト', () => {
    let expected = Array(5).fill(3);
    assert(myModule.getActual()[1]=== expected[1]);
  });
});

実行結果

f:id:ky_yk_d:20180623121041p:plain

想定通り落ちました。そして、「何が起きたのか」がコンソールに表示されています。t_wadaさんが「すごい表示、素晴らしい表示、実にわかりやすい表示」自画自賛する表示です。

単純に想定と実際が出てくるだけでなく、assert()の引数に渡した式の途中の評価結果も表示されています。これなら、どの時点でおかしくなっているのかもよくわかります。便利に使えそうですね。

-w オプションで変更を検知してテストを自動実行する

ちなみに、npm run mocha -w ./test/sample-test.jspackage.jsonscripts "mocha": "mocha --require intelli-espower-loader -w"-wオプションをつけると、ファイルの変更を検知してテストを自動実行するモードになります。このモードを終了するにはctrl + cです。下記の記事を参照しました。

毎回コマンド叩く必要がなくなるのでサイコーです。

2018.06.24 修正

"mocha": "mocha --require intelli-espower-loader"の設定下で、変更監視モードで起動するには、 npm run mocha -- -w ./test/sample-test.jsなどと--`を付けてからオプションとファイル名を記載する必要がありました。下記の記事を参照しました。

まとめ

以上、環境構築方法とテストの書き方をご紹介しました。簡単に始められたので、ガシガシテスト書いていこうと思います。

  • mocha + power-assert のテスト環境は簡単に構築できる!
  • power-assert は便利に使えそう!
  • -w オプションを使えばさらにテストは高速に!

参考記事一覧

テスト駆動開発

テスト駆動開発

【おまけ】素因数分解プログラムのテストを書いてみる

整数値を渡すと素因数分解した結果を配列にして返すプログラムを書いてみました。JavaScriptよくわからん。

なお、開発の過程はブログ向きでないので、GitHubリポジトリをご覧ください。コミットの単位が荒くて恐縮ですが、試行錯誤の跡を見ていただけるかと思います(笑)

github.com

実装コード

let isDivisor = (dividend, divisor) => {
  return dividend % divisor === 0;
}

exports.findSmallestFactor = function(num){
    let possibleLargestFactor = Math.floor(Math.sqrt(num));
    for (let i = 2; i <= possibleLargestFactor; i++){
      if (isDivisor(num, i)){
        return i;
      }
    }
    return 0;
  };

 exports.factorize = function(num){
    let result = [];
    let temp = num;
    while (true){
      let smallestFactor = this.findSmallestFactor(temp);
      if (smallestFactor === 0 ){
        result.push(temp);
        break;
      } else {
        result.push(smallestFactor);
        temp = temp / smallestFactor;
      }
    }
    return result;
  };

テストコード

const assert = require('power-assert');
const PrimeFactors = require('../src/PrimeFactors');

describe('PrimeFactors', () => {

  describe('素因数分解が正しく行われる', () => {

    it ('2のときは2を返す', () => {
      assert(PrimeFactors.factorize(2).toString() === [2].toString());
    });
    
    it ('4のときは[2,2]を返す', () => {
      assert(PrimeFactors.factorize(4).toString() === [2,2].toString());
    });
    
    it ('6のときは[2,3]を返す', () => {
      assert(PrimeFactors.factorize(6).toString() === [2,3].toString());
    }); 

    it ('108のときは[2,2,3,3,3]を返す', () => {
      assert(PrimeFactors.factorize(108).toString() === [2,2,3,3,3].toString());
    });

  });

  describe('最小の約数を返す', () => {

    it ('12を渡すと2を返す', () => {
      assert(PrimeFactors.findSmallestFactor(12) === 2);
    });
    
    it ('17を渡すと0を返す', () => {
      assert(PrimeFactors.findSmallestFactor(17) === 0);
    });

  });
  
});

実行結果

f:id:ky_yk_d:20180623112659p:plain

まさに「何もなければ黙るのみ、落ちるときはやかましく」ですね。