Skip to content

Commit 0d70b3a

Browse files
authored
Merge pull request #2 from henrysun918/spy-facade
Add `_spy` and expose typed spies for functions, getters, and setters
2 parents 081c34b + e57ed18 commit 0d70b3a

File tree

6 files changed

+1192
-165
lines changed

6 files changed

+1192
-165
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# 2.0.0
2+
* Support typing of spies through the `_spy` facade.
3+
* Allow spying and stubbing value accessors on any property

README.md

Lines changed: 112 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,44 @@ import { MockFactory} from 'jasmine-mock-factory';
1212

1313
it('should pass', () => {
1414
const mockInstance = MockFactory.create(SomeClass);
15-
mockInstance.doSomething.and.returnValue('awesome!');
1615

16+
/* arrange */
17+
mockInstance._spy.doSomething._func.and.returnValue('awesome!');
18+
19+
/* act */
1720
mockInstance.doSomething(); // returns 'awesome!'
1821

22+
/* assert */
1923
expect(mockInstance.doSomething).toHaveBeenCalled();
2024
}
2125
```
26+
## Quick reference
27+
```TypeScript
28+
/* create a mock from a class*/
29+
const mockInstance1 = MockFactory.create(RealClass);
30+
31+
/* create a mock from an instance*/
32+
const mockInstance2 = MockFactory.create(realInstance);
33+
34+
/* access a function spy */
35+
const spy1 = mockInstance._spy.functionName._func
36+
37+
/* access a getter spy */
38+
const spy2 = mockInstance._spy.propertyName._get
39+
40+
/* access a setter spy */
41+
const spy3 = mockInstance._spy.propertyName._set
42+
```
2243
2344
## Prerequisite
2445
2546
This util is built with and for [Jasmine](https://jasmine.github.io/) test framework. Basic understanding of Jasmine is assumed.
2647
27-
This util requires [ES6 Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) and only contains un-compiled `*.ts` files which must be compiled with a [TypeScript](https://www.typescriptlang.org/) compiler.
28-
48+
This util requires [ES6 Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) and only contains `*.ts` files that must be compiled with a [TypeScript](https://www.typescriptlang.org/) compiler.
2949
3050
3151
## Usage
52+
3253
### Install
3354
```Shell
3455
npm install jasmine-mock-factory --save-dev
@@ -62,33 +83,101 @@ const realInstance: RealInterface = new RealClass();
6283
const mockInstance = MockFactory.create(realInstance);
6384
```
6485
65-
### Using a mock
66-
`MockFactory.create()` will return an object with the same interface as the original object. You can invoke methods and get/set properties on this object.
86+
#### From window objects
87+
```TypeScript
88+
/* make sure you have included dom types from the TypeScript library */
89+
const mockWindow = MockFactory.create(window);
90+
const mockDocument = MockFactory.create(document);
91+
const mockLocation = MockFactory.create(location);
92+
```
6793
68-
* All the public and private methods will have a jasmine.Spy as the initial value. The Spy cannot be overwritten.
69-
* All the public and private properties will have `undefined` as the initial value. The value can be overwritten with anything.
70-
71-
### Examples
94+
### Using a mock
95+
* `MockFactory.create()` will return an object with the same interface as the real object. You can invoke functions and access properties on this object.
96+
* In addition, the mock object provides a `_spy` facade, where you can access and config spies on functions and properties.
7297
```TypeScript
73-
class RealClass {
74-
public doSomething(...arg: any[]) { ... }
75-
public someProperty = 'whatever';
76-
}
98+
const mockInstance = MockFactory.create(location);
99+
mockInstance.reload(); // use it as if it were the real window.location
100+
let temp = mockInstance.search; // use it as if it were the real window.search
101+
mockInstance.hash = 'myHash'; // use it as if it were the real window.hash
102+
mockInstance._spy.reload._func; // returns the spy behind location.reload
103+
mockInstance._spy.search._get; // returns the spy behind the getter for location.search
104+
mockInstance._spy.hash._set; // returns the spy behind the setter for location.hash
105+
```
77106
78-
const mockInstance = MockFactory.create(RealClass);
107+
#### Invoking functions
108+
* All functions will have a `jasmine.Spy` as the initial value. The spy cannot be overwritten and returns `undefined` by default.
109+
* To access protected and private functions, cast the mockInstance `as any` or use bracket notation.
110+
```TypeScript
111+
mockInstance.publicFunction(42); // the spy behind it is invoked with 42
79112

80-
// get, set property
81-
expect(mockInstance.someProperty).toBeUndefined();
82-
mockInstance.someProperty = 'hello';
83-
expect(mockInstance.someProperty).toBe('hello');
113+
(mockInstance as any).privateFunction(42);
114+
mockInstance['privateFunction'](42); // equivalent
115+
```
84116
85-
// use function spy
86-
expect(mockInstance.doSomething).not.toHaveBeenCalled();
117+
#### Spying/stubbing functions
118+
* You can change return values of functions or assert their calls by accessing them directly or through the `_spy` facade.
119+
* Access a function spy on `mockInstance._spy.functionName._func`.
120+
```TypeScript
121+
/* stubbing a public function */
122+
mockInstance._spy.publicFunction._func.and.returnValue(42);
123+
(mockInstance.publicFunction as jasmine.Spy).and.returnValue(42); // equivalent, but not recommented because it requires casting
124+
125+
/* stubbing a private function */
126+
mockInstance._spy.privateFunction._func.and.returnValue(42);
127+
((mockInstance as any).privateFunction as jasmine.Spy).and.returnValue(42); // equivalent, but not recommented because it requires casting twice
128+
```
87129
88-
(mockInstance.doSomething as jasmine.Spy).and.returnValue('awesome!');
130+
#### Accessing properties
131+
* All properties have `undefined` as the initial value. The value can be overwritten with anything.
132+
* You have read and write access to any property, even if they were readonly in the real object.
133+
* To read or write a protected or private property, cast the mockInstance `as any` or use bracket notation.
134+
* To write a readonly property, cast the mockInstance `as any`. The bracket notation won't work.
135+
* By default, modification to the properties will persist, even if a getter or setter exists in the real object.
136+
```TypeScript
137+
/* persist modification */
138+
mockInstance.publicProperty = 42;
139+
let temp = mockInstance.publicProperty; // temp = 42;
140+
141+
/* access readonly property */
142+
mockInstance.readonlyProperty = 42; // typescript compiler error
143+
(mockInstance as any).readonlyProperty = 42; // no problem
144+
mockInstance['readonlyProperty'] = 42; // typescript compiler error
145+
146+
/* access private property */
147+
(mockInstance as any).privateProperty = 'foo';
148+
mockInstance['privateProperty'] = 'foo'; // equivalent
149+
```
89150
90-
expect(mockInstance.doSomething(42)).toBe('awesome!');
91-
expect(mockInstance.doSomething).toHaveBeenCalledWith(42);
151+
#### Spying/stubbing getters and setters
152+
* All properties have spies on the getter and setter, even if the getter and setter don't exist in the real object.
153+
* Access a getter spy on `mockInstance._spy.propertyName._get`.
154+
* Access a setter spy on `mockInstance._spy.propertyName._set`.
155+
* NOTE: modification to the properties will not persist after getter or setter spies are customized
156+
* NOTE: `expect(mockInstance.someProperty).toBe(...)` will trigger `mockInstance._spy.someProperty._get`. Design the sequence of your assertions carefully to avoid shooting yourself in the foot.
157+
```TypeScript
158+
/* assert getter calls */
159+
let temp = mockInstance.publicProperty;
160+
expect(mockInstance._spy.publicProperty._get).toHaveBeenCalled();
161+
162+
/* assert setter calls on a public property */
163+
mockInstance.publicProperty = 42;
164+
expect(mockInstance._spy.publicProperty._set).toHaveBeenCalledWith(42);
165+
166+
/* customize setter */
167+
expect(mockInstance.publicProperty).toBe(42); // pass. setter hasn't been customized
168+
mockInstance._spy.publicProperty._set.and.callFake(() => { /* noop */});
169+
mockInstance.publicProperty = 100;
170+
expect(mockInstance.publicProperty).toBe(100); // fail. expect 42 to be 100. setter was customized
171+
172+
/* assert setter calls on a private property */
173+
mockInstance['privateProperty'] = 42;
174+
expect(mockInstance._spy.privateProperty._set).toHaveBeenCalledWith(42);
175+
176+
/* customize getter */
177+
expect(mockInstance['privateProperty']).toBe(42); // pass. getter hasn't been customized
178+
mockInstance._spy.privateProperty._get.and.returnValue(100);
179+
mockInstance['privateProperty'] = 42;
180+
expect(mockInstance['privateProperty']).toBe(42); // fail, expect 100 to be 42. getter was customzied
92181
```
93182
94183
## Develope

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jasmine-mock-factory",
3-
"version": "1.0.4",
3+
"version": "2.0.0",
44
"description": "A Jasmine helper for creating mocked classes",
55
"license": "MIT",
66
"author": "Chuanqi Sun",

0 commit comments

Comments
 (0)