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 spyOn ser ut til å være den samme som for jest.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');
});

Relaterte artikler