Заглушка Sinon.js и тестовый вызов внешней функции с объектом в качестве параметра, позже измененного ссылкой

Я хочу проверить вызов, который моя функция делает для другой функции, особенно аргумента, который является объектом.

Проблема в том, что sinon.js, по-видимому, хранит ссылку на параметр объекта, указанный в его массиве аргументов, что, естественно, ожидается. Однако это создает проблему, когда параметр позже изменяется с помощью ref, который изменяет кажущееся значение этих параметров во время вызова функции.

Каков наилучший способ модульного тестирования в такой ситуации?

Вот надуманный пример:

http://jsfiddle.net/xtfQu/

var view = Backbone.View.extend({
    initialize: function () {
        _.bindAll(this);    
    },

    dostuff: function () {
        var coords = { x: 0, y: 0 };
        for (var i = 0; i < 10; i++) {
            this.otherstuff(coords);

            coords.x += 10;
        }
    },

    otherstuff: function (coord) {
        // Stubbed
    }
});

test("a test", function() {
    // Arrange
    var blah = new view();

    sinon.stub(blah, 'otherstuff');

    // Act
    blah.dostuff();

    // Assert
    var expectedFirstCallCoords = { x: 0, y: 0 };

    // All assertions on the value of x for a particular call would seem to be impossible.
    // blah.otherstuff.firstCall.args[0].x --> 100
    deepEqual(blah.otherstuff.firstCall.args[0], expectedFirstCallCoords, 'parameter changed by ref, untestable?');
});​

Я могу придумать различные хаки вокруг этой «проблемы». Есть ли более чистый подход, который не включает клонирование объекта или изменение моего производственного кода просто ради того, чтобы заставить sinon работать?


person Adam Terlson    schedule 15.10.2012    source источник
comment
Это похоже на недостаток Синон. Вероятно, следует клонировать аргументы, по крайней мере, если они являются нативными объектами. Вы спрашивали в их списке рассылки? У Кристиана может быть обходной путь или он сможет быстро его исправить. Или он вполне мог бы предложить альтернативную технику. Я не вижу ничего, что не было бы достаточно навязчивым.   -  person Scott Sauyet    schedule 15.10.2012
comment
@ScottSauyet Спасибо за подтверждение того, что я не сумасшедший. :) Единственная проблема с клонированием этих объектов sinon заключается в том, что тесты со strictEquals для них не пройдут. Если бы я вместо этого утверждал: strictEquals(blah.otherstuff.firstCall.args[0], expectedFirstCallCoords), это не помогло бы против клона. В моем надуманном примере это не проблема, но что, если expectedFirstCallCoords было введено в функцию/класс? Это было бы очень нежелательно. В лучшем случае, который я вижу в любом случае, это то, что ему нужно будет хранить как исходные ссылки на объекты, так и клонированные объекты, и это сбивает с толку.   -  person Adam Terlson    schedule 15.10.2012
comment
Да, я не рассматривал strictEquals. (Вы используете это в тестировании? Я никогда не использую.) Я действительно не вижу отличного решения для этого. Но что касается подтверждения того, что вы не сумасшедший, я оставлю это между вами и вашим психиатром! :-) Удачи!   -  person Scott Sauyet    schedule 16.10.2012
comment
@ScottSauyet Лично я считаю использование equal плохой практикой. Учтите, когда вы проверяете свои значения, которые проходит equal(true, 1);.   -  person Adam Terlson    schedule 16.10.2012
comment
В основном я использую JSTestDriver, а assertEquals фреймворка утверждений — это глубокое сравнение на равенство, но не эталонное сравнение. Я не использую assertSame или не часто, что является эталонным сравнением. Но у меня также есть, например, assertTrue, assertNotNull, чтобы поймать более конкретные типы. С другой стороны, я часто хочу, чтобы любое истинное значение прошло мои логические тесты, поэтому у нас могут быть очень разные стандарты! :-)   -  person Scott Sauyet    schedule 16.10.2012
comment
Пока вы используете equal со знанием того, что это действительно правда, это, конечно, именно то, для чего оно нужно! Я просто люблю откровенность :). Кроме того, то, что я тестировал в своем equal(true, 1), было не логическим, а вместо этого 1, поэтому ожидание целого числа и прохождение теста, когда я фактически получил логическое значение (или строку), кажется мне неправильным. Для большинства логических случаев, когда меня интересует только правдивость, я использую ok(). Чистый результат, теперь, когда я думаю об этом, я никогда не использую equal. :) Но это всего лишь, признаюсь, личное предпочтение. Хорошая дискуссия!   -  person Adam Terlson    schedule 16.10.2012


Ответы (1)


Спасибо Мантони за связанное с этим обсуждение Github:

var param = { property: 'value' };
var engage = sinon.stub();
var engageWithValue = engage.withArgs(sinon.match({ property : 'value' }));

engage(param);

param.property = 'new value';

sinon.assert.calledOnce(engageWithValue); // passes
person Adam Terlson    schedule 16.10.2012