web-dev-qa-db-ja.com

同じモカテストを異なるデータで複数回実行する

問題

モカで同じことを行ういくつかのテストがあります。私にとってこれは重複であり、システムをメンテナンスしたいときに行うのは最悪のことです。

var exerciseIsPetitionActive = function (expected, dateNow) {
    var actual = sut.isPetitionActive(dateNow);
    chai.assert.equal(expected, actual);
};

test('test_isPetitionActive_calledWithDateUnderNumSeconds_returnTrue', function () {
    exerciseIsPetitionActive(true, new Date('2013-05-21 13:11:34'));
});

test('test_isPetitionActive_calledWithDateGreaterThanNumSeconds_returnFalse', function () {
    exerciseIsPetitionActive(false, new Date('2013-05-21 13:12:35'));
});

私が必要なものは何

重複したモカテストを1つだけで折りたたむ方法が必要です。

たとえば、PhpUnit(および他のテストフレームワーク)では、 dataProviders があります。
phpUnitでは、dataProviderは次のように機能します。

<?php class DataTest extends PHPUnit_Framework_TestCase {
    /**
     * @dataProvider provider
     */
    public function testAdd($a, $b, $c)
    {
        $this->assertEquals($c, $a + $b);
    }

    public function provider()
    {
        return array(
          array(0, 0, 0),
          array(0, 1, 1),
          array(1, 0, 1),
          array(1, 1, 3)
        );
    }
}

ここのプロバイダーはテストにパラメーターを注入し、テストはすべてのケースを実行します。重複したテストに最適です。

モカに似たようなものがあるかどうか、たとえば次のようなものを知りたいです。

var exerciseIsPetitionActive = function (expected, dateNow) {
    var actual = sut.isPetitionActive(dateNow);
    chai.assert.equal(expected, actual);
};

@usesDataProvider myDataProvider
test('test_isPetitionActive_calledWithParams_returnCorrectAnswer', function (expected, date) {
    exerciseIsPetitionActive(expected, date);
});

var myDataProvider = function() {
  return {
      {true, new Date(..)},
      {false, new Date(...)}
  };
};

私がすでに見たもの

Shared Behaviours と呼ばれるテクニックがあります。しかし、テストスイートで問題を直接解決するのではなく、テストを複製したさまざまなコンポーネントで問題を解決するだけです。

質問

mochaでdataProvidersを実装する方法を知っていますか?

42
Tomas Prado

Mochaはそのためのツールを提供していませんが、自分で簡単に行うことができます。ループ内でテストを実行し、クロージャーを使用してテスト関数にデータを渡すだけです。

suite("my test suite", function () {
    var data = ["foo", "bar", "buzz"];
    var testWithData = function (dataItem) {
        return function () {
            console.log(dataItem);
            //Here do your test.
        };
    };

    data.forEach(function (dataItem) {
        test("data_provider test", testWithData(dataItem));
    });
});
22
Kaizo

異なるデータで同じテストを実行する基本的なアプローチは、データを提供するループでテストを繰り返すことです。

describe('my tests', function () {
  var runs = [
    {it: 'options1', options: {...}},
    {it: 'options2', options: {...}},
  ];

  before(function () {
    ...
  });

  runs.forEach(function (run) {
    it('does sth with ' + run.it, function () {
      ...
    });
  });
});

beforeは、it内のすべてのdescribesの前に実行されます。 beforeのオプションの一部を使用する必要がある場合、しないforEachループに含めます。これは、mochaが最初にすべてのbeforesとすべてのits。これはおそらく不要です。 describe全体をループに入れることもできます。

var runs = [
  {it: 'options1', options: {...}},
  {it: 'options2', options: {...}},
];

runs.forEach(function (run) {
  describe('my tests with ' + run.it, function () {
    before(function () {
      ...
    });

    it('does sth with ' + run.it, function () {
      ...
    });
  });
});

複数のdescribesでテストを汚染したくない場合は、論争の的となっているモジュールsinonをこの問題に使用できます。

var sinon = require('sinon');

describe('my tests', function () {
  var runs = [
    {it: 'options1', options: {...}},
    {it: 'options2', options: {...}},
  ];

  // use a stub to return the proper configuration in `beforeEach`
  // otherwise `before` is called all times before all `it` calls
  var stub = sinon.stub();
  runs.forEach(function (run, idx) {
    stub.onCall(idx).returns(run);
  });

  beforeEach(function () {
    var run = stub();
    // do something with the particular `run.options`
  });

  runs.forEach(function (run, idx) {
    it('does sth with ' + run.it, function () {
      sinon.assert.callCount(stub, idx + 1);
      ...
    });
  });
});

シノンは汚れたように感じますが、効果的です。 lecheなどのいくつかの援助モジュールはsinonに基づいていますが、おそらくさらなる複雑さを導入する必要はありません。

18
Wtower

Leche は、その機能をMochaに追加します。 お知らせ および docs を参照してください。

テストが失敗した場合は、どのデータセットが関係していたかを通知するため、単にテストをループするよりも優れています。

更新:

私はLecheのセットアップが気に入らず、Karmaで動作させることができなかったため、最終的にデータプロバイダーを separate file に抽出しました。

使用する場合は、 ソースを取得する のみを使用します。ドキュメントが利用可能です Leche readme で、ファイル自体に追加情報と使用法のヒントがあります。

3
hashchange

@Kaizoの答えに基づいて、PHPUnitでデータプロバイダーをエミュレートするためのテスト(リクエストからいくつかのパラメーターを取得するコントローラー)を考え出しました。 getParametersメソッドは、Expressからリクエストを受信し、req.paramいくつかのクエリパラメータを検査します。たとえば、GET /jobs/?page=1&per_page=5。これは、Express要求オブジェクトをスタブ化する方法も示しています。

うまくいけば、それも誰かを助けることができます。

// Core modules.
var assert = require('assert');

// Public modules.
var express = require('express');
var sinon = require('sinon');

// Local modules.
var GetJobs = require(__base + '/resources/jobs/controllers/GetJobs');

/**
 * Test suite for the `GetJobs` controller class.
 */
module.exports = {
    'GetJobs.getParameters': {
        'should parse request parameters for various cases': function () {
            // Need to stub the request `param` method; see http://expressjs.com/3x/api.html#req.param
            var stub = sinon.stub(express.request, 'param');
            var seeds = [
                // Expected, page, perPage
                [{limit: 10, skip: 0}],
                [{limit: 5, skip: 10}, 3, 5]
            ];
            var controller = new GetJobs();

            var test = function (expected, page, perPage) {
                stub.withArgs('page').returns(page);
                stub.withArgs('per_page').returns(perPage);

                assert.deepEqual(controller.getParameters(express.request), expected);
            };

            seeds.forEach(function (seed) {
                test.apply({}, seed);
            });
        }
    }
};

唯一の欠点は、Mochaが実際のアサーションをカウントしないことです(PHPUnitのように)、それは1つのテストとして表示されるだけです。

2
Andrew Eddie

mocha-testcheck がこのための最も簡単なツールであることがわかりました。あらゆる種類のデータを生成します。どの入力がテストの失敗を引き起こしているのかを絞り込みます。

0

mocha-testdata ライブラリを使用して、より簡単なソリューションを以下に説明します。

問題のサンプルソリューション。

import * as assert from assert;
import { givenAsync } from mocha-testdata;

suite('My async test suite', function () {
  given([0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 3]).test('sum to 6', function (a, b, c) {
    assert.strictEqual(a + b + c, 6);
  });
});

Node.jsアプリで最も一般的な非同期関数呼び出しをテストする必要がある場合は、代わりにgivenAsyncを使用します。

import * as assert from assert;
import { givenAsync } from mocha-testdata;

suite('My async test suite', function () {
  givenAsync([1, 2, 3], [3, 2, 1]).test('sum to 6', function (done, a, b, c) {
    doSomethingAsync(function () {
        assert.strictEqual(a + b + c, 6);
        done();
    });
  });
});