lil-mocky 1.5.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -70
- package/lil-mocky.sublime-workspace +205 -131
- package/package.json +1 -1
- package/src/lil-mocky.js +33 -45
- package/test/lilMockyTest.js +271 -177
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# lil-mocky
|
|
2
2
|
|
|
3
|
-
A lightweight JavaScript mocking library for testing. Create mock functions, objects,
|
|
3
|
+
A lightweight JavaScript mocking library for testing. Create mock functions, objects, and classes with call tracking and return value control. Includes spy functionality for tracking calls to existing methods.
|
|
4
4
|
|
|
5
5
|
## 🎯 Features
|
|
6
6
|
|
|
@@ -32,10 +32,10 @@ describe('User Service', () => {
|
|
|
32
32
|
callback({ name: 'Alice', age: 30 });
|
|
33
33
|
|
|
34
34
|
// Verify it was called correctly
|
|
35
|
-
expect(callback.calls
|
|
35
|
+
expect(callback.calls[0]).to.deep.equal({
|
|
36
36
|
user: { name: 'Alice', age: 30 }
|
|
37
37
|
});
|
|
38
|
-
expect(callback.calls
|
|
38
|
+
expect(callback.calls.length).to.equal(1);
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
```
|
|
@@ -69,10 +69,10 @@ const onComplete = mocky.fn().args('result').build();
|
|
|
69
69
|
processUsers([{ name: 'Alice' }], onComplete);
|
|
70
70
|
|
|
71
71
|
// Verify the callback was called correctly
|
|
72
|
-
expect(onComplete.calls
|
|
72
|
+
expect(onComplete.calls[0]).to.deep.equal({
|
|
73
73
|
result: [{ name: 'Alice', processed: true }]
|
|
74
74
|
});
|
|
75
|
-
expect(onComplete.calls
|
|
75
|
+
expect(onComplete.calls.length).to.equal(1);
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
**Common patterns:**
|
|
@@ -97,7 +97,7 @@ expect(result.data).to.deep.equal([1, 2, 3]);
|
|
|
97
97
|
// Arguments with defaults
|
|
98
98
|
const logger = mocky.fn().args('message', { level: 'info' }).build();
|
|
99
99
|
logger('Test message');
|
|
100
|
-
expect(logger.calls
|
|
100
|
+
expect(logger.calls[0]).to.deep.equal({
|
|
101
101
|
message: 'Test message',
|
|
102
102
|
level: 'info'
|
|
103
103
|
});
|
|
@@ -114,16 +114,18 @@ const mock = mocky.fn().build();
|
|
|
114
114
|
mock.ret('hello');
|
|
115
115
|
mock(); // Returns 'hello'
|
|
116
116
|
|
|
117
|
-
// Any value type
|
|
117
|
+
// Any value type — including falsy values
|
|
118
118
|
mock.ret(null);
|
|
119
|
-
mock.ret(
|
|
119
|
+
mock.ret(0);
|
|
120
|
+
mock.ret(false);
|
|
121
|
+
mock.ret('');
|
|
120
122
|
mock.ret([1, 2, 3]);
|
|
121
123
|
mock.ret({ data: 'value' });
|
|
122
124
|
|
|
123
|
-
// Different return per call (
|
|
124
|
-
mock.ret('first',
|
|
125
|
-
mock.ret('second',
|
|
126
|
-
mock.ret('default');
|
|
125
|
+
// Different return per call (0-indexed)
|
|
126
|
+
mock.ret('first', 0); // First call
|
|
127
|
+
mock.ret('second', 1); // Second call
|
|
128
|
+
mock.ret('default'); // All other calls (no index = default)
|
|
127
129
|
|
|
128
130
|
mock(); // 'first'
|
|
129
131
|
mock(); // 'second'
|
|
@@ -138,7 +140,7 @@ const mock = mocky.fn().ret('pre-configured').build();
|
|
|
138
140
|
mock(); // Returns 'pre-configured'
|
|
139
141
|
|
|
140
142
|
// Per-call values work too
|
|
141
|
-
const mock = mocky.fn().ret('default').ret('first',
|
|
143
|
+
const mock = mocky.fn().ret('default').ret('first', 0).build();
|
|
142
144
|
|
|
143
145
|
// Builder values are restored on reset
|
|
144
146
|
mock.ret('override');
|
|
@@ -171,19 +173,17 @@ mock(); // Throws 'Something went wrong'
|
|
|
171
173
|
// Non-Error values work too
|
|
172
174
|
mock.throw('string error');
|
|
173
175
|
|
|
174
|
-
// Per-call throwing
|
|
175
|
-
mock.throw(new Error('first call only'),
|
|
176
|
+
// Per-call throwing (0-indexed)
|
|
177
|
+
mock.throw(new Error('first call only'), 0);
|
|
176
178
|
mock.ret('default');
|
|
177
179
|
|
|
178
180
|
mock(); // Throws 'first call only'
|
|
179
181
|
mock(); // Returns 'default'
|
|
180
182
|
```
|
|
181
183
|
|
|
182
|
-
|
|
184
|
+
#### .calls - Verify Arguments
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
Check what arguments were passed to the mock:
|
|
186
|
+
Check what arguments were passed to the mock. `calls` is a getter that returns the array of all calls:
|
|
187
187
|
|
|
188
188
|
```javascript
|
|
189
189
|
const mock = mocky.fn().args('name', 'age').build();
|
|
@@ -192,17 +192,35 @@ mock('Alice', 30);
|
|
|
192
192
|
mock('Bob', 25);
|
|
193
193
|
|
|
194
194
|
// Get specific call
|
|
195
|
-
mock.calls
|
|
196
|
-
mock.calls
|
|
195
|
+
mock.calls[0]; // { name: 'Alice', age: 30 }
|
|
196
|
+
mock.calls[1]; // { name: 'Bob', age: 25 }
|
|
197
197
|
|
|
198
198
|
// Get all calls
|
|
199
|
-
mock.calls
|
|
200
|
-
mock.calls
|
|
199
|
+
mock.calls; // [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
|
|
200
|
+
mock.calls.length; // 2
|
|
201
201
|
|
|
202
202
|
// Without .args() config, returns raw arguments array
|
|
203
203
|
const rawMock = mocky.fn().build();
|
|
204
204
|
rawMock('a', 'b', 'c');
|
|
205
|
-
rawMock.calls
|
|
205
|
+
rawMock.calls[0]; // ['a', 'b', 'c']
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### .data - Custom State
|
|
209
|
+
|
|
210
|
+
`data` is a plain object on the mock for storing custom state. It persists across calls and is cleared on reset:
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
const mock = mocky.fn((ctx) => {
|
|
214
|
+
ctx.data.count = (ctx.data.count || 0) + 1;
|
|
215
|
+
return ctx.data.count;
|
|
216
|
+
}).build();
|
|
217
|
+
|
|
218
|
+
mock(); // 1
|
|
219
|
+
mock(); // 2
|
|
220
|
+
mock.data.count; // 2
|
|
221
|
+
|
|
222
|
+
mock.reset();
|
|
223
|
+
mock.data; // {} (cleared)
|
|
206
224
|
```
|
|
207
225
|
|
|
208
226
|
#### .reset() - Clear State
|
|
@@ -214,12 +232,12 @@ const mock = mocky.fn().build();
|
|
|
214
232
|
mock.ret('value');
|
|
215
233
|
mock('test');
|
|
216
234
|
|
|
217
|
-
mock.calls
|
|
235
|
+
mock.calls.length; // 1
|
|
218
236
|
|
|
219
237
|
mock.reset();
|
|
220
238
|
|
|
221
239
|
// Everything cleared
|
|
222
|
-
mock.calls
|
|
240
|
+
mock.calls.length; // 0
|
|
223
241
|
mock(); // Returns undefined (ret cleared)
|
|
224
242
|
```
|
|
225
243
|
|
|
@@ -244,10 +262,9 @@ const mock = mocky.fn((ctx) => {
|
|
|
244
262
|
ctx.args // Named arguments (from .args() config)
|
|
245
263
|
ctx.rawArgs // Raw arguments array (before .args() processing)
|
|
246
264
|
ctx.ret // Value set via .ret()
|
|
247
|
-
ctx.call // Call
|
|
265
|
+
ctx.call // Call index (0-indexed)
|
|
248
266
|
ctx.data // Custom state object (persists across calls, cleared on reset)
|
|
249
267
|
ctx.original // Original function (available in spies)
|
|
250
|
-
ctx.state // Internal state (prefer ctx.data instead)
|
|
251
268
|
|
|
252
269
|
return someValue;
|
|
253
270
|
}).args('param1', 'param2').build();
|
|
@@ -308,10 +325,10 @@ counter(); // 1
|
|
|
308
325
|
|
|
309
326
|
```javascript
|
|
310
327
|
const fetcher = mocky.fn((ctx) => {
|
|
311
|
-
if (ctx.call ===
|
|
328
|
+
if (ctx.call === 0)
|
|
312
329
|
return { status: 'loading' };
|
|
313
330
|
|
|
314
|
-
if (ctx.call ===
|
|
331
|
+
if (ctx.call === 1)
|
|
315
332
|
return { status: 'success', data: [1, 2, 3] };
|
|
316
333
|
|
|
317
334
|
return { status: 'cached' };
|
|
@@ -350,37 +367,13 @@ async function createUser(apiClient, userData) {
|
|
|
350
367
|
const result = await createUser(api, { name: 'Alice' });
|
|
351
368
|
|
|
352
369
|
// Verify the API was called correctly
|
|
353
|
-
expect(api.post.calls
|
|
370
|
+
expect(api.post.calls[0]).to.deep.equal({
|
|
354
371
|
url: '/users',
|
|
355
372
|
data: { name: 'Alice' }
|
|
356
373
|
});
|
|
357
374
|
expect(result).to.deep.equal({ id: 123 });
|
|
358
375
|
```
|
|
359
376
|
|
|
360
|
-
**Immutability after .build():**
|
|
361
|
-
|
|
362
|
-
Once you call `.build()`, the mock object's structure is immutable. You cannot add or reassign properties:
|
|
363
|
-
|
|
364
|
-
```javascript
|
|
365
|
-
// ❌ WRONG - can't modify after .build()
|
|
366
|
-
const mock = mocky.obj({
|
|
367
|
-
method: mocky.fn()
|
|
368
|
-
}).build();
|
|
369
|
-
|
|
370
|
-
mock.method = newImplementation; // TypeError: Cannot assign to read only property
|
|
371
|
-
mock.newMethod = mocky.fn().build(); // TypeError: Cannot add property
|
|
372
|
-
|
|
373
|
-
// ✅ RIGHT - define everything when building
|
|
374
|
-
const mock = mocky.obj({
|
|
375
|
-
method: mocky.fn((ctx) => {
|
|
376
|
-
// Your custom implementation here
|
|
377
|
-
return 'result';
|
|
378
|
-
})
|
|
379
|
-
}).build();
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
This immutability prevents bugs and ensures `.reset()` works correctly. To change behavior during tests, use `.ret()` or `.reset()` instead of reassigning properties.
|
|
383
|
-
|
|
384
377
|
**Nested mocks for complex structures:**
|
|
385
378
|
|
|
386
379
|
```javascript
|
|
@@ -429,7 +422,7 @@ api.newProp = 'added';
|
|
|
429
422
|
api.reset();
|
|
430
423
|
|
|
431
424
|
// After reset:
|
|
432
|
-
// - api.get.calls
|
|
425
|
+
// - api.get.calls is []
|
|
433
426
|
// - api.get return values cleared
|
|
434
427
|
// - api.baseURL is 'https://api.example.com' (restored)
|
|
435
428
|
// - api.timeout is 5000 (restored)
|
|
@@ -483,26 +476,26 @@ await userService.createUser({ name: 'Alice' });
|
|
|
483
476
|
authService.login('alice');
|
|
484
477
|
|
|
485
478
|
// Verify each instance was used correctly
|
|
486
|
-
expect(Logger.inst(0).constructor.calls
|
|
479
|
+
expect(Logger.inst(0).constructor.calls[0]).to.deep.equal({
|
|
487
480
|
moduleName: 'UserService'
|
|
488
481
|
});
|
|
489
|
-
expect(Logger.inst(0).info.calls
|
|
482
|
+
expect(Logger.inst(0).info.calls[0]).to.deep.equal({
|
|
490
483
|
message: 'Creating user'
|
|
491
484
|
});
|
|
492
485
|
|
|
493
|
-
expect(Logger.inst(1).constructor.calls
|
|
486
|
+
expect(Logger.inst(1).constructor.calls[0]).to.deep.equal({
|
|
494
487
|
moduleName: 'AuthService'
|
|
495
488
|
});
|
|
496
|
-
expect(Logger.inst(1).info.calls
|
|
489
|
+
expect(Logger.inst(1).info.calls[0]).to.deep.equal({
|
|
497
490
|
message: 'User logging in'
|
|
498
491
|
});
|
|
499
492
|
|
|
500
|
-
expect(Logger.
|
|
493
|
+
expect(Logger.instCount).to.equal(2);
|
|
501
494
|
```
|
|
502
495
|
|
|
503
496
|
**Accessing mock helpers on instances:**
|
|
504
497
|
|
|
505
|
-
You can access `.calls
|
|
498
|
+
You can access `.calls`, `.ret()`, and `.reset()` directly on instance methods:
|
|
506
499
|
|
|
507
500
|
```javascript
|
|
508
501
|
const Logger = mocky.cls({
|
|
@@ -513,7 +506,7 @@ const logger = new Logger();
|
|
|
513
506
|
logger.info('test message');
|
|
514
507
|
|
|
515
508
|
// Access calls directly on the instance
|
|
516
|
-
expect(logger.info.calls
|
|
509
|
+
expect(logger.info.calls[0]).to.deep.equal({ message: 'test message' });
|
|
517
510
|
|
|
518
511
|
// Configure returns on the instance
|
|
519
512
|
logger.info.ret('logged');
|
|
@@ -521,7 +514,7 @@ expect(logger.info('another')).to.equal('logged');
|
|
|
521
514
|
|
|
522
515
|
// Reset via instance
|
|
523
516
|
logger.info.reset();
|
|
524
|
-
expect(logger.info.calls
|
|
517
|
+
expect(logger.info.calls.length).to.equal(0);
|
|
525
518
|
```
|
|
526
519
|
|
|
527
520
|
**Pre-configuring instances:**
|
|
@@ -549,6 +542,29 @@ const users = await db1.query('SELECT * FROM users'); // [{ id: 1, ... }]
|
|
|
549
542
|
const empty = await db2.query('SELECT * FROM users'); // []
|
|
550
543
|
```
|
|
551
544
|
|
|
545
|
+
#### Instance Access
|
|
546
|
+
|
|
547
|
+
Use `Mock.inst(n)` to access or pre-configure a specific instance (lazy-creates the description if needed):
|
|
548
|
+
|
|
549
|
+
```javascript
|
|
550
|
+
Mock.inst(0); // First instance
|
|
551
|
+
Mock.inst(1); // Second instance
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Use `instCount` (or `instanceCount`) to get the number of instantiated instances:
|
|
555
|
+
|
|
556
|
+
```javascript
|
|
557
|
+
const Mock = mocky.cls({
|
|
558
|
+
method: mocky.fn()
|
|
559
|
+
}).build();
|
|
560
|
+
|
|
561
|
+
const inst1 = new Mock();
|
|
562
|
+
const inst2 = new Mock();
|
|
563
|
+
|
|
564
|
+
Mock.instCount; // 2
|
|
565
|
+
Mock.instanceCount; // 2 (synonym)
|
|
566
|
+
```
|
|
567
|
+
|
|
552
568
|
#### Using ctx.self for Instance State
|
|
553
569
|
|
|
554
570
|
`ctx.self` is the "mockable surface" — the object that holds the mock's state. For class mocks, this is the description object (what `Mock.inst(n)` returns), not the raw class instance. Properties you want to access via `ctx.self` should be defined as members:
|
|
@@ -622,13 +638,13 @@ Mock.inst(0).method.ret('value');
|
|
|
622
638
|
const instance1 = new Mock();
|
|
623
639
|
const instance2 = new Mock();
|
|
624
640
|
|
|
625
|
-
Mock.
|
|
641
|
+
Mock.instCount; // 2
|
|
626
642
|
|
|
627
643
|
Mock.reset();
|
|
628
644
|
|
|
629
645
|
// After reset:
|
|
630
646
|
// - All instance configurations cleared
|
|
631
|
-
// - Mock.
|
|
647
|
+
// - Mock.instCount is 0
|
|
632
648
|
// - Next instantiation starts fresh at instance 0
|
|
633
649
|
```
|
|
634
650
|
|
|
@@ -659,7 +675,7 @@ function notifyUser(user, message) {
|
|
|
659
675
|
const result = notifyUser({ email: 'alice@example.com' }, 'Hello!');
|
|
660
676
|
|
|
661
677
|
// Verify the method was called correctly
|
|
662
|
-
expect(spy.calls
|
|
678
|
+
expect(spy.calls[0]).to.deep.equal([
|
|
663
679
|
'alice@example.com',
|
|
664
680
|
'Notification',
|
|
665
681
|
'Hello!'
|
|
@@ -699,7 +715,7 @@ const spy = mocky.spy(obj, 'add', (ctx) => {
|
|
|
699
715
|
}, ['x', 'y']);
|
|
700
716
|
|
|
701
717
|
obj.add(3, 4); // 70 — (3 + 4) * 10
|
|
702
|
-
spy.calls
|
|
718
|
+
spy.calls[0]; // { x: 3, y: 4 }
|
|
703
719
|
|
|
704
720
|
spy.restore();
|
|
705
721
|
```
|
|
@@ -731,7 +747,7 @@ client1.request('/api/users', { method: 'GET' });
|
|
|
731
747
|
client2.request('/api/posts', { method: 'GET' });
|
|
732
748
|
|
|
733
749
|
// Both instances' calls are tracked
|
|
734
|
-
expect(spy.calls
|
|
750
|
+
expect(spy.calls.length).to.equal(2);
|
|
735
751
|
spy.restore();
|
|
736
752
|
```
|
|
737
753
|
|
|
@@ -745,7 +761,7 @@ Migration guide for Jest users:
|
|
|
745
761
|
|------|-----------|
|
|
746
762
|
| `jest.fn()` | `mocky.fn().build()` |
|
|
747
763
|
| `mock.mockReturnValue(val)` | `mock.ret(val)` |
|
|
748
|
-
| `mock.mock.calls[0][0]` | `mock.calls
|
|
764
|
+
| `mock.mock.calls[0][0]` | `mock.calls[0]` |
|
|
749
765
|
| `jest.spyOn(obj, 'method')` | `mocky.spy(obj, 'method')` |
|
|
750
766
|
| `spy.mockRestore()` | `spy.restore()` |
|
|
751
767
|
|