diff --git a/README.md b/README.md index 5a441ef..2e59053 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -Strongly-typed wrappers for the "spy" portion of Sinon. See example usage in `src/examples/examples.ts` (which can be run in a browser by opening `dist/examples/examples.html`). +Strongly-typed wrappers for the "spy" and "stub" portion of Sinon. See example usage in `src/examples/examples.ts` (which can be run in a browser by opening `dist/examples/examples.html`), `src/examples/class-spy` and `src/examples/stubs`. Totally unsupported. Please don't contact me about this! diff --git a/dist/TypeSinon.d.ts b/dist/TypeSinon.d.ts index 806ba7a..a8b9b3e 100644 --- a/dist/TypeSinon.d.ts +++ b/dist/TypeSinon.d.ts @@ -5,4 +5,5 @@ declare module TypeSinon { interface Spy extends SinonSpy { fn: TFunc; } + function stub(obj?: any, method?: TFunc, fn?: TFunc): SinonStub; } diff --git a/dist/TypeSinon.js b/dist/TypeSinon.js index 1387384..3ceecec 100644 --- a/dist/TypeSinon.js +++ b/dist/TypeSinon.js @@ -17,6 +17,20 @@ var TypeSinon; return (obj[key] = spyOnFunc(method)); } } - throw new Error("Method found on object.\nMethod: " + method + "\nObject: " + obj); + throw new Error("Method not found on object.\nMethod: " + method + "\nObject: " + obj); } + function stub(obj, method, fn) { + if (obj && method) { + for (var key in obj) { + if (obj[key] === method) { + return sinon.stub(obj, key, fn); + } + } + throw new Error("Method not found on object.\nMethod: " + method + "\nObject: " + obj); + } + else { + return sinon.stub(obj); + } + } + TypeSinon.stub = stub; })(TypeSinon || (TypeSinon = {})); diff --git a/dist/examples/class-spy/App.d.ts b/dist/examples/App.d.ts similarity index 100% rename from dist/examples/class-spy/App.d.ts rename to dist/examples/App.d.ts diff --git a/dist/examples/class-spy/App.js b/dist/examples/App.js similarity index 100% rename from dist/examples/class-spy/App.js rename to dist/examples/App.js diff --git a/dist/examples/Staff.js b/dist/examples/Staff.js index 76eb2b2..26a2b8a 100644 --- a/dist/examples/Staff.js +++ b/dist/examples/Staff.js @@ -15,6 +15,7 @@ var Staff; myAnimals.forEach(function (animal) { _this._zooService.feedAnimalByName(animal.name); }); + console.log("Employee duties performed."); }; return Employee; })(); diff --git a/dist/examples/class-spy/tests.d.ts b/dist/examples/class-spy/tests.d.ts index 83f666f..91c3c9a 100644 --- a/dist/examples/class-spy/tests.d.ts +++ b/dist/examples/class-spy/tests.d.ts @@ -1,4 +1,4 @@ /// -/// +/// declare var engine: App.Engine, engineStartSpy: TypeSinon.Spy<() => void>; declare var engine: App.Engine, engineStopSpy: TypeSinon.Spy<() => void>; diff --git a/dist/examples/class-spy/tests.html b/dist/examples/class-spy/tests.html index b8925bd..30eb362 100644 --- a/dist/examples/class-spy/tests.html +++ b/dist/examples/class-spy/tests.html @@ -5,7 +5,7 @@ - + \ No newline at end of file diff --git a/dist/examples/class-spy/tests.js b/dist/examples/class-spy/tests.js index b5d53f0..6fd2e1e 100644 --- a/dist/examples/class-spy/tests.js +++ b/dist/examples/class-spy/tests.js @@ -1,5 +1,5 @@ /// -/// +/// //test 1: should change state from stopped to started on power button press // Arrange var engine = new App.Engine("stopped"), engineStartSpy = TypeSinon.spy(engine, engine.start); diff --git a/dist/examples/stubs/example-stubs.d.ts b/dist/examples/stubs/example-stubs.d.ts new file mode 100644 index 0000000..8439eb3 --- /dev/null +++ b/dist/examples/stubs/example-stubs.d.ts @@ -0,0 +1,11 @@ +/// +/// +declare var stub1: SinonStub; +declare var engine2: App.Engine; +declare var stub2: SinonStub; +declare var engine3: App.Engine; +declare var stub3: SinonStub; +declare var engine4: App.Engine; +declare var fakeStartFunc: () => void; +declare var stub4: SinonStub; +declare var wrongFakePerformDutiesFunc: (p: number) => boolean; diff --git a/dist/examples/stubs/example-stubs.html b/dist/examples/stubs/example-stubs.html new file mode 100644 index 0000000..c705b91 --- /dev/null +++ b/dist/examples/stubs/example-stubs.html @@ -0,0 +1,11 @@ + + + + See browser console for output. + + + + + + + diff --git a/dist/examples/stubs/example-stubs.js b/dist/examples/stubs/example-stubs.js new file mode 100644 index 0000000..e225cba --- /dev/null +++ b/dist/examples/stubs/example-stubs.js @@ -0,0 +1,40 @@ +/// +/// +// simple stub +var stub1 = TypeSinon.stub().throws(); +try { + stub1(); +} +catch (e) { + console.log("stub1 throws"); +} +// stub existing object +var engine2 = new App.Engine(); +console.info("engine.getState method before creating stub returns", engine2.getState()); +var stub2 = TypeSinon.stub(engine2); +console.info("engine.getState method after creating stub returns", engine2.getState()); +// stub method on existing object +var engine3 = new App.Engine(); +var stub3 = TypeSinon.stub(engine3, engine3.start).throws(); +try { + engine3.start(); +} +catch (e) { + console.log("stub3 throws"); + console.info("engine.getState method should still return state properly:", engine3.getState()); +} +// stub method on existing object with given (as param) function +var engine4 = new App.Engine(); +var fakeStartFunc = function () { + console.log("fake startEngine"); +}; +var stub4 = TypeSinon.stub(engine4, engine4.start, fakeStartFunc); +engine4.start(); +var wrongFakePerformDutiesFunc = function (p) { + console.log("wrong fake performDuties"); + return true; +}; +// below will not work, TS compiler will return following error: +// Type argument candidate '() => void' is not a valid type argument because it is not a supertype of candidate '(p: number) => boolean'. +// TypeSinon.stub(engine4, engine4.start, wrongFakePerformDutiesFunc); +// engine4.start(); diff --git a/src/TypeSinon.ts b/src/TypeSinon.ts index 2c1d10f..a43f565 100644 --- a/src/TypeSinon.ts +++ b/src/TypeSinon.ts @@ -24,10 +24,25 @@ module TypeSinon { } } - throw new Error("Method found on object.\nMethod: " + method + "\nObject: " + obj); + throw new Error("Method not found on object.\nMethod: " + method + "\nObject: " + obj); } export interface Spy extends SinonSpy { fn: TFunc; } + + export function stub(obj?: any, method?: TFunc, fn?: TFunc): SinonStub { + if (obj && method) { + // Find the function and stub it + for (var key in obj) { + if (obj[key] === method) { + return sinon.stub(obj, key, fn); + } + } + + throw new Error("Method not found on object.\nMethod: " + method + "\nObject: " + obj); + } else { + return sinon.stub(obj); + } + } } \ No newline at end of file diff --git a/src/examples/class-spy/App.ts b/src/examples/App.ts similarity index 99% rename from src/examples/class-spy/App.ts rename to src/examples/App.ts index c2d0098..2063de2 100644 --- a/src/examples/class-spy/App.ts +++ b/src/examples/App.ts @@ -1,4 +1,3 @@ - module App { /** * A class that can be tested. @@ -32,4 +31,4 @@ module App { return this._state; } } -} \ No newline at end of file +} diff --git a/src/examples/Staff.ts b/src/examples/Staff.ts index 070f817..455e0e6 100644 --- a/src/examples/Staff.ts +++ b/src/examples/Staff.ts @@ -18,6 +18,7 @@ module Staff { myAnimals.forEach((animal) => { this._zooService.feedAnimalByName(animal.name); }); + console.log("Employee duties performed."); } } } \ No newline at end of file diff --git a/src/examples/class-spy/tests.html b/src/examples/class-spy/tests.html index b8925bd..30eb362 100644 --- a/src/examples/class-spy/tests.html +++ b/src/examples/class-spy/tests.html @@ -5,7 +5,7 @@ - + \ No newline at end of file diff --git a/src/examples/class-spy/tests.ts b/src/examples/class-spy/tests.ts index fc94c2e..0ecc1da 100644 --- a/src/examples/class-spy/tests.ts +++ b/src/examples/class-spy/tests.ts @@ -1,5 +1,5 @@ /// -/// +/// //test 1: should change state from stopped to started on power button press diff --git a/src/examples/examples.ts b/src/examples/examples.ts index 9635ac9..cddd9b2 100644 --- a/src/examples/examples.ts +++ b/src/examples/examples.ts @@ -38,4 +38,4 @@ harold.performDuties(); console.log("Animals fed: " + mockFeedAnimals.args); // ... and 'called' just gives a bool to say whether the func was invoked at all. -console.log("Will the zoo self-destruct? " + mockBeginSelfDestruct.called); +console.log("Will the zoo self-destruct? " + mockBeginSelfDestruct.called); \ No newline at end of file diff --git a/src/examples/stubs/example-stubs.html b/src/examples/stubs/example-stubs.html new file mode 100644 index 0000000..c705b91 --- /dev/null +++ b/src/examples/stubs/example-stubs.html @@ -0,0 +1,11 @@ + + + + See browser console for output. + + + + + + + diff --git a/src/examples/stubs/example-stubs.ts b/src/examples/stubs/example-stubs.ts new file mode 100644 index 0000000..160f8ca --- /dev/null +++ b/src/examples/stubs/example-stubs.ts @@ -0,0 +1,50 @@ +/// +/// + +// simple stub + +var stub1 = TypeSinon.stub().throws(); +try { + stub1(); +} catch(e) { + console.log("stub1 throws"); +} + +// stub existing object + +var engine2 = new App.Engine(); +console.info("engine.getState method before creating stub returns", engine2.getState()); +var stub2 = TypeSinon.stub(engine2); +console.info("engine.getState method after creating stub returns", engine2.getState()); + +// stub method on existing object + +var engine3 = new App.Engine(); +var stub3 = TypeSinon.stub(engine3, engine3.start).throws(); + +try { + engine3.start(); +} catch(e) { + console.log("stub3 throws"); + console.info("engine.getState method should still return state properly:", engine3.getState()); +} + +// stub method on existing object with given (as param) function + +var engine4 = new App.Engine(); +var fakeStartFunc = function() { + console.log("fake startEngine"); +}; +var stub4 = TypeSinon.stub(engine4, engine4.start, fakeStartFunc); +engine4.start(); + +var wrongFakePerformDutiesFunc = function (p: number): boolean { + console.log("wrong fake performDuties"); + return true; +} + +// below will not work, TS compiler will return following error: +// Type argument candidate '() => void' is not a valid type argument because it is not a supertype of candidate '(p: number) => boolean'. + +// TypeSinon.stub(engine4, engine4.start, wrongFakePerformDutiesFunc); +// engine4.start(); \ No newline at end of file