JavaScript Mochaによるユニットテスト

JavaScriptのテストフレームワークMochaによるユニットテストを記載します.

まず, MochaはNode.js上で動作するテストフレームワークなので, Node.jsをインストールしておく必要があります.

1. Mochaのインストール

$ (sudo) npm install -g mocha

2. テストコード

test.js

var assert = require('assert');

describe('Unit TEST', function() {

    it('add', function() {
        assert(1 + 1 === 2);
    });

    it('sub', function() {
        assert(1 - 1 === 0);
    });

    it('mul', function() {
        assert(4 * 2 === 8);
    });

    it('div', function() {
        assert(4 / 2 === 2);
    });

});

3. テスト実行

$ mocha (--reporter spec) test.js

  Unit TEST
    ✓ add
    ✓ sub
    ✓ mul
    ✓ div


  4 passing (9ms)

カリー化と部分適用

カリー化と部分適用…今までずっと混同していたので, 備忘録的に記載しておきます.

JavaScriptの例

// 部分適用可能な関数を返す関数の定義 (カリー化)
var pow = function(exponent) {
    return function(base) {
        return Math.pow(base, exponent);
    }
};

// 引数を適用して, 新しい関数を生成する (部分適用)
var square = pow(2);
var cube   = pow(3);

console.log(square(2));  // -> 4
console.log(square(3));  // -> 9
console.log(cube(2));    // -> 8
console.log(cube(3));    // -> 27

Java 8 Optional

Java 8では, nullをより安全に処理するための仕様であるOptional型が導入されました. 使い方はこんな感じです.

OptionalのクラスメソッドであるofやofNullable (値がnullかもしれない場合に利用) でOptionalインスタンスを生成できます.

Optionalでラップされた値を取り出す (アンラップする) には, Optionalインスタンスの以下のメソッドを利用します.

Method Description
get 単純なアンラップ
orElse nullでなければ, アンラップ. nullであれば, 第1引数に渡したオブジェクトを返す.
orElseGet nullでなければ, アンラップ. nullであれば, 第1引数に渡した関数の結果を (遅延処理して) 返す.
ifPresent nullでなければ, 第1引数に渡した関数を実行. nullであれば何もしない.
map nullでなければ, 第1引数に渡した関数の実行結果を返す. nullであれば空のOptionalを返す.

Web MIDI API MIDIメッセージの受信

MIDIデバイスからのMIDIメッセージを受信するには, MIDIInputインスタンスのonmidimessageイベントハンドラを定義します.

onmidimessageイベントハンドラには,MIDIMessageEventイベントオブジェクトが引数に渡されます.

MIDIMessageEventイベントオブジェクトには,Number型のreceivedTimeプロパティとサイズが3のUint8Arrayのdataプロパティが定義されています.

そして, MIDIメッセージはこのUint8Arrayのdataプロパティに格納されています. MIDIメッセージの詳細はこちらを参照してください.

インデックス0のデータは,  音を鳴らす (9nH ノートオン) or 止める (8nH ノートオフ) メッセージが格納されています.

サンプルコードでは, この値 (のマスク値) で分岐して, noteOn関数かnoteOff関数のどちらを呼び出すかを決定しています.

インデックス1のデータは,ピッチ (音の高さ) を表すメッセージとなります (ノートナンバーと呼ばれます). 最も低い音が0, 最も高い音が127となります. また, ピアノ88鍵に対応するノートナンバーは, 22 〜 109となります.

サンプルコードでは, この値をWeb Audio APIのOscillatorNodeに反映させています.

インデックス2のデータは, 音の強弱を表すメッセージとなります (ベロシティと呼ばれます). 1 〜 127までの値をとり, 127が最も強い音となります.

サンプルコードでは, この値をWeb Audio APIのGainNodeに反映させています.

Web MIDI API MIDIPort (MIDIInput / MIDIOutput) インスタンスを取得する

MIDIPort (MIDIInput / MIDIOutput) インスタンスとは, 簡単に表現すれば, 入出力となるMIDIデバイスを抽象化したオブジェクトのことです.

厳密には, MIDIPortクラスのサブクラスとして, MIDIInputクラス / MIDIOutputクラスが定義されています.

MIDIPort (のサブクラスのインスタンス) がMIDIデバイスとMIDI信号の送受信処理をするので, MIDIAccessインスタンスからMIDIPort (のサブクラスの) インスタンスを取得します.

navigator.requestMIDIAccess({sysex : true}).then(function(midiAccess) {
    var inputs  = [];
    var outputs = [];

    console.dir(midiAccess);

    if (typeof midiAccess === 'function') {
        // Legacy Chrome
        inputs  = midiAccess.inputs();
        outputs = midiAccess.outputs();
    } else {
        // Chrome 39 and later
        console.dir(midiAccess.inputs);
        console.dir(midiAccess.outputs);

        var inputIterator  = midiAccess.inputs.values();   // MIDIInputMap  -> Iterator
        var outputIterator = midiAccess.outputs.values();  // MIDIOutputMap -> Iterator

        console.dir(inputIterator);
        console.dir(outputIterator);

        // Iteration
        for (var i = inputIterator.next(); !i.done; i = inputIterator.next()) {
            inputs.push(i.value);
        }

        for (var o = outputIterator.next(); !o.done; o = outputIterator.next()) {
            outputs.push(o.value);
            }
        }

        console.dir(inputs);
        console.dir(outputs);
}, function(error) {
       console.dir(error);
});

レガシーなChromeでは, MIDIPortインスタンスの取得が異なり, MIDIAccessインスタンスのinputsメソッド / outputsメソッドを呼び出すだけです.

Chrome 39以上のバージョンでは,  MIDIInputMapインスタンス / MIDIOutputMapインスタンスにアクセスする必要があります.

そのために, MIDIAccessインスタンスのinputsプロパティ / outputsプロパティにアクセスします.

そして, valuesメソッドを呼び出すことで, 入出力のMIDIデバイスを抽象化したオブジェクトの集合, つまり, MIDIPortインスタンスの集合であるIteratorオブジェクトを取得できます.

あとは, イテレーションして各要素を抽出し, 配列に格納しておくだけです.

Web MIDI API MIDIAccessインスタンスを取得する

まず前提として, Web MIDI APIを利用するためにはMac版ChromeのWeb MIDI APIを有効にする必要があります.

  1. アドレスバーに chrome://flags を入力する
  2. Web MIDI APIを有効にして, Chromeを再起動します.
Chrome Web MIDI API設定
Chrome Web MIDI API設定

Web MIDI APIを利用するための最初のステップは, Web MIDI APIのほとんどの処理の起点となる, MIDIAccessインスタンスを取得することです.

そのために, navigatorオブジェクトのrequestMIDIAccessメソッドを呼び出します.

requestMIDIAccessメソッドはPromiseオブジェクトを返すので, 後続関数となるthenの第1引数にリクエストが成功した場合の処理を, 第2引数にリクエストが失敗した場合の処理を記述します.

navigator.requestMIDIAccess({sysex : true}).then(function(midiAccess) {
    // 第1引数は, MIDIAccessインスタンス
    console.dir(midiAccess);
}, function(error) {
    // 第1引数は, DOMErrorインスタンス (リクエストがユーザーによりブロックされた場合など)
    console.dir(error);
});

以上の設定 & コードでWeb MIDI APIを利用するための最初のステップが完了です.