web-dev-qa-db-ja.com

異なるパラメータを使用して同じ関数への複数の呼び出しをテストするにはどうすればよいですか?

私がこのような関数を持っているとしましょう:

function foo () {
    obj.method(1);
    obj.method(2);
    obj.method(3);
}

それをテストするために、私は3つのテストを実行したいと思います(Mocha TDDとSinonを使用):

test('medthod is called with 1', function () {
    var expectation = sinon.mock(obj).expects('method').once().withExactArgs(1);
    foo();
    expectation.verify();
});

test('medthod is called with 2', function () {
    var expectation = sinon.mock(obj).expects('method').once().withExactArgs(2);
    foo();
    expectation.verify();
});

test('medthod is called with 3', function () {
    var expectation = sinon.mock(obj).expects('method').once().withExactArgs(3);
    foo();
    expectation.verify();
});

このシステムの使用は、各テストで「予期しない呼び出し」メッセージが表示されて失敗します。

ツリーテストを1つに結合することで解決しました。

test('medthod is called with 1, 2 and 3', function () {
    var mock = sinon.mock(obj);
    mock.expects('method').once().withExactArgs(1);
    mock.expects('method').once().withExactArgs(2);
    mock.expects('method').once().withExactArgs(3);
    foo();
    mock.verify();
});

しかし、3つのアサーション/期待を持つものではなく、3つのテストが必要です。

これはどのように達成できますか?

17
Kaizo

いつものようにテストについて何か奇妙なことがあるとき、問題はテストされているコードにあります

この場合、カップリングに悩まされます。現在、この機能には2つの責任があります。

  • 使用するデータを決定します。
  • データを使用してメソッドを呼び出します。

これを解決するには、責任を2つの関数/オブジェクト/クラスに分割し、それぞれを個別にテストする必要があります。たとえば、次のような可能性があります。

  • 最初の関数(データを生成して返す関数)は、返されたデータが期待と一致することを確認するためにテストされます。

  • 2番目の関数(元の関数)には、データジェネレーターを呼び出すことを確認するテストがあり、次に、期待される関数にデータを正しく送信することを確認するテストがあり、3番目の関数は必要な回数だけ関数を呼び出すことを確認します。データ。

コードは次のようになります。

function foo() {
    dataGenerator.generate().forEach(function (item) {
        obj.method(item);
    })
}

dataGenerator.generate = function () {
    return [1,2,3];
};

そしてテスト:

test('generateData is called', function () {
    var expectation = sinon.mock(dataGenerator).expects('generate').once();
    foo();
    expectation.verify();
});

test('method is called with the correct args', function () {
    var expectedArgs = 1;
    sinon.stub(dataGenerator, "generate").returns([expectedArgs]);
    var expectation = sinon.mock(obj).expects('method').once().withExactArgs(expectedArgs);
    foo();
    expectation.verify();
});

test('method is called as many times as the amount of data', function () {
    sinon.stub(dataGenerator, "generate").returns([1,2]);
    var expectation = sinon.mock(obj).expects('method').twice();
    foo();
    expectation.verify();
});

test('dataGenerator.generate returns [1,2,3]', function () {
    var expected = [1,2,3];
    var result = dataGenerator.generate();
    assert.equal(result, expected)
});

3番目のテストでは、メソッドが呼び出された回数のみがチェックされることに注意してください。 2番目のテストでは、データが正しく渡されていることをすでに確認しており、4番目のテストではデータ自体をテストしています。

4
Kaizo

これは肥大化したバージョンですが、このソリューションは機能する可能性があります。それでも必要かどうかはわかりませんが、ここに追加します。 http://jsfiddle.net/reyuto/jhkL7j34/

    obj = {
        method: function (param) {}
    };

    function foo() {
        obj.method(1);
        obj.method(2);
        obj.method(3);
    }

    mock = sinon.mock(obj);

    QUnit.test('method is called with 1', function () {
        var expectation1 = mock.expects('method').once().withExactArgs(1);
        var expectation2 = mock.expects('method').atLeast(2);
        foo();
        expectation1.verify();
        expectation2.verify();
    });

    QUnit.test('method is called with 2', function () {
        var expectation1 = mock.expects('method').atLeast(1);
        var expectation2 = mock.expects('method').once().withExactArgs(2);
        var expectation3 = mock.expects('method').atLeast(1);
        foo();
        expectation1.verify();
        expectation2.verify();
        expectation3.verify();
    });

    QUnit.test('method is called with 3', function () {
        var expectation1 = mock.expects('method').once().withExactArgs(3);
        var expectation2 = mock.expects('method').atLeast(2);
        foo();
        expectation1.verify();
        expectation2.verify();
    });
0
reyuto