javascript - global Jest SpyOn-funksjon kaller ikke den originale funksjonen
Jeg håper noen kan hjelpe meg med å forstå interaktiviteten til JS-prototyper og jest.spOn().
Jeg har et lite eksempel: En eksempelklasse i filen TestObj.ts:
export default class TestObj {
foo() {
// Do Something e.g.
console.log("Hello World!");
}
}
Det følgende eksempel Test Case lykkes, men console.log blir aldri utført.
import TestObj from './TestObj';
const spyObj = jest.spyOn(TestObj.prototype, 'foo');
test('debug test', () => {
const obj = new TestObj();
obj.foo();
expect(spyObj).toHaveBeenCalled();
});
Hvis jeg endrer eksempeltesttilfellet til det følgende, lykkes testen, og console.log-setningen blir kalt som forventet.
import TestObj from './TestObj';
test('debug test', () => {
const spyObj = jest.spyOn(TestObj.prototype, 'foo');
const obj = new TestObj();
obj.foo();
expect(spyObj).toHaveBeenCalled();
});
Noen idé om hvorfor versjonen som bruker den globale spyOn-variabelen ikke fungerer som forventet?
Rediger :
Det ser ut til å ikke være relatert til prototyper. Det samme problemet finnes for en funksjon uten noen form for klasse, og ved å redigere det første kodeeksempelet (TestObj.ts) til dette:
export foo() {
// Do Something e.g.
console.log("Hello World!");
};
Vi får det samme problemet med den oppdaterte andre koden. (Testen lykkes, men konsollloggen blir aldri nådd.)
import * as testlib from './TestObj';
const spyObj = jest.spyOn(testlib, 'foo');
test('debug test', () => {
testlib.foo();
expect(spyObj).toHaveBeenCalled();
});
Men hvis vi oppdaterer det andre utdraget til følgende, lykkes testen og konsollloggen blir utført:
import * as testlib from './TestObj';
const spyObj: jest.SpyInstance;
beforeEach(() => {
spyObj = jest.spyOn(testlib, 'foo');
});
test('debug test', () => {
testlib.foo();
expect(spyObj).toHaveBeenCalled();
});
Jeg har imidlertid fortsatt ingen anelse om hvorfor jeg oppdager dette problemet.
Svar #1
hvem som helst som kommer over dette innlegget,
Problembeskrivelse
Jeg gjorde mye mer forskning (prøve og feile, MDN, Jest man-side og mange Medium-artikler), og jeg tror at jeg fant årsaken til den merkelige oppførselen. For å forstå dette problemet er det viktig å kjenne til flere punkter:
- 1: JS-prototyper er globale variabler som hvert objekt av den tilsvarende typen er avhengig av.
- 2: Jest tilbakestiller ikke globale variabler etter hver test, endringer gjort før, eller inne i en test til en global variabel vil bli beholdt for hele testpakken (filen).
- 3: Jest spy on-funksjonen er faktisk en mockup av den spesifiserte funksjonen med en implementering som kaller funksjonen selv. f.eks.:
jest.SpyOn(TestObj.prototype, 'foo');er faktisk implementert som:TestObj.prototype.foo=new jest.fn().mockImplementation(() => {original_TestObj.prototype.foo()});Dette betyr at å spionere på en funksjon i en klasseprototype faktisk endrer en global variabel. - 4: Avhengig av din jest-konfigurasjon, er det mulighet for å tilbakestille mock-funksjoner til standardverdien før hver test. Men vær forsiktig, standardfunksjonen for
spyOnser ut til å være den samme som forjest.fn()selv, en tom implementering, noe som betyr at mocken fortsatt kan kalles, men ingen kode, spesielt ikke den originale implementeringen, blir utført.
Løsning
- unngå å endre globale variabler, hvis du vil at testtilfellene dine skal være uavhengige av hverandre.
- unngå å spionere på prototyper, i testtilfeller, hvis du kun trenger spionen i en enkelt test, prøv å spionere på det lokale objektet, for eksempel:
test('should foo', () => {
const testObj = new TestObj();
const spyOnFn = jest.spyOn(testObj, 'foo');
// Do anything
expect(spyOnFn).to//Have been anything
});
- hvis du trenger
spyOn-implementasjoner av den samme funksjonen i mer enn én test, prøv å lage en global variabel for testen, men bruk før-hver-funksjonaliteten i jest for å sette opp spionen. Denne funksjonaliteten utføres etter at alle mocks er tilbakestilt (hvis aktivert). f.eks.:
let spyOnFunction1: jest.SpyInstance;
beforeEach(()=> {
spyOnFunction1 = jest.spyOn(TestObj.prototype, 'foo');
});
