Skip to content

Commit

Permalink
Merge pull request #2063 from dustinfarris/pass-through-class
Browse files Browse the repository at this point in the history
add callThroughWithNew method
  • Loading branch information
fatso83 committed Jul 23, 2019
2 parents 67b8c0c + 85d3f2a commit 985a827
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 1 deletion.
21 changes: 21 additions & 0 deletions docs/release-source/release/stubs.md
Expand Up @@ -405,6 +405,27 @@ obj.sum.callThrough();

obj.sum(2, 2); // 'bar'
obj.sum(1, 2); // 3


#### `stub.callThroughWithNew();`

Causes the original method wrapped into the stub to be called using the `new` operator when none of the conditional stubs are matched.

```javascript
var obj = {};
obj.Sum = function MyConstructor(a, b) {
this.result = a + b;
};
sinon
.stub(obj, 'Sum')
.callThroughWithNew()
.withArgs(1, 2)
.returns({ result: 9000 });
(new obj.Sum(2, 2)).result; // 4
(new obj.Sum(1, 2)).result; // 9000
```


Expand Down
10 changes: 10 additions & 0 deletions lib/sinon/behavior.js
Expand Up @@ -135,6 +135,7 @@ var proto = {
);
},

/*eslint complexity: ["error", 20]*/
invoke: function invoke(context, args) {
/*
* callCallback (conditionally) calls ensureArgs
Expand Down Expand Up @@ -171,7 +172,16 @@ var proto = {
return (this.promiseLibrary || Promise).reject(this.returnValue);
} else if (this.callsThrough) {
var wrappedMethod = this.effectiveWrappedMethod();

return wrappedMethod.apply(context, args);
} else if (this.callsThroughWithNew) {
// Get the original method (assumed to be a constructor in this case)
var WrappedClass = this.effectiveWrappedMethod();
// Turn the arguments object into a normal array
var argsArray = slice(args);
// Call the constructor
var F = WrappedClass.bind.apply(WrappedClass, concat([null], argsArray));
return new F();
} else if (typeof this.returnValue !== "undefined") {
return this.returnValue;
} else if (typeof this.callArgAt === "number") {
Expand Down
4 changes: 4 additions & 0 deletions lib/sinon/default-behaviors.js
Expand Up @@ -221,6 +221,10 @@ var defaultBehaviors = {
fake.callsThrough = true;
},

callThroughWithNew: function callThroughWithNew(fake) {
fake.callsThroughWithNew = true;
},

get: function get(fake, getterFunction) {
var rootStub = fake.stub || fake;

Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -117,7 +117,7 @@
"esm": {
"cjs": {
"mutableNamespace": false,
"cache":true
"cache": true
},
"mode": "auto"
}
Expand Down
47 changes: 47 additions & 0 deletions test/stub-test.js
Expand Up @@ -3136,6 +3136,53 @@ describe("stub", function() {
});
});

describe(".callThroughWithNew", function() {
it("does not call original function with new when arguments match conditional stub", function() {
// We need a function here because we can't wrap properties that are already stubs
var callCount = 0;
var OriginalClass = function SomeClass() {
this.foo = "bar";
callCount++;
};

var myObj = {
MyClass: OriginalClass
};

var propStub = createStub(myObj, "MyClass");
propStub.withArgs("foo").returns({ foo: "bar" });
propStub.callThroughWithNew(propStub);

var result = new myObj.MyClass("foo");

assert.equals(result.foo, "bar");
assert.equals(callCount, 0);
});

it("calls original function with new with same arguments when call does not match conditional stub", function() {
// We need a function here because we can't wrap properties that are already stubs
var callArgs = [];

function OriginalClass() {
callArgs = arguments;
this.foo = "baz";
}

var myObj = {
MyClass: OriginalClass
};

var propStub = createStub(myObj, "MyClass");
propStub.withArgs("foo").returns({ foo: "bar" });
propStub.callThroughWithNew(propStub);

var result = new myObj.MyClass("not foo", ["definitely", "not", "foo"]);
assert.equals(callArgs[0], "not foo");
assert.equals(callArgs[1], ["definitely", "not", "foo"]);
assert.equals(result.foo, "baz");
});
});

describe(".get", function() {
it("allows users to stub getter functions for properties", function() {
var myObj = {
Expand Down

0 comments on commit 985a827

Please sign in to comment.