3行まとめ

  • espower-babelは役目を終えたので、Deprecated
  • Babel + power-assertはbabel-preset-power-assertを使う
  • コード上はrequire("power-assert")require("assert") どちらでもpower-assert化できるようになった

espower-babelは非推奨へ

Babel + Mocha + power-assertの組み合わせを出来るだけ設定ファイルなどを作らずに使えるespower-babelというモジュールを書いていましたが、役目を終えたので非推奨(deprecated)にしました。

理由としては、Babel@6からは設定(ファイル)を必ず必要とするので、espower-babelをかませる分、柔軟性がなくなったり余計な処理が起きて遅くなるためです。

代わりにbabel-registerbabel-preset-power-assertを直接使って、開発時のBabelのビルド設定としてpower-assertを導入する方法を推奨しています。

以下は、power-assert + Mocha + Babel環境を新規インストールする場合の手順ですが、espower-babelからの移行はmigrate-espower-babel-to-babel-preset-power-assertを使うことで同様のことができるようにしてあります。

$ npm i -g migrate-espower-babel-to-babel-preset-power-assert
$ cd <該当プロジェクトrootへ>
$ migrate-espower-babel-to-babel-preset-power-assert

でマイグレーションしてくれます。 (自分のプロジェクトをespower-babelから移行するのに書いたので、人により構成が違うため動かないかもしれません。その場合はPull Requestをいただけると助かります。)


新規で、power-assert + Mocha + Babel環境(ランタイム変換)を導入する手順です。

サンプルプロジェクト

設定方法

必要なモジュールをインストール

npm i -D power-assert mocha babel-register babel-preset-power-assert babel-preset-es2015

mochaからテストを実行する際にBabelの変換をするので、babel-registerと以下の2つのpresetをインストールします。

  • babel-preset-es2015
    • ES2015の変換を行うpreset
  • babel-preset-power-assert
    • power-assert関連のpreset

.babelrcを作成

Babelの設定をするために、.babelrcを次のように作成します。 power-assertは開発ビルド(テスト中)にしか必要ないので、envで振り分けしておきます。 envNODE_ENVによって振り分けされます。 NODE_ENV=production <コマンド>のような感じで環境変数を指定し、必要なプラグインを分けることができます。 何も指定していない場合はNODE_ENV=developmentと同じになります。

{
  "presets": [
    "es2015"
  ],
  "env": {
    "development": {
      "presets": [
        "power-assert"
      ]
    }
  }
}

mocha.optsを作成

Mochaの設定をするmocha.optsを作成します。

test/mocha.opts に以下のように書くだけです。設定ファイルとして作らないで、引数に渡すだけでいいかもしれません。

--compilers js:babel-register

こうすることで、Mochaのテスト実行時はBabelによる変換がランタイムで行われます。

テストを書く

次のような足し算をするコードを書いてみます。

add.js:

"use strict";
const assert = require("assert");
export default function add(x, y) {
    assert(typeof x === "number");
    assert(typeof y === "number");
    return x + y;
}

これをテストするコードをassertモジュールを使って書いてみます。

add-test.js

// require("power-assert") じゃなくて assert でもいい
const assert = require("assert");
import add from "../src/add";
describe("add", function () {
    it("should return x + y", function () {
        const result = add(1, 2);
        assert.equal(result, 5);// <= Wrong
    });
});

$ mochaという感じでテストを実行してみると、このテストは3 == 5となっているので失敗します。

power-assert result

テストを失敗すると、power-assertにより構造的な結果が表示されています。 .babelrcの設定にもとづいて変換する際にbabel-preset-power-assertが次の2つのことをやっています。

  1. require("assert")require("power-assert")に書き換える
  2. assert関数などに仕込みをして、失敗した時に情報をいっぱい表示できるようにする
    • こちらは今までもやっていたこと

これにより、require("power-assert")ではなく、ただのrequire("assert")もpower-assert化された状態でassertionが行えるようになっています。

通常今までは、テストコードのみrequire("power-assert")をして、アプリケーションコード側ではrequire("assert")をしていたと思います。

例えば、このadd.jsの例では引数のチェックにassertを使っています。

"use strict";
const assert = require("assert");
export default function add(x, y) {
    assert(typeof x === "number");
    assert(typeof y === "number");
    return x + y;
}

なので、次のようなコードを書くと例外を投げるのでテストは失敗します。

    it("arguments should be type of number", function () {
        add("string", "string");
    });

今までは、ただのassert()だったので、大した情報はでませんでした。 しかし、これもMocha経由で実行してみると、assert(typeof x === "number");がpower-assert化されています。

assert to be power-asssert

これはどういうことになるかというと、コード上でライブラリとしてrequire("power-assert")と読み込む必要がなくなり、ツールとしてpower-assertが動くようになった事を意味します。

なので、power-assertを使わなくなった時はツール(具体的にはbabelrcから)power-assertを外すだけで、コードは一切変更しなくても良くなったということです。

そのため、power-assertは開発用のライブラリからツールという位置づけに変わったという話でした。

サンプルプロジェクト

おまけ

アプリケーションにassertを書くようになったと言っても、今回のadd(x ,y)のように決まりきったような引数のチェックを毎回書くのは大変です。

先ほどのadd.jsは次のような感じでテストすることができます。

const assert = require("assert");
import add from "../src/add";
describe("add", function () {
    it("should return x + y", function () {
        const result = add(1, 2);
        assert.equal(result, 3);
    });
    it("arguments should be type of number", function () {
        try {
            add("string", "string");

            throw new Error("unreachable line");
        } catch (error) {
            assert.equal(error.name, assert.AssertionError.name);
        }
    });
});

引数の型違反例外(assert.AssertionError)もテストされていることが分かります。

JSDocから自動でランタイムAssertionを追加してくれるbabel-plugin-jsdoc-to-assertを使うことで、add.jsから型チェック的なassertは取り除けます。

具体的にはpresetsbabel-preset-jsdoc-to-assertを追加して、JSDocを書くだけです。

--- a/.babelrc
+++ b/.babelrc
@@ -5,6 +5,7 @@
   "env": {
     "development": {
       "presets": [
+        "jsdoc-to-assert",
         "power-assert"
       ]
     }

diff --git a/src/add.js b/src/add.js
index 4748ae3..5cc3b9c 100644
--- a/src/add.js
+++ b/src/add.js
@@ -1,7 +1,9 @@
 "use strict";
-const assert = require("assert");
+/**
+ * @param {number} x
+ * @param {number} y
+ * @returns {Number}
+ */
 export default function add(x, y) {
-    assert(typeof x === "number");
-    assert(typeof y === "number");
     return x + y;
-}

変更したコミット:

babel-preset-jsdoc-to-assertもpower-assert化できるといいのですが、Babelの変換の仕組み上難しいのでまだできてません。

また、assertをアプリケーション側で使っていた際に、プロダクションビルドからは取り除きたいということがあります。その場合はunassertを使えば、取り除けるので便利です。

変更したコミット:

ここまでを全部適応した最終的な.bebelrcは次のような形になっています。

{
  "presets": [
    "es2015"
  ],
  "env": {
    "development": {
      "presets": [
        "jsdoc-to-assert",
        "power-assert"
      ]
    },
    "production": {
      "plugins": [
        "babel-plugin-unassert"
      ]
    }
  }
}

おわり

  • espower-babelはBebel5以下における解だったので、役目はひとまず終わり
  • 普通にコンパイル言語と同じように、デバッグ時のみ情報量の多いassertの適応、プロダクションビルド時は取り除くということができるようになった
  • ホーア論理的な事前条件はassertやJSDocで、事後条件はテストで保証するみたいなことはやりやすくなった
  • コード量や実行速度はそこまで変わらずにチェックできることが色々増えた