Skip to content

Commit

Permalink
Merge pull request #1313 from takasmiley/issues/#1274
Browse files Browse the repository at this point in the history
Fix #1274 callCount property is incorrect
  • Loading branch information
fatso83 committed Jun 28, 2017
2 parents d8f005b + 1661a0f commit d22eda3
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 23 deletions.
49 changes: 27 additions & 22 deletions lib/sinon/spy.js
Expand Up @@ -41,18 +41,6 @@ function spy(object, property, types) {
return wrapMethod(object, property, descriptor);
}

function matchingFake(fakes, args, strict) {
if (!fakes) {
return undefined;
}

var matchingFakes = fakes.filter(function (fake) {
return fake.matches(args, strict);
});

return matchingFakes.pop();
}

function incrementCallCount() {
this.called = true;
this.callCount += 1;
Expand Down Expand Up @@ -174,25 +162,28 @@ var spyApi = {
},

invoke: function invoke(func, thisValue, args) {
var matching = matchingFake(this.fakes, args);
var matchings = this.matchingFakes(args);
var currentCallId = callId++;
var exception, returnValue;

incrementCallCount.call(this);
push.call(this.thisValues, thisValue);
push.call(this.args, args);
push.call(this.callIds, callId++);
push.call(this.callIds, currentCallId);
matchings.forEach(function (matching) {
incrementCallCount.call(matching);
push.call(matching.thisValues, thisValue);
push.call(matching.args, args);
push.call(matching.callIds, currentCallId);
});

// Make call properties available from within the spied function:
createCallProperties.call(this);

try {
this.invoking = true;

if (matching) {
returnValue = matching.invoke(func, thisValue, args);
} else {
returnValue = (this.func || func).apply(thisValue, args);
}
returnValue = (this.func || func).apply(thisValue, args);

var thisCall = this.getCall(this.callCount - 1);
if (thisCall.calledWithNew() && typeof returnValue !== "object") {
Expand All @@ -206,6 +197,11 @@ var spyApi = {

push.call(this.exceptions, exception);
push.call(this.returnValues, returnValue);
matchings.forEach(function (matching) {
push.call(matching.exceptions, exception);
push.call(matching.returnValues, returnValue);
});

var err = new ErrorConstructor();
// 1. Please do not get stack at this point. It may be so very slow, and not actually used
// 2. PhantomJS does not serialize the stack trace until the error has been thrown:
Expand All @@ -214,6 +210,9 @@ var spyApi = {
throw err;
} catch (e) {/* empty */}
push.call(this.errorsWithCallStack, err);
matchings.forEach(function (matching) {
push.call(matching.errorsWithCallStack, err);
});

// Make return value and exception available in the calls:
createCallProperties.call(this);
Expand Down Expand Up @@ -291,10 +290,10 @@ var spyApi = {
var args = slice.call(arguments);

if (this.fakes) {
var match = matchingFake(this.fakes, args, true);
var matching = this.matchingFakes(args, true).pop();

if (match) {
return match;
if (matching) {
return matching;
}
} else {
this.fakes = [];
Expand Down Expand Up @@ -328,6 +327,12 @@ var spyApi = {
return fake;
},

matchingFakes: function (args, strict) {
return (this.fakes || []).filter(function (fake) {
return fake.matches(args, strict);
});
},

matches: function (args, strict) {
var margs = this.matchingArguments;

Expand Down
10 changes: 9 additions & 1 deletion lib/sinon/stub.js
Expand Up @@ -11,6 +11,8 @@ var stubEntireObject = require("./stub-entire-object");
var stubDescriptor = require("./stub-descriptor");
var throwOnFalsyObject = require("./throw-on-falsy-object");

var slice = Array.prototype.slice;

function stub(object, property, descriptor) {
throwOnFalsyObject.apply(null, arguments);

Expand Down Expand Up @@ -85,7 +87,13 @@ var uuid = 0;
var proto = {
create: function create(stubLength) {
var functionStub = function () {
return getCurrentBehavior(functionStub).invoke(this, arguments);
var args = slice.call(arguments);
var matchings = functionStub.matchingFakes(args);

var fnStub = matchings.sort(function (a, b) {
return a.matchingArguments.length - b.matchingArguments.length;
}).pop() || functionStub;
return getCurrentBehavior(fnStub).invoke(this, arguments);
};

functionStub.id = "stub#" + uuid++;
Expand Down
78 changes: 78 additions & 0 deletions test/spy-test.js
Expand Up @@ -326,6 +326,45 @@ describe("spy", function () {
});
});

it("counts with combination of withArgs arguments and order of calling withArgs", function () {
var object = {
f1: function () {},
f2: function () {}
};

// f1: the order of withArgs(1), withArgs(1, 1)
var spy1 = createSpy(object, "f1");
assert.equals(spy1.callCount, 0);
assert.equals(spy1.withArgs(1).callCount, 0);
assert.equals(spy1.withArgs(1, 1).callCount, 0);

object.f1();
object.f1(1);
object.f1(1, 1);
object.f1(1, 2);

assert.equals(spy1.callCount, 4);
assert.equals(spy1.withArgs(1).callCount, 3);
assert.equals(spy1.withArgs(1, 1).callCount, 1);
assert.equals(spy1.withArgs(1, 2).callCount, 1);

// f2: the order of withArgs(1, 1), withArgs(1)
var spy2 = createSpy(object, "f2");
assert.equals(spy2.callCount, 0);
assert.equals(spy2.withArgs(1, 1).callCount, 0);
assert.equals(spy2.withArgs(1).callCount, 0);

object.f2();
object.f2(1);
object.f2(1, 1);
object.f2(1, 2);

assert.equals(spy2.callCount, 4);
assert.equals(spy2.withArgs(1).callCount, 3);
assert.equals(spy2.withArgs(1, 1).callCount, 1);
assert.equals(spy2.withArgs(1, 2).callCount, 1);
});

describe(".named", function () {
it("sets displayName", function () {
var spy = createSpy();
Expand Down Expand Up @@ -2468,5 +2507,44 @@ describe("spy", function () {
assert.equals(spy.length, 3);
});
});

describe(".matchingFakes", function () {
beforeEach(function () {
this.spy = createSpy();
});

it("is function", function () {
assert.isFunction(this.spy.matchingFakes);
});

it("returns an empty array by default", function () {
assert.equals(this.spy.matchingFakes([]), []);
assert.equals(this.spy.matchingFakes([1]), []);
assert.equals(this.spy.matchingFakes([1, 1]), []);
});

it("returns one matched fake", function () {
this.spy.withArgs(1);
this.spy.withArgs(2);

assert.equals(this.spy.matchingFakes([1]), [this.spy.withArgs(1)]);
assert.equals(this.spy.matchingFakes([2]), [this.spy.withArgs(2)]);
});

it("return some matched fake", function () {
this.spy.withArgs(1);
this.spy.withArgs(1, 1);
this.spy.withArgs(2);

assert.equals(this.spy.matchingFakes([]), []);
assert.equals(this.spy.matchingFakes([1]), [
this.spy.withArgs(1)
]);
assert.equals(this.spy.matchingFakes([1, 1]), [
this.spy.withArgs(1),
this.spy.withArgs(1, 1)
]);
});
});
});

13 changes: 13 additions & 0 deletions test/stub-test.js
Expand Up @@ -102,6 +102,19 @@ describe("stub", function () {
assert(callback.called);
});

it("should works with combination of withArgs arguments", function () {
var stub = createStub();
stub.returns(0);
stub.withArgs(1, 1).returns(2);
stub.withArgs(1).returns(1);

assert.equals(stub(), 0);
assert.equals(stub(1), 1);
assert.equals(stub(1, 1), 2);
assert.equals(stub(1, 1, 1), 2);
assert.equals(stub(2), 0);
});

describe(".returns", function () {
it("returns specified value", function () {
var stub = createStub.create();
Expand Down

0 comments on commit d22eda3

Please sign in to comment.