Как проверить несколько вызовов одной и той же функции с разными параметрами?
Предположим, у меня есть такая функция:
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();
});
Используя эту систему, sinon терпит неудачу с сообщением "неожиданный вызов" в каждом тесте.
Я решил, что он соединяет тесты дерева в один:
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();
});
Но я хочу иметь три теста, а не один с тремя утверждениями/ожиданиями.
Как это можно достичь?
Ответы
Ответ 1
Как всегда, , когда есть что-то странное в отношении теста, проблема находится в тестируемом коде.
В этом случае мы страдаем от сцепления. В настоящее время функция выполняет две функции:
- Определите используемые данные.
- Вызовите метод с данными.
Чтобы решить эту проблему, мы должны разделить обязанности в двух функциях/объектах/классах, а затем тестировать их отдельно. Например, одна возможность может быть:
-
Первая функция (которая генерирует и возвращает данные) будет проверена, проверяя, что возвращаемые данные соответствуют нашим ожиданиям.
-
Вторая функция (наша оригинальная) проведет проверку проверки того, что она вызывает генератор данных, а затем проверит проверку правильности отправки данных на ожидаемую функцию, а третья - проверяет, что она вызывает функции столько раз, сколько необходимо для данных.
Код будет примерно таким:
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)
});
Обратите внимание, что третий тест проверяет количество раз, когда метод вызывается. Второй тест уже проверил, что данные переданы правильно, а четвертый проверяет сами данные.
Ответ 2
Это раздутая версия, но это решение может работать. Не уверен, что вам все еще нужно, но я просто добавляю его здесь. 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();
});