Skip to content

Commit ad5d6ee

Browse files
authored
docs: improve mocking classes section (#7833)
1 parent 63ca7a1 commit ad5d6ee

File tree

1 file changed

+39
-5
lines changed

1 file changed

+39
-5
lines changed

docs/guide/mocking.md

+39-5
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,10 @@ class Dog {
608608
return 'animal'
609609
}
610610

611+
greet = (): string => {
612+
return `Hi! My name is ${this.name}!`
613+
}
614+
611615
speak(): string {
612616
return 'bark!'
613617
}
@@ -622,18 +626,40 @@ We can re-create this class with ES5 functions:
622626
```ts
623627
const Dog = vi.fn(function (name) {
624628
this.name = name
629+
// mock instance methods in the constructor, each instance will have its own spy
630+
this.greet = vi.fn(() => `Hi! My name is ${this.name}!`)
625631
})
626632

627633
// notice that static methods are mocked directly on the function,
628634
// not on the instance of the class
629635
Dog.getType = vi.fn(() => 'mocked animal')
630636

631637
// mock the "speak" and "feed" methods on every instance of a class
632-
// all `new Dog()` instances will inherit these spies
638+
// all `new Dog()` instances will inherit and share these spies
633639
Dog.prototype.speak = vi.fn(() => 'loud bark!')
634640
Dog.prototype.feed = vi.fn()
635641
```
636642

643+
::: warning
644+
If a non-primitive is returned from the constructor function, that value will become the result of the new expression. In this case the `[[Prototype]]` may not be correctly bound:
645+
646+
```ts
647+
const CorrectDogClass = vi.fn(function (name) {
648+
this.name = name
649+
})
650+
651+
const IncorrectDogClass = vi.fn(name => ({
652+
name
653+
}))
654+
655+
const Marti = new CorrectDogClass('Marti')
656+
const Newt = new IncorrectDogClass('Newt')
657+
658+
Marti instanceof CorrectDogClass // ✅ true
659+
Newt instanceof IncorrectDogClass // ❌ false!
660+
```
661+
:::
662+
637663
::: tip WHEN TO USE?
638664
Generally speaking, you would re-create a class like this inside the module factory if the class is re-exported from another module:
639665

@@ -673,14 +699,22 @@ test('can feed dogs', () => {
673699
```
674700
:::
675701

676-
Now, when we create a new instance of the `Dog` class its `speak` method (alongside `feed`) is already mocked:
702+
Now, when we create a new instance of the `Dog` class its `speak` method (alongside `feed` and `greet`) is already mocked:
677703

678704
```ts
679-
const dog = new Dog('Cooper')
680-
dog.speak() // loud bark!
705+
const Cooper = new Dog('Cooper')
706+
Cooper.speak() // loud bark!
707+
Cooper.greet() // Hi! My name is Cooper!
681708

682709
// you can use built-in assertions to check the validity of the call
683-
expect(dog.speak).toHaveBeenCalled()
710+
expect(Cooper.speak).toHaveBeenCalled()
711+
expect(Cooper.greet).toHaveBeenCalled()
712+
713+
const Max = new Dog('Max')
714+
715+
// methods assigned to the prototype are shared between instances
716+
expect(Max.speak).toHaveBeenCalled()
717+
expect(Max.greet).not.toHaveBeenCalled()
684718
```
685719

686720
We can reassign the return value for a specific instance:

0 commit comments

Comments
 (0)