1'use strict'; 2const common = require('../common'); 3const assert = require('node:assert'); 4const { mock, test } = require('node:test'); 5test('spies on a function', (t) => { 6 const sum = t.mock.fn((arg1, arg2) => { 7 return arg1 + arg2; 8 }); 9 10 assert.strictEqual(sum.mock.calls.length, 0); 11 assert.strictEqual(sum(3, 4), 7); 12 assert.strictEqual(sum.call(1000, 9, 1), 10); 13 assert.strictEqual(sum.mock.calls.length, 2); 14 15 let call = sum.mock.calls[0]; 16 assert.deepStrictEqual(call.arguments, [3, 4]); 17 assert.strictEqual(call.error, undefined); 18 assert.strictEqual(call.result, 7); 19 assert.strictEqual(call.target, undefined); 20 assert.strictEqual(call.this, undefined); 21 22 call = sum.mock.calls[1]; 23 assert.deepStrictEqual(call.arguments, [9, 1]); 24 assert.strictEqual(call.error, undefined); 25 assert.strictEqual(call.result, 10); 26 assert.strictEqual(call.target, undefined); 27 assert.strictEqual(call.this, 1000); 28}); 29 30test('spies on a bound function', (t) => { 31 const bound = function(arg1, arg2) { 32 return this + arg1 + arg2; 33 }.bind(50); 34 const sum = t.mock.fn(bound); 35 36 assert.strictEqual(sum.mock.calls.length, 0); 37 assert.strictEqual(sum(3, 4), 57); 38 assert.strictEqual(sum(9, 1), 60); 39 assert.strictEqual(sum.mock.calls.length, 2); 40 41 let call = sum.mock.calls[0]; 42 assert.deepStrictEqual(call.arguments, [3, 4]); 43 assert.strictEqual(call.result, 57); 44 assert.strictEqual(call.target, undefined); 45 assert.strictEqual(call.this, undefined); 46 47 call = sum.mock.calls[1]; 48 assert.deepStrictEqual(call.arguments, [9, 1]); 49 assert.strictEqual(call.result, 60); 50 assert.strictEqual(call.target, undefined); 51 assert.strictEqual(call.this, undefined); 52}); 53 54test('spies on a constructor', (t) => { 55 class ParentClazz { 56 constructor(c) { 57 this.c = c; 58 } 59 } 60 61 class Clazz extends ParentClazz { 62 #privateValue; 63 64 constructor(a, b) { 65 super(a + b); 66 this.a = a; 67 this.#privateValue = b; 68 } 69 70 getPrivateValue() { 71 return this.#privateValue; 72 } 73 } 74 75 const ctor = t.mock.fn(Clazz); 76 const instance = new ctor(42, 85); 77 78 assert(instance instanceof Clazz); 79 assert(instance instanceof ParentClazz); 80 assert.strictEqual(instance.a, 42); 81 assert.strictEqual(instance.getPrivateValue(), 85); 82 assert.strictEqual(instance.c, 127); 83 assert.strictEqual(ctor.mock.calls.length, 1); 84 85 const call = ctor.mock.calls[0]; 86 87 assert.deepStrictEqual(call.arguments, [42, 85]); 88 assert.strictEqual(call.error, undefined); 89 assert.strictEqual(call.result, instance); 90 assert.strictEqual(call.target, Clazz); 91 assert.strictEqual(call.this, instance); 92}); 93 94test('a no-op spy function is created by default', (t) => { 95 const fn = t.mock.fn(); 96 97 assert.strictEqual(fn.mock.calls.length, 0); 98 assert.strictEqual(fn(3, 4), undefined); 99 assert.strictEqual(fn.mock.calls.length, 1); 100 101 const call = fn.mock.calls[0]; 102 assert.deepStrictEqual(call.arguments, [3, 4]); 103 assert.strictEqual(call.result, undefined); 104 assert.strictEqual(call.target, undefined); 105 assert.strictEqual(call.this, undefined); 106}); 107 108test('internal no-op function can be reused', (t) => { 109 const fn1 = t.mock.fn(); 110 fn1.prop = true; 111 const fn2 = t.mock.fn(); 112 113 fn1(1); 114 fn2(2); 115 fn1(3); 116 117 assert.notStrictEqual(fn1.mock, fn2.mock); 118 assert.strictEqual(fn1.mock.calls.length, 2); 119 assert.strictEqual(fn2.mock.calls.length, 1); 120 assert.strictEqual(fn1.prop, true); 121 assert.strictEqual(fn2.prop, undefined); 122}); 123 124test('functions can be mocked multiple times at once', (t) => { 125 function sum(a, b) { 126 return a + b; 127 } 128 129 function difference(a, b) { 130 return a - b; 131 } 132 133 function product(a, b) { 134 return a * b; 135 } 136 137 const fn1 = t.mock.fn(sum, difference); 138 const fn2 = t.mock.fn(sum, product); 139 140 assert.strictEqual(fn1(5, 3), 2); 141 assert.strictEqual(fn2(5, 3), 15); 142 assert.strictEqual(fn2(4, 2), 8); 143 assert(!('mock' in sum)); 144 assert(!('mock' in difference)); 145 assert(!('mock' in product)); 146 assert.notStrictEqual(fn1.mock, fn2.mock); 147 assert.strictEqual(fn1.mock.calls.length, 1); 148 assert.strictEqual(fn2.mock.calls.length, 2); 149}); 150 151test('internal no-op function can be reused as methods', (t) => { 152 const obj = { 153 _foo: 5, 154 _bar: 9, 155 foo() { 156 return this._foo; 157 }, 158 bar() { 159 return this._bar; 160 }, 161 }; 162 163 t.mock.method(obj, 'foo'); 164 obj.foo.prop = true; 165 t.mock.method(obj, 'bar'); 166 assert.strictEqual(obj.foo(), 5); 167 assert.strictEqual(obj.bar(), 9); 168 assert.strictEqual(obj.bar(), 9); 169 assert.notStrictEqual(obj.foo.mock, obj.bar.mock); 170 assert.strictEqual(obj.foo.mock.calls.length, 1); 171 assert.strictEqual(obj.bar.mock.calls.length, 2); 172 assert.strictEqual(obj.foo.prop, true); 173 assert.strictEqual(obj.bar.prop, undefined); 174}); 175 176test('methods can be mocked multiple times but not at the same time', (t) => { 177 const obj = { 178 offset: 3, 179 sum(a, b) { 180 return this.offset + a + b; 181 }, 182 }; 183 184 function difference(a, b) { 185 return this.offset + (a - b); 186 } 187 188 function product(a, b) { 189 return this.offset + a * b; 190 } 191 192 const originalSum = obj.sum; 193 const fn1 = t.mock.method(obj, 'sum', difference); 194 195 assert.strictEqual(obj.sum(5, 3), 5); 196 assert.strictEqual(obj.sum(5, 1), 7); 197 assert.strictEqual(obj.sum, fn1); 198 assert.notStrictEqual(fn1.mock, undefined); 199 assert.strictEqual(originalSum.mock, undefined); 200 assert.strictEqual(difference.mock, undefined); 201 assert.strictEqual(product.mock, undefined); 202 assert.strictEqual(fn1.mock.calls.length, 2); 203 204 const fn2 = t.mock.method(obj, 'sum', product); 205 206 assert.strictEqual(obj.sum(5, 3), 18); 207 assert.strictEqual(obj.sum, fn2); 208 assert.notStrictEqual(fn1, fn2); 209 assert.strictEqual(fn2.mock.calls.length, 1); 210 211 obj.sum.mock.restore(); 212 assert.strictEqual(obj.sum, fn1); 213 obj.sum.mock.restore(); 214 assert.strictEqual(obj.sum, originalSum); 215 assert.strictEqual(obj.sum.mock, undefined); 216}); 217 218test('spies on an object method', (t) => { 219 const obj = { 220 prop: 5, 221 method(a, b) { 222 return a + b + this.prop; 223 }, 224 }; 225 226 assert.strictEqual(obj.method(1, 3), 9); 227 t.mock.method(obj, 'method'); 228 assert.strictEqual(obj.method.mock.calls.length, 0); 229 assert.strictEqual(obj.method(1, 3), 9); 230 231 const call = obj.method.mock.calls[0]; 232 233 assert.deepStrictEqual(call.arguments, [1, 3]); 234 assert.strictEqual(call.result, 9); 235 assert.strictEqual(call.target, undefined); 236 assert.strictEqual(call.this, obj); 237 238 assert.strictEqual(obj.method.mock.restore(), undefined); 239 assert.strictEqual(obj.method(1, 3), 9); 240 assert.strictEqual(obj.method.mock, undefined); 241}); 242 243test('spies on a getter', (t) => { 244 const obj = { 245 prop: 5, 246 get method() { 247 return this.prop; 248 }, 249 }; 250 251 assert.strictEqual(obj.method, 5); 252 253 const getter = t.mock.method(obj, 'method', { getter: true }); 254 255 assert.strictEqual(getter.mock.calls.length, 0); 256 assert.strictEqual(obj.method, 5); 257 258 const call = getter.mock.calls[0]; 259 260 assert.deepStrictEqual(call.arguments, []); 261 assert.strictEqual(call.result, 5); 262 assert.strictEqual(call.target, undefined); 263 assert.strictEqual(call.this, obj); 264 265 assert.strictEqual(getter.mock.restore(), undefined); 266 assert.strictEqual(obj.method, 5); 267}); 268 269test('spies on a setter', (t) => { 270 const obj = { 271 prop: 100, 272 // eslint-disable-next-line accessor-pairs 273 set method(val) { 274 this.prop = val; 275 }, 276 }; 277 278 assert.strictEqual(obj.prop, 100); 279 obj.method = 88; 280 assert.strictEqual(obj.prop, 88); 281 282 const setter = t.mock.method(obj, 'method', { setter: true }); 283 284 assert.strictEqual(setter.mock.calls.length, 0); 285 obj.method = 77; 286 assert.strictEqual(obj.prop, 77); 287 assert.strictEqual(setter.mock.calls.length, 1); 288 289 const call = setter.mock.calls[0]; 290 291 assert.deepStrictEqual(call.arguments, [77]); 292 assert.strictEqual(call.result, undefined); 293 assert.strictEqual(call.target, undefined); 294 assert.strictEqual(call.this, obj); 295 296 assert.strictEqual(setter.mock.restore(), undefined); 297 assert.strictEqual(obj.prop, 77); 298 obj.method = 65; 299 assert.strictEqual(obj.prop, 65); 300}); 301 302test('spy functions can be bound', (t) => { 303 const sum = t.mock.fn(function(arg1, arg2) { 304 return this + arg1 + arg2; 305 }); 306 const bound = sum.bind(1000); 307 308 assert.strictEqual(bound(9, 1), 1010); 309 assert.strictEqual(sum.mock.calls.length, 1); 310 311 const call = sum.mock.calls[0]; 312 assert.deepStrictEqual(call.arguments, [9, 1]); 313 assert.strictEqual(call.result, 1010); 314 assert.strictEqual(call.target, undefined); 315 assert.strictEqual(call.this, 1000); 316 317 assert.strictEqual(sum.mock.restore(), undefined); 318 assert.strictEqual(sum.bind(0)(2, 11), 13); 319}); 320 321test('mocks prototype methods on an instance', async (t) => { 322 class Runner { 323 async someTask(msg) { 324 return Promise.resolve(msg); 325 } 326 327 async method(msg) { 328 await this.someTask(msg); 329 return msg; 330 } 331 } 332 const msg = 'ok'; 333 const obj = new Runner(); 334 assert.strictEqual(await obj.method(msg), msg); 335 336 t.mock.method(obj, obj.someTask.name); 337 assert.strictEqual(obj.someTask.mock.calls.length, 0); 338 339 assert.strictEqual(await obj.method(msg), msg); 340 341 const call = obj.someTask.mock.calls[0]; 342 343 assert.deepStrictEqual(call.arguments, [msg]); 344 assert.strictEqual(await call.result, msg); 345 assert.strictEqual(call.target, undefined); 346 assert.strictEqual(call.this, obj); 347 348 const obj2 = new Runner(); 349 // Ensure that a brand new instance is not mocked 350 assert.strictEqual( 351 obj2.someTask.mock, 352 undefined 353 ); 354 355 assert.strictEqual(obj.someTask.mock.restore(), undefined); 356 assert.strictEqual(await obj.method(msg), msg); 357 assert.strictEqual(obj.someTask.mock, undefined); 358 assert.strictEqual(Runner.prototype.someTask.mock, undefined); 359}); 360 361test('spies on async static class methods', async (t) => { 362 class Runner { 363 static async someTask(msg) { 364 return Promise.resolve(msg); 365 } 366 367 static async method(msg) { 368 await this.someTask(msg); 369 return msg; 370 } 371 } 372 const msg = 'ok'; 373 assert.strictEqual(await Runner.method(msg), msg); 374 375 t.mock.method(Runner, Runner.someTask.name); 376 assert.strictEqual(Runner.someTask.mock.calls.length, 0); 377 378 assert.strictEqual(await Runner.method(msg), msg); 379 380 const call = Runner.someTask.mock.calls[0]; 381 382 assert.deepStrictEqual(call.arguments, [msg]); 383 assert.strictEqual(await call.result, msg); 384 assert.strictEqual(call.target, undefined); 385 assert.strictEqual(call.this, Runner); 386 387 assert.strictEqual(Runner.someTask.mock.restore(), undefined); 388 assert.strictEqual(await Runner.method(msg), msg); 389 assert.strictEqual(Runner.someTask.mock, undefined); 390 assert.strictEqual(Runner.prototype.someTask, undefined); 391 392}); 393 394test('given null to a mock.method it throws a invalid argument error', (t) => { 395 assert.throws(() => t.mock.method(null, {}), { code: 'ERR_INVALID_ARG_TYPE' }); 396}); 397 398test('it should throw given an inexistent property on a object instance', (t) => { 399 assert.throws(() => t.mock.method({ abc: 0 }, 'non-existent'), { 400 code: 'ERR_INVALID_ARG_VALUE' 401 }); 402}); 403 404test('spy functions can be used on classes inheritance', (t) => { 405 // Makes sure that having a null-prototype doesn't throw our system off 406 class A extends null { 407 static someTask(msg) { 408 return msg; 409 } 410 static method(msg) { 411 return this.someTask(msg); 412 } 413 } 414 class B extends A {} 415 class C extends B {} 416 417 const msg = 'ok'; 418 assert.strictEqual(C.method(msg), msg); 419 420 t.mock.method(C, C.someTask.name); 421 assert.strictEqual(C.someTask.mock.calls.length, 0); 422 423 assert.strictEqual(C.method(msg), msg); 424 425 const call = C.someTask.mock.calls[0]; 426 427 assert.deepStrictEqual(call.arguments, [msg]); 428 assert.strictEqual(call.result, msg); 429 assert.strictEqual(call.target, undefined); 430 assert.strictEqual(call.this, C); 431 432 assert.strictEqual(C.someTask.mock.restore(), undefined); 433 assert.strictEqual(C.method(msg), msg); 434 assert.strictEqual(C.someTask.mock, undefined); 435}); 436 437test('spy functions don\'t affect the prototype chain', (t) => { 438 439 class A { 440 static someTask(msg) { 441 return msg; 442 } 443 } 444 class B extends A {} 445 class C extends B {} 446 447 const msg = 'ok'; 448 449 const ABeforeMockIsUnchanged = Object.getOwnPropertyDescriptor(A, A.someTask.name); 450 const BBeforeMockIsUnchanged = Object.getOwnPropertyDescriptor(B, B.someTask.name); 451 const CBeforeMockShouldNotHaveDesc = Object.getOwnPropertyDescriptor(C, C.someTask.name); 452 453 t.mock.method(C, C.someTask.name); 454 C.someTask(msg); 455 const BAfterMockIsUnchanged = Object.getOwnPropertyDescriptor(B, B.someTask.name); 456 457 const AAfterMockIsUnchanged = Object.getOwnPropertyDescriptor(A, A.someTask.name); 458 const CAfterMockHasDescriptor = Object.getOwnPropertyDescriptor(C, C.someTask.name); 459 460 assert.strictEqual(CBeforeMockShouldNotHaveDesc, undefined); 461 assert.ok(CAfterMockHasDescriptor); 462 463 assert.deepStrictEqual(ABeforeMockIsUnchanged, AAfterMockIsUnchanged); 464 assert.strictEqual(BBeforeMockIsUnchanged, BAfterMockIsUnchanged); 465 assert.strictEqual(BBeforeMockIsUnchanged, undefined); 466 467 assert.strictEqual(C.someTask.mock.restore(), undefined); 468 const CAfterRestoreKeepsDescriptor = Object.getOwnPropertyDescriptor(C, C.someTask.name); 469 assert.ok(CAfterRestoreKeepsDescriptor); 470}); 471 472test('mocked functions report thrown errors', (t) => { 473 const testError = new Error('test error'); 474 const fn = t.mock.fn(() => { 475 throw testError; 476 }); 477 478 assert.throws(fn, /test error/); 479 assert.strictEqual(fn.mock.calls.length, 1); 480 481 const call = fn.mock.calls[0]; 482 483 assert.deepStrictEqual(call.arguments, []); 484 assert.strictEqual(call.error, testError); 485 assert.strictEqual(call.result, undefined); 486 assert.strictEqual(call.target, undefined); 487 assert.strictEqual(call.this, undefined); 488}); 489 490test('mocked constructors report thrown errors', (t) => { 491 const testError = new Error('test error'); 492 class Clazz { 493 constructor() { 494 throw testError; 495 } 496 } 497 498 const ctor = t.mock.fn(Clazz); 499 500 assert.throws(() => { 501 new ctor(); 502 }, /test error/); 503 assert.strictEqual(ctor.mock.calls.length, 1); 504 505 const call = ctor.mock.calls[0]; 506 507 assert.deepStrictEqual(call.arguments, []); 508 assert.strictEqual(call.error, testError); 509 assert.strictEqual(call.result, undefined); 510 assert.strictEqual(call.target, Clazz); 511 assert.strictEqual(call.this, undefined); 512}); 513 514test('mocks a function', (t) => { 515 const sum = (arg1, arg2) => arg1 + arg2; 516 const difference = (arg1, arg2) => arg1 - arg2; 517 const fn = t.mock.fn(sum, difference); 518 519 assert.strictEqual(fn.mock.calls.length, 0); 520 assert.strictEqual(fn(3, 4), -1); 521 assert.strictEqual(fn(9, 1), 8); 522 assert.strictEqual(fn.mock.calls.length, 2); 523 524 let call = fn.mock.calls[0]; 525 assert.deepStrictEqual(call.arguments, [3, 4]); 526 assert.strictEqual(call.result, -1); 527 assert.strictEqual(call.target, undefined); 528 assert.strictEqual(call.this, undefined); 529 530 call = fn.mock.calls[1]; 531 assert.deepStrictEqual(call.arguments, [9, 1]); 532 assert.strictEqual(call.result, 8); 533 assert.strictEqual(call.target, undefined); 534 assert.strictEqual(call.this, undefined); 535 536 assert.strictEqual(fn.mock.restore(), undefined); 537 assert.strictEqual(fn(2, 11), 13); 538}); 539 540test('mocks a constructor', (t) => { 541 class ParentClazz { 542 constructor(c) { 543 this.c = c; 544 } 545 } 546 547 class Clazz extends ParentClazz { 548 #privateValue; 549 550 constructor(a, b) { 551 super(a + b); 552 this.a = a; 553 this.#privateValue = b; 554 } 555 556 getPrivateValue() { 557 return this.#privateValue; 558 } 559 } 560 561 class MockClazz { 562 #privateValue; 563 564 constructor(z) { 565 this.z = z; 566 } 567 } 568 569 const ctor = t.mock.fn(Clazz, MockClazz); 570 const instance = new ctor(42, 85); 571 572 assert(!(instance instanceof MockClazz)); 573 assert(instance instanceof Clazz); 574 assert(instance instanceof ParentClazz); 575 assert.strictEqual(instance.a, undefined); 576 assert.strictEqual(instance.c, undefined); 577 assert.strictEqual(instance.z, 42); 578 assert.strictEqual(ctor.mock.calls.length, 1); 579 580 const call = ctor.mock.calls[0]; 581 582 assert.deepStrictEqual(call.arguments, [42, 85]); 583 assert.strictEqual(call.result, instance); 584 assert.strictEqual(call.target, Clazz); 585 assert.strictEqual(call.this, instance); 586 assert.throws(() => { 587 instance.getPrivateValue(); 588 }, /TypeError: Cannot read private member #privateValue /); 589}); 590 591test('mocks an object method', (t) => { 592 const obj = { 593 prop: 5, 594 method(a, b) { 595 return a + b + this.prop; 596 }, 597 }; 598 599 function mockMethod(a) { 600 return a + this.prop; 601 } 602 603 assert.strictEqual(obj.method(1, 3), 9); 604 t.mock.method(obj, 'method', mockMethod); 605 assert.strictEqual(obj.method.mock.calls.length, 0); 606 assert.strictEqual(obj.method(1, 3), 6); 607 608 const call = obj.method.mock.calls[0]; 609 610 assert.deepStrictEqual(call.arguments, [1, 3]); 611 assert.strictEqual(call.result, 6); 612 assert.strictEqual(call.target, undefined); 613 assert.strictEqual(call.this, obj); 614 615 assert.strictEqual(obj.method.mock.restore(), undefined); 616 assert.strictEqual(obj.method(1, 3), 9); 617 assert.strictEqual(obj.method.mock, undefined); 618}); 619 620test('mocks a getter', (t) => { 621 const obj = { 622 prop: 5, 623 get method() { 624 return this.prop; 625 }, 626 }; 627 628 function mockMethod() { 629 return this.prop - 1; 630 } 631 632 assert.strictEqual(obj.method, 5); 633 634 const getter = t.mock.method(obj, 'method', mockMethod, { getter: true }); 635 636 assert.strictEqual(getter.mock.calls.length, 0); 637 assert.strictEqual(obj.method, 4); 638 639 const call = getter.mock.calls[0]; 640 641 assert.deepStrictEqual(call.arguments, []); 642 assert.strictEqual(call.result, 4); 643 assert.strictEqual(call.target, undefined); 644 assert.strictEqual(call.this, obj); 645 646 assert.strictEqual(getter.mock.restore(), undefined); 647 assert.strictEqual(obj.method, 5); 648}); 649 650test('mocks a setter', (t) => { 651 const obj = { 652 prop: 100, 653 // eslint-disable-next-line accessor-pairs 654 set method(val) { 655 this.prop = val; 656 }, 657 }; 658 659 function mockMethod(val) { 660 this.prop = -val; 661 } 662 663 assert.strictEqual(obj.prop, 100); 664 obj.method = 88; 665 assert.strictEqual(obj.prop, 88); 666 667 const setter = t.mock.method(obj, 'method', mockMethod, { setter: true }); 668 669 assert.strictEqual(setter.mock.calls.length, 0); 670 obj.method = 77; 671 assert.strictEqual(obj.prop, -77); 672 assert.strictEqual(setter.mock.calls.length, 1); 673 674 const call = setter.mock.calls[0]; 675 676 assert.deepStrictEqual(call.arguments, [77]); 677 assert.strictEqual(call.result, undefined); 678 assert.strictEqual(call.target, undefined); 679 assert.strictEqual(call.this, obj); 680 681 assert.strictEqual(setter.mock.restore(), undefined); 682 assert.strictEqual(obj.prop, -77); 683 obj.method = 65; 684 assert.strictEqual(obj.prop, 65); 685}); 686 687test('mocks a getter with syntax sugar', (t) => { 688 const obj = { 689 prop: 5, 690 get method() { 691 return this.prop; 692 }, 693 }; 694 695 function mockMethod() { 696 return this.prop - 1; 697 } 698 const getter = t.mock.getter(obj, 'method', mockMethod); 699 assert.strictEqual(getter.mock.calls.length, 0); 700 assert.strictEqual(obj.method, 4); 701 702 const call = getter.mock.calls[0]; 703 704 assert.deepStrictEqual(call.arguments, []); 705 assert.strictEqual(call.result, 4); 706 assert.strictEqual(call.target, undefined); 707 assert.strictEqual(call.this, obj); 708 709 assert.strictEqual(getter.mock.restore(), undefined); 710 assert.strictEqual(obj.method, 5); 711}); 712 713test('mocks a setter with syntax sugar', (t) => { 714 const obj = { 715 prop: 100, 716 // eslint-disable-next-line accessor-pairs 717 set method(val) { 718 this.prop = val; 719 }, 720 }; 721 722 function mockMethod(val) { 723 this.prop = -val; 724 } 725 726 assert.strictEqual(obj.prop, 100); 727 obj.method = 88; 728 assert.strictEqual(obj.prop, 88); 729 730 const setter = t.mock.setter(obj, 'method', mockMethod); 731 732 assert.strictEqual(setter.mock.calls.length, 0); 733 obj.method = 77; 734 assert.strictEqual(obj.prop, -77); 735 assert.strictEqual(setter.mock.calls.length, 1); 736 737 const call = setter.mock.calls[0]; 738 739 assert.deepStrictEqual(call.arguments, [77]); 740 assert.strictEqual(call.result, undefined); 741 assert.strictEqual(call.target, undefined); 742 assert.strictEqual(call.this, obj); 743 744 assert.strictEqual(setter.mock.restore(), undefined); 745 assert.strictEqual(obj.prop, -77); 746 obj.method = 65; 747 assert.strictEqual(obj.prop, 65); 748}); 749 750test('mocked functions match name and length', (t) => { 751 function getNameAndLength(fn) { 752 return { 753 name: Object.getOwnPropertyDescriptor(fn, 'name'), 754 length: Object.getOwnPropertyDescriptor(fn, 'length'), 755 }; 756 } 757 758 function func1() {} 759 const func2 = function(a) {}; // eslint-disable-line func-style 760 const arrow = (a, b, c) => {}; 761 const obj = { method(a, b) {} }; 762 763 assert.deepStrictEqual( 764 getNameAndLength(func1), 765 getNameAndLength(t.mock.fn(func1)) 766 ); 767 assert.deepStrictEqual( 768 getNameAndLength(func2), 769 getNameAndLength(t.mock.fn(func2)) 770 ); 771 assert.deepStrictEqual( 772 getNameAndLength(arrow), 773 getNameAndLength(t.mock.fn(arrow)) 774 ); 775 assert.deepStrictEqual( 776 getNameAndLength(obj.method), 777 getNameAndLength(t.mock.method(obj, 'method', func1)) 778 ); 779}); 780 781test('method() fails if method cannot be redefined', (t) => { 782 const obj = { 783 prop: 5, 784 }; 785 786 Object.defineProperty(obj, 'method', { 787 configurable: false, 788 value(a, b) { 789 return a + b + this.prop; 790 } 791 }); 792 793 function mockMethod(a) { 794 return a + this.prop; 795 } 796 797 assert.throws(() => { 798 t.mock.method(obj, 'method', mockMethod); 799 }, /Cannot redefine property: method/); 800 assert.strictEqual(obj.method(1, 3), 9); 801 assert.strictEqual(obj.method.mock, undefined); 802}); 803 804test('method() fails if field is a property instead of a method', (t) => { 805 const obj = { 806 prop: 5, 807 method: 100, 808 }; 809 810 function mockMethod(a) { 811 return a + this.prop; 812 } 813 814 assert.throws(() => { 815 t.mock.method(obj, 'method', mockMethod); 816 }, /The argument 'methodName' must be a method/); 817 assert.strictEqual(obj.method, 100); 818 assert.strictEqual(obj.method.mock, undefined); 819}); 820 821test('mocks can be auto-restored', (t) => { 822 let cnt = 0; 823 824 function addOne() { 825 cnt++; 826 return cnt; 827 } 828 829 function addTwo() { 830 cnt += 2; 831 return cnt; 832 } 833 834 const fn = t.mock.fn(addOne, addTwo, { times: 2 }); 835 836 assert.strictEqual(fn(), 2); 837 assert.strictEqual(fn(), 4); 838 assert.strictEqual(fn(), 5); 839 assert.strictEqual(fn(), 6); 840}); 841 842test('mock implementation can be changed dynamically', (t) => { 843 let cnt = 0; 844 845 function addOne() { 846 cnt++; 847 return cnt; 848 } 849 850 function addTwo() { 851 cnt += 2; 852 return cnt; 853 } 854 855 function addThree() { 856 cnt += 3; 857 return cnt; 858 } 859 860 const fn = t.mock.fn(addOne); 861 862 assert.strictEqual(fn.mock.callCount(), 0); 863 assert.strictEqual(fn(), 1); 864 assert.strictEqual(fn(), 2); 865 assert.strictEqual(fn(), 3); 866 assert.strictEqual(fn.mock.callCount(), 3); 867 868 fn.mock.mockImplementation(addTwo); 869 assert.strictEqual(fn(), 5); 870 assert.strictEqual(fn(), 7); 871 assert.strictEqual(fn.mock.callCount(), 5); 872 873 fn.mock.restore(); 874 assert.strictEqual(fn(), 8); 875 assert.strictEqual(fn(), 9); 876 assert.strictEqual(fn.mock.callCount(), 7); 877 878 assert.throws(() => { 879 fn.mock.mockImplementationOnce(common.mustNotCall(), 6); 880 }, /The value of "onCall" is out of range\. It must be >= 7/); 881 882 fn.mock.mockImplementationOnce(addThree, 7); 883 fn.mock.mockImplementationOnce(addTwo, 8); 884 assert.strictEqual(fn(), 12); 885 assert.strictEqual(fn(), 14); 886 assert.strictEqual(fn(), 15); 887 assert.strictEqual(fn.mock.callCount(), 10); 888 fn.mock.mockImplementationOnce(addThree); 889 assert.strictEqual(fn(), 18); 890 assert.strictEqual(fn(), 19); 891 assert.strictEqual(fn.mock.callCount(), 12); 892}); 893 894test('local mocks are auto restored after the test finishes', async (t) => { 895 const obj = { 896 foo() {}, 897 bar() {}, 898 }; 899 const originalFoo = obj.foo; 900 const originalBar = obj.bar; 901 902 assert.strictEqual(originalFoo, obj.foo); 903 assert.strictEqual(originalBar, obj.bar); 904 905 const mockFoo = t.mock.method(obj, 'foo'); 906 907 assert.strictEqual(mockFoo, obj.foo); 908 assert.notStrictEqual(originalFoo, obj.foo); 909 assert.strictEqual(originalBar, obj.bar); 910 911 t.beforeEach(() => { 912 assert.strictEqual(mockFoo, obj.foo); 913 assert.strictEqual(originalBar, obj.bar); 914 }); 915 916 t.afterEach(() => { 917 assert.strictEqual(mockFoo, obj.foo); 918 assert.notStrictEqual(originalBar, obj.bar); 919 }); 920 921 await t.test('creates mocks that are auto restored', (t) => { 922 const mockBar = t.mock.method(obj, 'bar'); 923 924 assert.strictEqual(mockFoo, obj.foo); 925 assert.strictEqual(mockBar, obj.bar); 926 assert.notStrictEqual(originalBar, obj.bar); 927 }); 928 929 assert.strictEqual(mockFoo, obj.foo); 930 assert.strictEqual(originalBar, obj.bar); 931}); 932 933test('reset mock calls', (t) => { 934 const sum = (arg1, arg2) => arg1 + arg2; 935 const difference = (arg1, arg2) => arg1 - arg2; 936 const fn = t.mock.fn(sum, difference); 937 938 assert.strictEqual(fn(1, 2), -1); 939 assert.strictEqual(fn(2, 1), 1); 940 assert.strictEqual(fn.mock.calls.length, 2); 941 assert.strictEqual(fn.mock.callCount(), 2); 942 943 fn.mock.resetCalls(); 944 assert.strictEqual(fn.mock.calls.length, 0); 945 assert.strictEqual(fn.mock.callCount(), 0); 946 947 assert.strictEqual(fn(3, 2), 1); 948}); 949 950test('uses top level mock', () => { 951 function sum(a, b) { 952 return a + b; 953 } 954 955 function difference(a, b) { 956 return a - b; 957 } 958 959 const fn = mock.fn(sum, difference); 960 961 assert.strictEqual(fn.mock.calls.length, 0); 962 assert.strictEqual(fn(3, 4), -1); 963 assert.strictEqual(fn.mock.calls.length, 1); 964 mock.reset(); 965 assert.strictEqual(fn(3, 4), 7); 966 assert.strictEqual(fn.mock.calls.length, 2); 967}); 968 969test('the getter and setter options cannot be used together', (t) => { 970 assert.throws(() => { 971 t.mock.method({}, 'method', { getter: true, setter: true }); 972 }, /The property 'options\.setter' cannot be used with 'options\.getter'/); 973}); 974 975test('method names must be strings or symbols', (t) => { 976 const symbol = Symbol(); 977 const obj = { 978 method() {}, 979 [symbol]() {}, 980 }; 981 982 t.mock.method(obj, 'method'); 983 t.mock.method(obj, symbol); 984 985 assert.throws(() => { 986 t.mock.method(obj, {}); 987 }, /The "methodName" argument must be one of type string or symbol/); 988}); 989 990test('the times option must be an integer >= 1', (t) => { 991 assert.throws(() => { 992 t.mock.fn({ times: null }); 993 }, /The "options\.times" property must be of type number/); 994 995 assert.throws(() => { 996 t.mock.fn({ times: 0 }); 997 }, /The value of "options\.times" is out of range/); 998 999 assert.throws(() => { 1000 t.mock.fn(() => {}, { times: 3.14159 }); 1001 }, /The value of "options\.times" is out of range/); 1002}); 1003 1004test('spies on a class prototype method', (t) => { 1005 class Clazz { 1006 constructor(c) { 1007 this.c = c; 1008 } 1009 1010 getC() { 1011 return this.c; 1012 } 1013 } 1014 1015 const instance = new Clazz(85); 1016 1017 assert.strictEqual(instance.getC(), 85); 1018 t.mock.method(Clazz.prototype, 'getC'); 1019 1020 assert.strictEqual(instance.getC.mock.calls.length, 0); 1021 assert.strictEqual(instance.getC(), 85); 1022 assert.strictEqual(instance.getC.mock.calls.length, 1); 1023 assert.strictEqual(Clazz.prototype.getC.mock.calls.length, 1); 1024 1025 const call = instance.getC.mock.calls[0]; 1026 assert.deepStrictEqual(call.arguments, []); 1027 assert.strictEqual(call.result, 85); 1028 assert.strictEqual(call.error, undefined); 1029 assert.strictEqual(call.target, undefined); 1030 assert.strictEqual(call.this, instance); 1031}); 1032 1033test('getter() fails if getter options set to false', (t) => { 1034 assert.throws(() => { 1035 t.mock.getter({}, 'method', { getter: false }); 1036 }, /The property 'options\.getter' cannot be false/); 1037}); 1038 1039test('setter() fails if setter options set to false', (t) => { 1040 assert.throws(() => { 1041 t.mock.setter({}, 'method', { setter: false }); 1042 }, /The property 'options\.setter' cannot be false/); 1043}); 1044 1045test('getter() fails if setter options is true', (t) => { 1046 assert.throws(() => { 1047 t.mock.getter({}, 'method', { setter: true }); 1048 }, /The property 'options\.setter' cannot be used with 'options\.getter'/); 1049}); 1050 1051test('setter() fails if getter options is true', (t) => { 1052 assert.throws(() => { 1053 t.mock.setter({}, 'method', { getter: true }); 1054 }, /The property 'options\.setter' cannot be used with 'options\.getter'/); 1055}); 1056