読者です 読者をやめる 読者になる 読者になる

JavaScript - Jasmineでパラメタライズドテストするためのプラグインを作った

Javascript

jasmine-parameterizeというのを作りました。

f:id:kanno_kanno:20131018142332p:plain

背景

「パラメータだけ違って他が同じ」ようなテストを簡潔に書きたい場合があります。
これを実現するために「パラメータ」と「テスト本文」を切り分けるように書く仕組みが
パラメタライズドテストってやつだと思っているんですが、この辺の理解は自信ないです。

このパラメタライズドテストを行うための機構がJasmineにはなくて、
プラグインも良さそうなのを見つけられなかったので自分で作りました。
(ちなみにQUnitにはプラグインあります)

簡単なfizzbuzzテストを例に取ります。
テストの書き方が主目的なのでfizzbuzzとは何かには触れません。

まず標準のJasmineだけで愚直に書くとこうなります。

describe('fizzbuzz()', function() {
  it('sample', function() {
    expect(fizzbuzz(3)).toEqual('fizz');
    expect(fizzbuzz(5)).toEqual('buzz');
    expect(fizzbuzz(7)).toEqual(7);
    expect(fizzbuzz(9)).toEqual('fizz');
    expect(fizzbuzz(15)).toEqual('fizzbuzz');
  });
});

f:id:kanno_kanno:20131018142329p:plain

これでもいいのですが、次のような点が気になります。

  • 各expectでどこが違うのかパッと見て分かりにくい
  • 頑張ってexpect増やしても結果のexamplesは1つのままで寂しい

これを解消するために以下サイトでは次のような方法が書かれています。
Parameterized testing in Javascript

「自分でパラメータ一覧をぐるぐる回してitを呼ぶぜ!」

これに従い上記テストを書き換えるとこうなります。

describe('fizzbuzz()', function() {
  [
    [3, 'fizz'],
    [5, 'buzz'],
    [7, 7],
    [9, 'fizz'],
    [15, 'fizzbuzz'],
  ].forEach(function(param){
    it('sample', function() {
      expect(fizzbuzz(param[0])).toEqual(param[1]);
    });
  });
});

f:id:kanno_kanno:20131018142331p:plain

細かい書き方は色々変えられますが、次のような問題があります。

  • foreach~functionを毎回書くのがダルい
  • example名が同じで判別が付かない
    • 動的に名前を作ればいいんですけどタルい。JSは文字列中の変数展開できないし
  • 配列だと要素がparam[0]とかになって何を指しているのか分からない
    • オブジェクトでkeyアクセスにすれば明示的になるが、代わりに汚さを増す
    • it()内で一時変数に置いても同様

ということで、この辺を包み込んで書けるようにしたのをプラグインとしました。

インストール

jasmine-parameterize.jsを落としてきて読み込んでください。
npmでインストールとかナウいことはできません。

使用例

cases()にパラメータの配列を渡します。
この関数はオブジェクトを返し、それを通してit()を呼びます。
この時it()のfunctionに各パラメータが引数として渡されます。

describe('fizzbuzz()', function() {
  cases([
    [3, 'fizz'],
    [5, 'buzz'],
    [7, 7],
    [9, 'fizz'],
    [15, 'fizzbuzz'],
  ])
  .it('sample', function(n, expected) {
    expect(fizzbuzz(n)).toEqual(expected);
  });
});

f:id:kanno_kanno:20131018142332p:plain

もしパラメータが1つしかなければ一次元配列でも構いません。

describe('something', function() {
  cases([10, 20, 30])
  .it('spec', function(num) {
    // assert
  });
});

おわりに

パラメタライズの仕組みは言語/ライブラリによっては、テスト本体と離れていて逆に読みにくいと感じることがあります。
が、Jasmineではテスト本体と近い位置に書けるのでいいんじゃないかなと思います。

満足です。