Jest: Wie verspotte ich eine bestimmte Modulfunktion?

Erstellt am 25. Apr. 2016  ·  116Kommentare  ·  Quelle: facebook/jest

Ich kämpfe mit etwas, von dem ich denke, dass es sowohl einfach als auch offensichtlich sein sollte, aber aus irgendeinem Grund kann ich es nicht herausfinden.

Ich habe ein Modul. Es exportiert mehrere Funktionen. Hier ist myModule.js :

export function foo() {...}
export function bar() {...}
export function baz() {...}

Ich entmocke das Modul zum Testen.

jest.unmock('./myModule.js');

Allerdings muss ich foo verspotten, weil es Ajax-Aufrufe an mein Backend macht. Ich möchte, dass jede Funktion in dieser Datei unverspottet bleibt, erwarten Sie für foo , die ich verspotten möchte. Und die Funktionen bar und baz make Anrufe intern auf foo , also wenn mein Test ruft bar() , die unmocked bar ruft die verspottet foo .

Es erscheint in der Scherzdokumentation, dass Aufrufe von unmock und mock auf das gesamte Modul angewendet werden. Wie kann ich eine bestimmte Funktion verspotten? Meinen Code willkürlich in einzelne Module aufzuteilen, damit sie richtig getestet werden können, ist lächerlich.

Hilfreichster Kommentar

Du kannst tun:

jest.unmock('./myModule.js');

const myModule = require('myModule');
myModule.foo = jest.fn();

Siehe http://facebook.github.io/jest/docs/api.html#mock -functions

Alle 116 Kommentare

Bei genauerer Analyse scheint es, dass jest-mock einen AST für das gesamte Modul generiert und dann diesen AST verwendet, um ein simuliertes Modul zu erstellen, das den Exporten des Originals entspricht: https://github.com/facebook/jest/tree/master/packages /scherz-spott

Mit anderen Test-Frameworks, wie zum Beispiel Pythons Mock (https://docs.python.org/3/library/unittest.mock-examples.html), können Sie bestimmte Funktionen simulieren. Dies ist ein grundlegendes Testkonzept.

Ich empfehle die Möglichkeit, einen Teil eines Moduls zu verspotten. Ich denke, dass jest-mock geändert werden sollte, um Exporte von Mocking bedingt zu ignorieren und auf die ursprüngliche Implementierung zu verweisen.

Du kannst tun:

jest.unmock('./myModule.js');

const myModule = require('myModule');
myModule.foo = jest.fn();

Siehe http://facebook.github.io/jest/docs/api.html#mock -functions

Ich glaube, Sie haben ein grundlegendes Missverständnis darüber, wie require funktioniert. Wenn Sie require() aufrufen, erhalten Sie keine Instanz des Moduls. Sie erhalten ein Objekt mit Verweisen auf die Funktionen des Moduls. Wenn Sie im gewünschten Modul einen Wert überschreiben, wird Ihre eigene Referenz überschrieben, _aber die Implementierung behält die Originalreferenzen_.

Wenn Sie in Ihrem Beispiel myModule.foo() aufrufen, ja, rufen Sie die verspottete Version auf. Aber wenn Sie myModule.bar() aufrufen, was intern foo() aufruft, ist das foo es verweist, _nicht Ihre überschriebene Version_. Wenn Sie mir nicht glauben, können Sie es testen.

Daher ist das von Ihnen beschriebene Beispiel für mein Problem nicht ausreichend. Weißt du etwas, was ich nicht weiß?

@cpojer

Ich glaube, ich verstehe das ganz gut. Die Art und Weise, wie Babel Module kompiliert, macht dies jedoch nicht einfacher zu verstehen und ich verstehe Ihre Verwirrung. Ich weiß nicht genau, wie sich dies in einer echten ES2015-Umgebung mit Modulen verhalten würde, hauptsächlich weil derzeit keine solche Umgebung existiert (außer vielleicht den neuesten Versionen von Chrome Canary, die ich noch nicht ausprobiert habe). Um zu erklären, was passiert, müssen wir uns die kompilierte Ausgabe Ihres Babel-Codes ansehen. Es wird ungefähr so ​​aussehen:

var foo = function foo() {};
var bar = function bar() { foo(); };

exports.foo = foo;
exports.bar = bar;

In diesem Fall ist es in der Tat richtig, dass Sie foo nicht verspotten können, und ich entschuldige mich dafür, dass Sie Ihre ursprüngliche Ausgabe nicht richtig gelesen haben, aber es wurde keine Annahme getroffen, wie foo genannt wurde, also nahm ich an, dass es exports.foo() . Das Obige zu unterstützen, indem eine Funktion nach der Anforderung eines Moduls simuliert wird, ist in JavaScript unmöglich – es gibt (fast) keine Möglichkeit, die Bindung, auf die foo verweist, abzurufen und zu ändern.

Wenn Sie Ihren Code jedoch so ändern:

var foo = function foo() {};
var bar = function bar() { exports.foo(); };

exports.foo = foo;
exports.bar = bar;

und dann machen Sie in Ihrer Testdatei:

var module = require('../module');
module.foo = jest.fn();
module.bar();

es wird wie erwartet funktionieren. Dies ist, was wir bei Facebook tun, wo wir ES2015 nicht verwenden.

Während ES2015-Module möglicherweise unveränderliche Bindungen für das haben, was sie exportieren, erzwingt der zugrunde liegende kompilierte Code, zu dem Babel derzeit kompiliert, keine derartigen Einschränkungen. Ich sehe derzeit keine Möglichkeit, genau das zu unterstützen, was Sie in einer strikten ES2015-Modulumgebung mit nativ unterstützten Modulen verlangen. jest-mock funktioniert so, dass es den Modulcode isoliert ausführt und dann die Metadaten eines Moduls abruft und Scheinfunktionen erstellt. Auch in diesem Fall hat es keine Möglichkeit, die lokale Bindung von foo zu ändern. Wenn Sie Ideen haben, wie Sie dies effektiv umsetzen können, tragen Sie bitte hier oder mit einem Pull-Request bei. Ich möchte Sie daran erinnern, dass wir für dieses Projekt einen Verhaltenskodex haben, den Sie hier nachlesen können: https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct

Die richtige Lösung in Ihrem Beispiel besteht nicht darin, foo zu simulieren, sondern die API auf höherer Ebene zu simulieren, die foo aufruft (wie XMLHttpRequest oder die stattdessen verwendete Abstraktion).

@cpojer Danke für deine ausführliche Erklärung. Es tut mir leid, wenn ich Sie mit meiner Sprache beleidigt habe, ich bin sehr effizient mit meinem technischen Schreiben und möchte meinen Standpunkt so schnell wie möglich vermitteln. Um die Dinge ins rechte Licht zu rücken, habe ich 5 Stunden damit verbracht, dieses Thema zu verstehen und 2 detaillierte Kommentare geschrieben, dann haben Sie es mit einer kurzen Nachricht abgeschlossen, die den Sinn meiner beiden Aussagen völlig verfehlt hat. Deshalb sagte meine nächste Nachricht, dass Sie ein "grundlegendes Missverständnis" hatten, weil entweder 1) Sie meinen Standpunkt nicht verstanden haben oder 2) Sie require() nicht verstanden haben, was zum Glück Option 1 war.

Ich werde über eine mögliche Lösung für mein Problem nachdenken, um es zu umgehen, habe ich vorerst eine API auf niedrigerer Ebene verspottet, aber es sollte definitiv eine Möglichkeit geben, die Funktion direkt zu verspotten, da dies sehr nützlich wäre.

Ich stimme zu, dass es nützlich wäre, dies zu tun, aber es gibt keine gute Möglichkeit in JS, dies ohne (wahrscheinlich langsame) statische Analyse im Voraus zu tun :(

@cpojer : Ich bin mir nicht sicher, ob es der einzuspringen , aber ich konnte keine anderen Gespräche darüber finden.

Ausgehend von Ihrem obigen Vorschlag habe ich dies getan, um eine Funktion aus einer anderen im selben Modul zu verspotten:

jest.unmock('./someModule.js');
import someModule from './someModule.js';

it('function1 calls function 2', () => {
    someModule.function2 = jest.fn();

    someModule.function1(...);

    expect(someModule.function2).toHaveBeenCalledWith(...);
});

Dies funktioniert für den einen Test, aber ich habe keine Möglichkeit gefunden, dies auf eine Weise zu erreichen, die nur auf den einen it(...); Block isoliert ist. Wie oben geschrieben, betrifft es jeden Test, was es schwierig macht, die echten function2 in einem anderen Test zu testen. Irgendwelche Tipps?

Sie können .mockClear für die Funktion in beforeEach oder jest.clearAllMocks() aufrufen, wenn Sie Jest 16 verwenden.

Hey @cpojer! Ich benutze Jest 16. Weder jest.clearAllMocks() noch someModule.function2.mockClear() funktionieren für mich. Sie funktionieren nur, wenn es sich bei dem Mock um ein gesamtes Modul handelt, nicht um eine Funktion eines importierten Moduls. In meinem Projekt bleibt die Funktion in nachfolgenden Tests verspottet. Wenn dies nicht erwartet wird, werde ich sehen, ob ich in einem kleinen Beispielprojekt replizieren und ein neues Problem erstellen kann. Gute Idee?

@cpojer -

Die richtige Lösung in Ihrem Beispiel besteht nicht darin, foo zu simulieren, sondern die API auf höherer Ebene zu simulieren, die foo aufruft (wie XMLHttpRequest oder die stattdessen verwendete Abstraktion).

Ich bin neu bei Jest und kämpfe mit einem ähnlichen Problem. Ich verwende axios , das unter der Haube XMLHttpRequest , und ich möchte axios nicht verspotten, sondern das tatsächliche XMLHttpRequest verspotten. Es scheint, dass ich seine Methoden selbst implementieren müsste, etwa so . Ist das der richtige Ansatz?

Vielen Dank!

Ja, so etwas sollte Sie auf den richtigen Weg bringen! :) Verwenden Sie jest.fn als schönere API :D

@cpojer zu Ihrem Kommentar hier: https://github.com/facebook/jest/issues/936#issuecomment -214939935

Wie würden Sie das mit ES2015 machen?

// myModyle.js
export foo = (string) => "foo-" + string
export bar = (string2) => foo(string2)

// myModule.test.js
var module = require('./myModule');

// how do I mock foo here so this test passes?
expect(bar("hello")).toEqual("test-hello")

Für jeden, der auf der Suche nach einer Lösung darauf stößt, scheint Folgendes für mich zu funktionieren, wenn ich viele const / Funktionen in eine Datei exportiere und sie in eine Datei importiere, die ich teste

`` javascript function mockFunctions() { const original = require.requireActual('../myModule'); return { ...original, //Pass down all the exported objects test: jest.fn(() => {console.log('I didnt call the original')}), someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}), } jest.mock('../myModule', () => mockFunctions()); const storage = require.requireMock('../myModule');

@ainesophur , ich bin
Ich bin derzeit auf jest 18.1 (und create-react-app 0.9.4)

...<codes from comment above>..

// Let's say the original myModule has a function doSmth() that calls test()
storage.doSmth();
expect(storage.test).toHaveBeenCalled();

Der Test schlägt dann fehl mit:

expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called.

@huyph Sie

@ainesophaurus ...ähm. Ich dachte, Ihre obigen Codes dienen dazu, die Methode test() zu verspotten? dieser Teil: test: jest.fn(() => {console.log('I didnt call the original')}),

@ainesophur Ich habe deinen Code auch ausprobiert. Aber bei mir hat es nicht funktioniert. Es führt nie die Mock-Funktion aus. Die Erwartung wird also nie erfüllt.

Ich denke, dies liegt an der Art und Weise, wie require funktioniert, wie oben beschrieben ... Ich wünschte, es gäbe eine Lösung dafür.

@cpojer Gibt es etwas Neues in Bezug auf teilweise verspottete Module?

@rantonmattei & @huyph Ich müsste einen Ausschnitt Ihrer ausführen . Sie müssen Ihren Mock definieren, bevor die eigentliche Implementierungsdatei benötigt/importiert wird. Es ist schon eine Weile her, dass ich mit JEST gearbeitet habe, aber ich erinnere mich, dass ich es schließlich dazu gebracht habe, alles zu verspotten, was ich brauchte, sei es eine node_modules-Bibliothek oder eine Datei in meiner App. Ich habe ein bisschen wenig Zeit mit ATM, aber hier sind einige der Tests aus einem Projekt, an dem ich mit Jest gearbeitet habe.

Eine Datei aus einer Abhängigkeit verspotten

Die eigentliche Funktionsdefinition in diesem Beispiel erfolgt durch React-native.. Ich verspotte die Datei "react-native/Libraries/Utilities/dismissKeyboard.js"

Dies ist eine Scheindatei unter __mocks__/react-native/Libraries/Utilities/dismissKeyboard.js

function storeMockFunctions() {
  return jest.fn().mockImplementation(() => true);
}
jest.mock('react-native/Libraries/Utilities/dismissKeyboard', () => storeMockFunctions(), { virtual: true });
const dismissKeyboard = require('react-native/Libraries/Utilities/dismissKeyboard');
exports = module.exports = storeMockFunctions;

Ich kann die Testdatei, die ich für das obige verwendet habe, nicht finden, aber es war so etwas wie das Modul anfordern, ich würde es in __mocks__ finden und dann könnte ich etwas tun wie

expect(dismissKeyboard.mock.calls).toHaveLength(1);

Eine von Ihnen kontrollierte Datei verspotten
Tatsächliche Funktionsdefinition

export const setMerchantStores = (stores) => storage.set('stores', stores);

Testdatei mit Mock

const { storeListEpic, offerListEpic } = require('../merchant');

function storeMockFunctions() {
  const original = require.requireActual('../../common/Storage');
  return {
    ...original,
    setMerchantStores: jest.fn((...args) => original.setMerchantStores(...args)),
    setMerchantOffers: jest.fn((...args) => original.setMerchantOffers(...args)),
  };
}
jest.mock('../../common/Storage', () => storeMockFunctions());
import * as storage from '../../common/Storage';

afterEach(() => {
  storage.setMerchantStores.mockClear();
});

it('handle storeListEpic type STORE_LIST_REQUEST -> STORE_LIST_SUCCESS', async () => {
  const scope = nock('http://url')
  .get('/api/merchant/me/stores')
  .reply(200, storeData);
  const result = await storeListEpic(ActionsObservable.of(listStores())).toPromise();
  expect(storage.setMerchantStores.mock.calls).toHaveLength(1);
  expect(await storage.getMerchantStores()).toEqual({ ids: storesApiData.result, list: storesApiData.entities.store});
});

danke fürs Teilen von @ainesophaurus. Ich bekomme es immer noch nicht zum Laufen mit jest 18.1. Hier meine Codes:

it('should save session correctly', () => {

  function mockFunctions() {
    const original = require.requireActual('./session');
    return {
      ...original,
      restartCheckExpiryDateTimeout: jest.fn((() => {
        console.log('I didn\'t call the original');
      })),
    }
  }

  jest.mock('./session', () => mockFunctions());
  const mockSession = require('./session');

  // NOTE: saveSession() will call the original restartCheckExpiryDateTimeout() instead of my
  // mock one. However, mockSession.restartCheckExpiryDateTimeout() does call the mock one
  mockSession.saveSession('', getTomorrowDate(), 'AUTH');

  // mockSession.restartCheckExpiryDateTimeout(); // does print out "I didn't call the original"

  expect(mockSession.restartCheckExpiryDateTimeout).toHaveBeenCalled();
});

In session.js

export function saveSession(sessionId, sessionExpiryDate, authToken) {
  ....
  restartCheckExpiryDateTimeout(sessionExpiryDate);
  ...
}
....

export function restartCheckExpiryDateTimeout(...) {
....
}

Ich kann keinen Weg finden, dieses Problem zu lösen. Kann ich das bitte wieder öffnen? @cpojer

@huyph die Art und Weise, wie Sie den Export saveSession ausführen , wird das lokal definierte restartCheckExpiryDateTimeout aufrufen, anstatt das Modul zu durchlaufen und module.restartCheckExpiryDateTimeout aufzurufen - also Ihr verspottetes module.restartCheckExpiryDateTimeout von saveSession module.restartCheckExpiryDateTimeout nicht erkannt, da saveSession die aktuell definierte restartCheckExpiryDateTimeout Funktion aufruft.

Ich würde saveSession einem const zuweisen und dann saveSession.restartCheckExpiryDateTimeout = () => {...logic} . .dann rufen Sie aus saveSession.saveSession saveSession.restartCheckExpiryDateTimeout anstatt nur restartCheckExpiryDateTimeout . Exportieren Sie Ihre neue const anstelle der eigentlichen Funktion saveSession die dann Ihre Methoden definiert. Wenn Sie dann Ihr someVar.saveSession() anrufen, ruft es intern saveSession.restartCheckExpiryDateTimeout() was jetzt verspottet wird.

Ich hätte hinzufügen sollen, dass restartCheckExpiryDateTimeout() eine exportierte Funktion ist. Keine lokal definierte Funktion innerhalb von saveSession() ... (Mein Kommentar oben aktualisiert). In diesem Fall denke ich, dass module.saveSession() das richtige module.restartCheckExpiryDateTimeout() callen sollte, das verspottet wird.

Aber ich werde das, was Sie oben vorgeschlagen haben, trotzdem ausprobieren. Verschieben von saveSession() und restartCheckExpiryDateTimeout() in eine andere Konstante. Vielen Dank

Ich verstehe, dass es nicht im Bereich von saveSession definiert ist. saveSession ist
Aufruf der gleichgeordneten Methode im übergeordneten Gültigkeitsbereich. Ich bin oft darauf gestoßen
und was ich vorgeschlagen habe, hat dafür funktioniert

Am 8. Mai 2017, 20:38 Uhr, schrieb "Huy Pham" [email protected] :

Ich hätte hinzufügen sollen, dass restartCheckExpiryDateTimeout() ein exportiertes ist
Funktion. Keine lokal definierte Funktion in saveSession()...

Ich werde das, was Sie oben vorgeschlagen haben, jedoch ausprobieren. Vielen Dank


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/jest/issues/936#issuecomment-300029003 oder stumm
der Faden
https://github.com/notifications/unsubscribe-auth/AEeBdsmpOOmzvcUHB3D_-Z7MChIzt10Pks5r37WYgaJpZM4IPGAH
.

Ich habe das gerade versucht.. Ich habe das gefunden:

Dies funktioniert NICHT: (dh das ursprüngliche restartCheckExpiryDateTimeout() wird immer noch aufgerufen)

export session = {
   saveSession: () => {
      session.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Dies funktioniert: (dh der Mock restartCheckExpiryDateTimeout() wird stattdessen aufgerufen). Der Unterschied besteht in der Verwendung von function() anstelle der Pfeilform und der Verwendung von this. anstelle von session.

export session = {
   saveSession: function() {
      this.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Es könnte ein Problem mit dem Scherz sein, diese Codes zu transpilieren ....

Versuchen Sie, sie als Klassenobjekt anstelle von Pojo zu exportieren. Ich glaube das
Transpiler hebt die Variablen unterschiedlich hoch. Wir kommen zu einem funktionierenden
Test, versprochen.. Es ist ungefähr ein halbes Jahr her, seit ich an dem Projekt teilgenommen habe
das benutzte scherz, aber ich erinnere mich gut an dieses problem und ich erinnere mich irgendwann
eine Lösung finden.

Am 9. Mai 2017 um 00:53 Uhr schrieb "Huy Pham" [email protected] :

Ich habe das gerade versucht.. Ich habe das gefunden:

Dies funktioniert NICHT: (dh der ursprüngliche restartCheckExpiryDateTimeout() ist
werde immer noch angerufen)

Sitzung exportieren = {
saveSession: () => {
session.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}

Dies funktioniert NICHT: (dh der ursprüngliche restartCheckExpiryDateTimeout() ist
werde immer noch angerufen)

Sitzung exportieren = {
saveSession: function() {
this.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}

Es könnte ein Problem mit dem Scherz sein, diese Codes zu transpilieren ....


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/jest/issues/936#issuecomment-300060975 oder stumm
der Faden
https://github.com/notifications/unsubscribe-auth/AEeBdrRQExycPYiGtvm7qYi5G87w6b6Oks5r3_FlgaJpZM4IPGAH
.

@sorahn gleiches Problem. es6 + babel , Wie verspotte ich?
@cpojer Bedeutet das es6 + babel , export const function xx() {} , viele Funktionen exportieren , Jest hat keine Möglichkeit, eine Funktion in einem Modul (Datei) namens . zu simulieren durch eine andere Funktion im selben Modul (Datei)? Ich teste es, es scheint, ich liege richtig. Nur für das Muster commonjs kann Jest die Funktion erfolgreich verspotten, wie in Ihrem Beispiel.

@ainesophaurus funktioniert nicht.

Modul:

export const getMessage = (num: number): string => {
  return `Her name is ${genName(num)}`;
};

export function genName(num: number): string {
  return 'novaline';
}

Prüfung:

function mockFunctions() {
  const original = require.requireActual('../moduleA');
  return {
    ...original,
    genName: jest.fn(() => 'emilie')
  }
}
jest.mock('../moduleA', () => mockFunctions());
const moduleA = require('../moduleA');

describe('mock function', () => {

  it('t-0', () => {
    expect(jest.isMockFunction(moduleA.genName)).toBeTruthy();
  })

  it('t-1', () => {

    expect(moduleA.genName(1)).toBe('emilie');
    expect(moduleA.genName).toHaveBeenCalled();
    expect(moduleA.genName.mock.calls.length).toBe(1);
    expect(moduleA.getMessage(1)).toBe('Her name is emilie');
    expect(moduleA.genName.mock.calls.length).toBe(2);

  });

});

Testergebnis:

FAIL  jest-examples/__test__/mock-function-0.spec.ts
  ● mock function › t-1

    expect(received).toBe(expected)

    Expected value to be (using ===):
      "Her name is emilie"
    Received:
      "Her name is novaline"

      at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)

  mock function
    ✓ t-0 (1ms)
    ✕ t-1 (22ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        0.215s, estimated 1s

Schau dir meine letzten Kommentare oben an. Speziell der letzte. Ihre
exportierte Methoden rufen die gleichgeordnete Methode mit lokalem Gültigkeitsbereich im Vergleich zur
tatsächliche exportierte Methode (wo sich Ihr Mock befindet)

Am 31. Mai 2017, 02:00 Uhr, schrieb "novaline" [email protected] :

@ainesophaur https://github.com/ainesophur funktioniert nicht.

Modul:

export const getMessage = (num: number): string => {
Rückgabe Her name is ${genName(num)} ;
};
Exportfunktion genName(num: Zahl): string {
'novaline' zurückgeben;
}

Prüfung:

Funktion mockFunctions() {
const original = require.requireActual('../moduleA');
Rückkehr {
...Original,
genName: jest.fn(() => 'emilie')
}
}jest.mock('../moduleA', () => mockFunctions());const moduleA = require('../moduleA');
describe('Mock-Funktion', () => {

es('t-0', () => {
erwarten(jest.isMockFunction(moduleA.genName)).toBeTruthy();
})

es('t-1', () => {

expect(moduleA.genName(1)).toBe('emilie');
expect(moduleA.genName).toHaveBeenCalled();
expect(moduleA.genName.mock.calls.length).toBe(1);
expect(moduleA.getMessage(1)).toBe('Her name is emilie');
expect(moduleA.genName.mock.calls.length).toBe(2);

});

});

Testergebnis:

FAIL jest-examples/__test__/mock-function-0.spec.ts
● Scheinfunktion › t-1

expect(received).toBe(expected)

Expected value to be (using ===):
  "Her name is emilie"
Received:
  "Her name is novaline"

  at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
  at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)

Scheinfunktion
✓ t-0 (1ms)
✕ t-1 (22ms)

Testsuiten: 1 fehlgeschlagen, 1 insgesamt
Tests: 1 nicht bestanden, 1 bestanden, 2 insgesamt
Schnappschüsse: 0 insgesamt
Zeit: 0,215s, geschätzt 1s


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/jest/issues/936#issuecomment-305091749 oder stumm
der Faden
https://github.com/notifications/unsubscribe-auth/AEeBdv6SafXlTtKo3DNeFWhbL6gV9l0Gks5r_QHjgaJpZM4IPGAH
.

@ainesopheur : Ich habe es mit export class Session { } versucht. Und bei mir funktioniert es nicht.

Der einzige Ansatz, der für mich funktioniert, ist in meinem obigen Kommentar: wobei die function Syntax anstelle des Pfeils () => . Hier:

export const session = {
   saveSession: function() {
      this.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Dies ist auf Jest 20.0.3

Was ich mache, ist, einen const-Wrapper für die Funktionen zu erstellen und diesen Wrapper dann zu exportieren (z. B. export const fns). Verwenden Sie dann innerhalb des Moduls fns.functionName, und dann kann ich jest.fn() die Funktion fns.functionName verwenden

Wenn wir eine Mock-Funktion eines benutzerdefinierten Moduls schreiben, die in Typescript geschrieben ist, und wenn wir die Mock-Funktion aufrufen, ist dies die ursprüngliche Funktion, die im Coverage-Bericht behandelt wird, da wir die simulierte Version der Funktion aufrufen.

Ich habe 2 Funktionen, die ursprünglich in den Tests als importiert wurden
import { getCurrentDate, getStartEndDatesForTimeFrame } from ./../_helpers/date';
Wie Sie sehen, ist getStartEndDatesForTimeFrame von getCurrentDate abhängig. Mit dem folgenden Setup funktioniert der getCurrentDate Test gut und verwendet die simulierte Version. Auf der anderen Seite verwendet der getStartEndDatesForTimeFrame Test aus irgendeinem Grund nicht das verspottete getCurrentDate sondern die ursprüngliche Implementierung, sodass mein Test fehlschlägt. Ich habe viele verschiedene Setups ausprobiert (wie Date.now = jest.fn(() => "2017-11-16T20:33:09.071Z"); , konnte es aber nicht zum Laufen bringen. Irgendwelche Ideen?

export const getCurrentDate = () => new Date();
export const getStartEndDatesForTimeFrame = (timeFrame) => {
  ...
  const todayDate = getCurrentDate();
  ...
  switch (timeframe) {
    case TimeFrames.TODAY:
      console.log(todayDate); // this always prints the real value in tests instead of the mocked one
      start = new Date(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate(), 0, 0, 0);
      end = new Date(
        todayDate.getFullYear(),
        todayDate.getMonth(),
        todayDate.getDate(), 23, 59, 59,
      );
      break;
  ...
  return { start: start.toISOString(), end: end.toISOString() }
};
function mockFunctions() {
  const original = require.requireActual('../../_helpers/date');
  return {
    ...original,
    getCurrentDate: jest.fn(() => '2017-11-16T20:33:09.071Z'),
  }
}
jest.mock('../../_helpers/date', () => mockFunctions());
const dateModule = require.requireMock('../../_helpers/date');

describe('getCurrentDate', () => {
  it('returns the mocked date', () => {
    expect(dateModule.getCurrentDate()).
      toBe('2017-11-16T20:33:09.071Z'); // this works well and returns the mocked value
  });
});

describe('getStartEndDatesForTimeFrame', () => {
  it('returns the start and end dates for today', () => {
    expect(dateModule.getStartEndDatesForTimeFrame('today')).toEqual(
      { 'start': '2017-11-15T23:00:00.000Z', 'end': '2017-11-16T22:59:59.000Z' }
    ); // this one uses the original getCurrentDate instead of the mocked one :(
  });
});

Das getStartEndDatesForTimeFrame schlägt also fehl, da es die aktuelle Zeit verwendet und nicht die verspottete.

Ich habe es geschafft, dass es funktioniert, indem ich einem Vorschlag von @ainesopheur gefolgt bin - indem

// imageModel.js
const model = {
  checkIfImageExists,
  getImageUrl,
  generateImagePreview
}
export default model

async function checkIfImageExists(...) {}
async function getImageUrl() {}
async function generateImagePreview() {
  // I am calling it as `model` object's method here, not as a locally scoped function
  return model.getImageUrl(...)
}

// imageModel.test.js
import imageModel from './imageModel'

test('should mock getImageUrl called within the same file', async () => {
  imageModel.getImageUrl = jest.fn().mockReturnValueOnce(Promise.resolve())

  await imageModel.generateImagePreview()

  expect(imageModel.getImageUrl).toBeCalled()
})

@miluoshi Nur so konnte ich es auch. Gibt es einen Leistungsverlust oder ähnliches, wenn wir diese Methode verwenden? Es scheint "falsch", den Code zu ändern, damit Sie ihn testen können.

Ich hätte wirklich gerne eine Möglichkeit, einfach zu schreiben:
jest.mock('src/folder/file.func, () => {return 'whatever i want'})

Schlüsselstück hier ist das .func

@miluoshi @Rdlenke Wenn Ihr Code aus benannten Exporten besteht, können Sie auch import * as model und dann model.generateImagePreview = jest.fn(() => Promise.resolve); überschreiben

Wie würden Sie das mit sinon testen? Wie bereits erwähnt (siehe https://github.com/facebook/jest/issues/936#issuecomment-214939935), macht es die Funktionsweise von ESM unmöglich, func2 innerhalb von func1 zu verspotten, also Ich würde es nicht unbedingt als Basic bezeichnen.

Vielleicht könnte eine Babel-Mod geschrieben werden, die irgendwelche "testImport"-Funktionen einliest
und schreibt den Code um, um die Funktionen im Modul vor dem
Probelauf?

Am Montag, den 18. Dezember 2017 um 17:00 Uhr schrieb Jim Moody [email protected] :

Du hast recht @SimenB https://github.com/simenb , ich hatte etwas geändert
in meinem test zwischendurch auf Sinon umgestiegen, was wie es aussieht hat es bestanden.
Als ich das rückgängig gemacht habe, funktioniert es immer noch nicht. Ich denke das ist kein Problem
das ist gelöst.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/jest/issues/936#issuecomment-352488400 oder stumm
der Faden
https://github.com/notifications/unsubscribe-auth/AQRY9a5-s2_bjCWKNw5WiAJW-JeBf8W3ks5tBpoygaJpZM4IPGAH
.

--

Darren Cresswell
Vertragsentwickler | Entwickler Limited
E-Mail: [email protected]
Telefon:
Website: http://www.develer.co.uk

Bitte denken Sie an die Umwelt, bevor Sie diese E-Mail drucken
WARNUNG: Computerviren können per E-Mail übertragen werden. Der Empfänger
sollten diese E-Mail und alle Anhänge auf Viren überprüfen.
Developer Limited übernimmt keine Haftung für Schäden, die durch Viren verursacht werden
mit dieser E-Mail übermittelt. E-Mail-Versand kann nicht garantiert werden
sicher oder fehlerfrei, da Informationen abgefangen, beschädigt, verloren,
zerstört werden, verspätet oder unvollständig ankommen oder Viren enthalten. Der Absender
übernimmt daher keine Haftung für Fehler oder Auslassungen in den
Inhalte dieser Nachricht, die durch den E-Mail-Versand entstehen.

WARNUNG : Obwohl Developer Limited angemessene Vorkehrungen getroffen hat, um
Stellen Sie sicher, dass diese E-Mail keine Viren enthält. Das Unternehmen kann diese E-Mail nicht akzeptieren
Verantwortung für Verluste oder Schäden, die sich aus der Verwendung dieser E-Mail ergeben oder
Anhänge.

Developer Limited ist eine in England und Wales registrierte Gesellschaft mit beschränkter Haftung. |
Firmenbuchnummer 09817616 | Sitz: SUITE 1 SECOND
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, VEREINIGTES KÖNIGREICH, BH7 7DU

danke @ainesopheur für die

Falls jemand ein nicht asynchrones Arbeitsbeispiel nützlich findet, hier ist meins:

//reportError.js
const functions = {};

functions._reportToServer = (error, auxData) => {
  // do some stuff
};

functions.report = (error, auxData = {}) => {
  if (shouldReportToServer()) {
    functions._reportToServer(error, auxData);
  }
};
export default functions;

// reportError.jest.js
import reportError from 'app/common/redux/lib/reportError';
reportError._reportToServer = jest.fn();

describe('test reportError', () => {
  it('reports ERROR to server as as error', () => {
   reportError.report(new Error('fml'), {});
    expect(reportError._reportToServer).toHaveBeenCalledTimes(1);
  });
});

@jim-moody Wenn ich das Problem richtig verstanden habe, sollte dies für Ihr Beispiel funktionieren:

const spyOnExampleFunc2 = jest.spyOn(example, 'func2');
example.func1();
expect(spyOnExampleFunc2).toBeCalled();

(dies funktioniert _nur_, wenn die Funktionen als const exportiert werden, wie in Ihrem Beispiel)

@dinvlad mein Held!

In Anlehnung an die Antwort von @dinvlad denke ich, dass das Hinzufügen, Zeigen oder Verknüpfen der folgenden nachgemachten Dokumente auf der Seite des Scherzobjekts mit der Scheinfunktionen eine Verbesserung der Scherzdokumente zum Verspotten sein könnte:

  • jest.isMockFunction(fn)
  • jest.genMockFromModule(moduleName)
  • jest.mock (Modulname, Fabrik, Optionen)
  • jest.unmock(moduleName)
  • jest.doMock(Modulname, Fabrik, Optionen)
  • jest.dontMock(moduleName)

Mein Anwendungsfall ist, dass ich als neuer Benutzer von jest etwas mocha + sinon.js-Code zu jest migriere. Ich hatte bereits Spione und Erwartungen, also dachte ich, es wäre einfach. Aber nachdem ich diesen Thread gelesen und die Jest-Dokumente zu Mock-Funktionen gelesen hatte, hatte ich den Eindruck, dass die Verwendung von Jest auf diese Weise möglicherweise eine Neufassung meiner Tests oder ein detailliertes Verständnis von ESM oder Babel erfordert ... oder andere Verwirrung.

Danke für Jest - es macht meine Tests einfacher zu schreiben/verstehen und schneller auszuführen. :)

PR zur Klärung der Dokumente ist sehr willkommen! 🙂

Um nur bestimmte Module mit der ES- Modulsyntax zu verspotten, können Sie

import { foo } from './example';

jest.mock('./example', () => (
  ...require.requireActual('./example'),
  foo: jest.fn()
));

test('foo should be a mock function', () => {
  expect(foo('mocked!')).toHaveBeenCalledWith('mocked!');
});

Fühlt sich umgekehrt an, aber es ist der einfachste Weg, den ich kenne. Huttipp an @joshjg.

Ich habe mich irgendwo in der langen Diskussion verloren, ich hätte nur eine Frage, gibt es trotzdem zu testen, ob die eigentliche Implementierung der Funktion aufgerufen wird?

Von dem, was ich verstehe, wenn ich verwenden muß jest.fn() wird die ursprüngliche Funktion außer Kraft setzen, aber wenn ich es nicht verwenden, würde die Konsole gibt Fehler mir sagen , dass es eine sein muss jest.fn() function or a spy

Ich versuche, eine Middleware zu testen, bei der die Anforderung weitergegeben wird. Wenn ich sie also verspotte, geht die gesamte Logik verloren und die Daten werden nicht an die nächste Middleware weitergegeben. Wenn ich es nicht verspotte, indem ich es importiere, kann ich dann trotzdem testen, ob diese Funktion aufgerufen wurde?

Du kannst vielleicht jest.spyOn ? Standardmäßig ruft es die zugrunde liegende Funktion auf

Danke für die Hilfe, ich habe es versucht, aber der Test deutet darauf hin, dass es nie aufgerufen wurde, obwohl es aufgerufen wurde, weil ich die console.log abgelegt habe und es gedruckt wurde

Testdatei

import errorHandler from '../../controller/errorHandler'

describe('auth test', () => {
  describe('test error: ', () => {
    const test1 = jest.spyOn(errorHandler, 'handleClientError')
    test('should return 400', (done) => {
      request(app)
      .post('/auth/error')
      .then((res) => {
        expect(res.statusCode).toBe(400)
        expect(test1).toBeCalled()
        done()
      })
    })

errorHandler

module.exports = {
  handleClientError () {
    console.log('err')
  }
}

Konsole

console.log src/controller/errorHandler.js:10
      err

  ● auth test › test error:  ›  should return 400

    expect(jest.fn()).toBeCalled()

    Expected mock function to have been called.

      18 |         expect(res.statusCode).toBe(400)
    > 19 |         expect(test1).toBeCalled()
      20 |         done()
      21 |       })
      22 |     })

Heißt die Funktion handleClientError oder logError ?

@WangHansen Aus deinem Beispiel sollte dein Code expect(errorHandler.handleClientError).toBeCalled() // > true

@WangHansen könntest du .mockImplementation() zu deinem jest.spyOn() hinzufügen? Als jemand, der von Jasmine kommt, fand ich diesen Tipp entscheidend, um die gleiche Funktionalität wie Jasmines Spione zu erreichen. Z.B

const mockModuleFunction = jest
  .spyOn(module, 'function')
  .mockImplementation(() => 'hello');
...
expect(mockModuleFunction.mock).toBeCalled();

Wenn Sie mockImplementation() _nicht_ verwenden, dann erzeugt jest.spyOn() ein Objekt, das _kein_ ein Schein (afaiu) ist und sich tatsächlich der nativen Implementierung unterwirft. Wenn Sie die native Implementierung beibehalten müssen, lohnt es sich möglicherweise, diese zu verwenden

const moduleFunction = module.function;
jest.spyOn(module, 'function').mockImplementation(moduleFunction);
...

Ich bin mir nicht sicher, ob dies notwendig ist, aber ziemlich sicher, dass es funktionieren sollte.

Zurück zur ursprünglichen Anfrage...

Könnten Sie nicht einfach einen Proxy um einen Import * wickeln? z.B

import * als Test aus './myfile.js';

const-Handler = {
/** Intercepts: Eigenschaften abrufen */
get(Ziel, PropKey, Empfänger) {
console.log( GET ${propKey} );
Rückkehr 123;
},

/** Intercepts: checking whether properties exist */
has(target, propKey) {
    console.log(`HAS ${propKey}`);
    return true;
}};

const p = neuer Proxy (Test);

Am Dienstag, 30. Januar 2018 um 16:24 Uhr, Denis Loginov [email protected]
schrieb:

@WangHansen https://github.com/wanghansen könnten Sie hinzufügen
.mockImplementation() zu Ihrem jest.spyOn()? Als jemand, der von kommt
Jasmine, ich fand diesen Tipp entscheidend, um die gleiche Funktionalität zu erreichen wie
Jasmins Spione. Z.B

const mockModuleFunction = jest
.spyOn(modul, 'funktion')
.mockImplementation(() => 'Hallo');...expect(mockModuleFunction.mock).toBeCalled();

Wenn Sie mockImplementation() nicht verwenden, erzeugt jest.spyOn() ein
Objekt, das kein Schein ist (afaiu) und es sich tatsächlich dem Eingeborenen unterwirft
Implementierung. Wenn Sie die native Implementierung beibehalten müssen, ist es vielleicht
es lohnt sich zu verwenden

const moduleFunction = module.function;jest.spyOn(module, 'function').mockImplementation(moduleFunction);...

Ich bin mir nicht sicher, ob dies notwendig ist, aber ziemlich sicher, dass es funktionieren sollte .


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/jest/issues/936#issuecomment-361648414 oder stumm
der Faden
https://github.com/notifications/unsubscribe-auth/AQRY9VXyHNYatwOOY6EV637WGQH9k5Plks5tP0I9gaJpZM4IPGAH
.

--

Darren Cresswell
Vertragsentwickler | Entwickler Limited
E-Mail: [email protected]
Telefon:
Website: http://www.develer.co.uk

Bitte denken Sie an die Umwelt, bevor Sie diese E-Mail drucken
WARNUNG: Computerviren können per E-Mail übertragen werden. Der Empfänger
sollten diese E-Mail und alle Anhänge auf Viren überprüfen.
Developer Limited übernimmt keine Haftung für Schäden, die durch Viren verursacht werden
mit dieser E-Mail übermittelt. E-Mail-Versand kann nicht garantiert werden
sicher oder fehlerfrei, da Informationen abgefangen, beschädigt, verloren,
zerstört werden, verspätet oder unvollständig ankommen oder Viren enthalten. Der Absender
übernimmt daher keine Haftung für Fehler oder Auslassungen in den
Inhalte dieser Nachricht, die durch den E-Mail-Versand entstehen.

WARNUNG : Obwohl Developer Limited angemessene Vorkehrungen getroffen hat, um
Stellen Sie sicher, dass diese E-Mail keine Viren enthält. Das Unternehmen kann diese E-Mail nicht akzeptieren
Verantwortung für Verluste oder Schäden, die sich aus der Verwendung dieser E-Mail ergeben oder
Anhänge.

Developer Limited ist eine in England und Wales registrierte Gesellschaft mit beschränkter Haftung. |
Firmenbuchnummer 09817616 | Sitz: SUITE 1 SECOND
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, VEREINIGTES KÖNIGREICH, BH7 7DU

@dinvlad @iampeterbanjo @SimenB Nochmals vielen Dank für Ihre Hilfe, aber leider hat keine der von Ihnen vorgeschlagenen Methoden funktioniert. Ich frage mich, ob es daran liegt, dass die Funktion in der Form next(err) aufgerufen wird. Die Logik ist, wenn eine Anfrage fehlgeschlagen ist, wird sie an errorHandler indem return next(err) . Sicherlich wird die Funktion aufgerufen, denn als ich console.log hinzugefügt habe, wird sie gedruckt. Aber die Tests deuten darauf hin, dass es nie aufgerufen wird

@dinvlad Ich habe deine Methode ausprobiert, sie hat nicht funktioniert, aber trotzdem danke für deine Hilfe. Ich habe mich nur gefragt, warum Sie mockImplementation aufrufen müssen, laut dem offiziellen Dokument zu jest.spyOn , rufen Sie es nur auf, wenn Sie die ursprüngliche Funktion überschreiben möchten.

@WangHansen ja, du hast Recht, dass es nur benötigt wird, wenn man die ursprüngliche Methode überschreiben möchte. Ich warf nur eine Idee für diese Situationen.

Ein Grund, warum es für Sie fehlgeschlagen sein könnte, ist die Asynchronität. Wenn Ihre Methode Callbacks und/oder Promises (oder async/await) verwendet, müssen Sie sicherstellen, dass Ihre Erwartungen tatsächlich ausgeführt werden, bevor Ihre Testmethode beendet wird. Es gibt eine spezielle Methode expect.assertions(N) , um das zu behaupten. Stellen Sie außerdem sicher, dass Ihre Erwartung erst ausgeführt wird, nachdem der Code in Callbacks/Promises aufgerufen wurde. Ich bin sicher, Sie haben sich das angesehen, aber nur als Referenz, https://facebook.github.io/jest/docs/en/asynchronous.html

Es ist bedauerlich, dass das Verspotten intern verwendeter fns, wie es @seibelj beschreibt, nicht möglich ist, ohne das Modul impl zu ändern.

Tests sollten meiner Meinung nach nicht die Implementierung von Geschäftslogik bestimmen 😕 (zumindest nicht in diesem Maße)

Gibt es Pläne, ein solches Verhalten im Scherz umzusetzen?

Hi,
Etwas spät zur ganzen Diskussion, aber die ganze Diskussion durchgelesen - das ist mir immer noch nicht gelungen.
Die vielversprechende Lösung von @greypants hat bei mir nicht wirklich funktioniert, da sie immer noch die ursprüngliche Funktion aufruft.
Hat sich seit Beginn dieser Diskussion etwas geändert? Verpasse ich etwas?

Ich habe die Lösung von

import Module from './module'
import { getJSON } from './helpers'

jest.mock('./helpers', () =>
  Object.assign(require.requireActual('./helpers'), {
    getJSON: jest.fn()
  })
)

test('should use the mock', async () => {
  getJSON.mockResolvedValue('foo')
  await expect(module().someAsyncThingThatUsesGetJSON()).resolves.toEqual('foo')
})

test('should use the actual module', () => {
  expect(module().someFunctionThatUsesAHelper()).toBe(true)
})

Das fühlt sich immer noch irgendwie hackig an und die Dokumente waren nicht so hilfreich. Ich bin Jest und dem Team dahinter dankbar, aber es scheint ein häufiger Anwendungsfall zu sein, der zumindest eine "offizielle" Lösung haben sollte.

@sarahdayan , und wen es auch immer interessieren mag -
Am Ende habe ich babel-plugin-rewire verwendet.
Ich habe eine Weile gebraucht, um dieses Plugin zu finden, aber es war eine Lösung, die schlüssig genug war, um sich nicht hackig zu fühlen.

In den meisten Fällen möchten wir eine oder mehrere Funktionen eines Moduls nicht verspotten. Wenn Sie das Spottsystem von global jest verwenden, können Sie es mit genMockFromModule und requireActual . Hier ist das Beispiel:

//  __mocks__/myModule.js
const moduleMock = jest.genMockFromModule('./../myModule');

// in most cases we don't want to mock `someFunction`
moduleMock.someFunction = jest.fn(
  (...args) => require.requireActual('./../myModule').someFunction(...args)
)

module.exports = moduleMock;

Diese Lösung ermöglicht die Verwendung von Mocks für andere Funktionen aus dem Modul, die Verwendung der ursprünglichen someFunction Implementierung, wenn das gesamte Modul verspottet wird, und ermöglicht auch das Verspotten der someFunction Funktion mit mockImplementationOnce oder mockImplementation API.

Ich habe alle Gespräche oben gelesen, aber keine Lösung funktioniert für mich.
Wenn Sie immer noch nach einer Lösung für diesen Testfall suchen, lautet die Antwort babel-plugin-rewire . Dieses Plugin soll den von uns besprochenen Szenariofall lösen.
Bitte werfen Sie einen Blick auf diese Bibliothek, Sie werden mir später danken.

Also um den ganzen Thread oben zusammenzufassen:

  1. Angenommen, Sie haben ein Modul m mit den Funktionen f , g und h wobei g und h f aufrufen f verspotten, damit g und h den Mock anstelle des echten f . Leider ist dies nicht direkt f wird wie von cpojer beschrieben immer über exports aufgerufen . Dies ist unmöglich, wenn Ihr Modul die ES6-Import-/Export-Syntax in TypeScript verwendet (und ich vermute, dass das auch in Babel der Fall ist).
  2. Nehmen wir jedoch an, wir verschieben f in ein anderes Modul m2 . Dann hat m eine Anweisung wie import {f} from 'm2' und wenn g und h f anrufen, rufen sie tatsächlich m2.f wobei m2 = require('./m2') (so sieht die Babel/TypeScript-Übersetzung aus). Dies macht es möglich, f zuverlässig zu verspotten, wie es von greypants beschrieben wird . Mit anderen Worten, Sie können Aufrufe nur dann zuverlässig vortäuschen, wenn sie eine Modulgrenze überschreiten . Hinweis: Die Lösung von greypants erzeugt jetzt diese Fehlermeldung: "Die Modulfabrik von jest.mock() darf keine Variablen außerhalb des Gültigkeitsbereichs referenzieren - Ungültiger Variablenzugriff: __assign". Ich vermute, dass dies ein Fehler in Jest ist; Verwenden Sie als Problemumgehung Object.assign wie unten gezeigt.
  3. Wenn Sie jedoch, anstatt eine oder zwei Funktionen zu verspotten, alles außer einer oder zwei Funktionen verspotten möchten, verwenden Sie Code wie den von darkowic .

Beispiel für (2):

~~~js
// Modul m.js
importieren {f} von './m2'
Exportfunktion g() { return 'f zurückgegeben ' + f(); };

// Modul m2.js
Exportfunktion f() { return 'das echte f'; }

// test.js
import * als m aus './m'

jest.mock('./m2', () => Object.assign(
require.requireActual('./m2'), {
f: jest.fn().mockReturnValue('MOCK')
}));

test('verspottet', () => {
Expect(mg()).toEqual('f hat MOCK zurückgegeben');
});
~~~

Beim Testen stieß ich auf #2649: Das Aufrufen von jest.mock innerhalb eines Tests hat keine Auswirkung, und wenn Sie es im globalen Bereich aufrufen, können Sie vor anderen Tests nicht unmock aufrufen. Sehr nervig.

Vielen Dank!! @sarahdayan
Habe schon eine Weile danach gesucht

Wenn die Unterlagen fehlen, sind PRs immer willkommen, um diese zu klären 🙂

Hallo allerseits!

Ich spielte ein wenig herum und hatte folgende Idee, um dieses Problem zu lösen:

  • ein Modul verspotten, aber das verspottete Modul hat das Originalmodul in der Prototypkette.
  • Bereitstellung einer Methode zum Hinzufügen von Eigenschaften zum simulierten Modul (die die Eigenschaften des Prototyps überschreibt)
  • Stellen Sie auch eine Methode bereit, um die Eigenschaften aus dem simulierten Modul zu entfernen (um die aus dem Prototyp wieder zu verwenden).
// m1.js
export const f = () => "original f"

// __mocks__/m1.js
const originalM1 = require.requireActual("../m1");
// set the original as a prototype
let mockModule: any = Object.create(originalM1);
const __setMock = (name, value) => {
  mockModule[name] = value;
};
const __removeMock = (name) => {
  Reflect.deleteProperty(mockModule, name);
};
// enhance the mocked module to allow overriding original exports
module.exports = Object.assign(mockModule, { __setMock, __removeMock });


// m1.test.js
import { f } from "./m1";

jest.mock("./m1");

test("mocking stuff", () => {
  // here nothing is mocked - the original module is used
  expect(f()).toBe("original f");

  // override the export f of the module
  require("./m1").__setMock("f", () => "mocked f");
  expect(f()).toBe("mocked f");

  // set it back to the original
  require("./m1").__removeMock("f");
  expect(f()).toBe("original f");

  //override with another value
  require("./m1").__setMock("f", () => "another mocked f");
  expect(f()).toBe("another mocked f");
});

Meine 2 Cent:

Ich habe viele Lösungen getestet (wenn nicht alle) und die einzige, die für mich funktioniert hat, ist diese (Jest 23):

// importedModule.js
export const funcB = () => {
  return "not mocked";
};
export const funcA = () => {
  return mockProxy.funcB(); // this works but it's soooo hacky
};

export const mockProxy = {
  funcB
};

// moduleToTest.js
import { funcA } from "./moduleImported.js";

export default function() {
  return funcA();
}

// test
let moduleImported = require("./moduleImported.js");
moduleImported.mockProxy.funcB = jest.fn(() => "mocked");
const funcToTest = require("./moduleToTest.js").default; // or import

it("should return mocked", function() {
  expect(funcToTest()).toBe("mocked");
});

Ich bin zu dem Schluss gekommen, dass es keine gute Idee ist, dies zu versuchen, weil:

  • test.js weiß zu viel über die Implementierungsdetails von importedModule.js
  • die Lösung ist zu zerbrechlich und niemand wird den Zweck der mockProxy verstehen, wenn man sich importedModule.js ansieht

Hat jemand dafür eine funktionierende Lösung gefunden?

Ich benutze:

"jest": "^21.2.1",
"jest-cli": "^21.2.1",

@jamesone hast du das gelesen https://github.com/facebook/jest/issues/936#issuecomment -410080252 ?

Das hat bei mir funktioniert, basierend auf der Antwort von @thomaskempel :

In meinem Fall wollte ich eine Abhängigkeit in node_modules nachahmen, nennen wir es "shared-components". Es exportiert eine Reihe von benannten Komponenten. Ich wollte nur ein paar dieser genannten Exporte verspotten und den Rest als echt belassen.

Also in __mocks__/shared-components.js ich:

const original = require.requireActual('shared-components');

module.exports = {
...original,
moduleNameToOverride: jest.fn().mockImplementation(() => {
      return 'whatever';
    }),
}

In meinem Fall habe ich die Implementierungen herausgestochen. Hoffentlich hilft das in Zukunft jemandem.

Ich hatte vor kurzem das gleiche Problem, die Unterhaltung in diesem Thread hat mir geholfen, besser zu verstehen, und ich habe meine Ergebnisse hier zusammengefasst https://medium.com/@DavideRama/mock -spy-exported-functions-within-a-single- Modul-im-Jest-cdf2b61af642

Inspiriert von @qwerties Lösung

mockGet = jest.fn()
jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  get: mockGet
}))

@MajorBreakfast , das mit React.lazy funktioniert?

const mockLazy = jest.fn();

jest.mock('React', () => ({
    ...jest.requireActual('React'),
    lazy: mockLazy
}));

Ich bekomme immer noch ReferenceError: React is not defined .

So simulieren Sie einen unabhängigen Exportfunktionsbaustein:
Exportieren Sie alle Einzelfunktionen, die zum Standardexport gehören, aus der Datei.

Beispiel:
dataDao.js

Funktion getData()
Funktion setData()
Funktion deleteData()
export {getData, setData, deleteData}

Jetzt können Sie alle Funktionen aus der Datei per Standardbenennung in Ihren Scherztest importieren;

dataDao.spec.js

* als dataDao aus '../dataDao' importieren;
// Die Module ausspionieren, die beim Import auf den zugewiesenen Standardnamen verweisen
jest.spyOn(dataDao, 'getData')
jest.spyOn(dataDao, 'setData')
jest.spyOn(dataDao, 'deleteData')

@vchinthakunta , das mag funktionieren, aber es sieht aus wie eine Verletzung eines Hauptzwecks der Export-/Import-Syntax: andere Module können bestimmte Methoden oder Datenfelder nicht mehr über importieren

import { justThisThing } from 'someModule';

Übersehe ich da was?

@MajorBreakfast , das mit React.lazy funktioniert?

const mockLazy = jest.fn();

jest.mock('React', () => ({
    ...jest.requireActual('React'),
    lazy: mockLazy
}));

Ich bekomme immer noch ReferenceError: React is not defined .

Ich denke, 'React' sollte hier klein geschrieben werden, da es auf den Import verweist.

jest.mock('react'...)

Ich habe meinen Code mit dem folgenden funktionieren lassen, was einfacher ist als andere Lösungen, die ich hier gesehen habe. Sie müssen nicht require oder default Exporte einrichten.

Helfer/navigation.js

export const initHeader = () => {
    // initialise header
    ...
}

...

export const anotherHelperFunction = () => {
    // do stuff
    ...
}

Komponente, die navigation.js verwendet

import { initHeader } from '../helpers/navigation';

jest.mock('../helpers/navigation');

...

describe('Test component', () => {

    it('should reinitialise header', () => {
        const mockHeaderInit = jest.fn();
        initHeader.mockImplementation(mockHeaderInit);

        const component = mountComponent(mockProps);
        component.simulate('click');

        expect(mockHeaderInit).toBeCalled();
    }
}
mockGet = jest.fn()
jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  get: mockGet
}))
ReferenceError: mockGet is not defined

       4 | const mockGet = jest.fn();
       5 | jest.mock('./browserStorage', () => ({
       6 |   ...jest.requireActual('./browserStorage'),
    >  7 |   get: mockGet,
         |        ^
       8 | }));

jest.mock ist hochgezogen, verwenden Sie doMock oder

jest.mock('./browserStorage', () => ({
  ...jest.requireActual('./browserStorage'),
  get: jest.fn(),
}));

const {get: mockGet} = require('./browserStorage');

Dies scheint ein häufiges Problem zu sein. Steht dazu etwas in den Scherzdokumenten?

Gibt es also eine Lösung, um eine Funktion innerhalb desselben Moduls zu verspotten?

Die folgende Methode hat bei mir funktioniert, der Trick besteht darin, die vorgetäuschte Funktion am Ende des Tests wieder zurückzusetzen.
Dieses Beispiel verspottet die Verifizierungsfunktion des jsonwebtoken-Moduls.

  test('perform my test', async () => {
    // save the real jwt.verify function
    const verify = jwt.verify
    // mock it.
    jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
    // do the test
    ...
    // set the real function back.
    jwt.verify = verify
  })

Die folgende Methode hat bei mir funktioniert, der Trick besteht darin, die vorgetäuschte Funktion am Ende des Tests wieder zurückzusetzen.
Dieses Beispiel verspottet die Verifizierungsfunktion des jsonwebtoken-Moduls.

  test('perform my test', async () => {
    // save the real jwt.verify function
    const verify = jwt.verify
    // mock it.
    jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
    // do the test
    ...
    // set the real function back.
    jwt.verify = verify
  })

wo verwendest du die const verify ? Wie auch immer, dies funktioniert nur, wenn Ihre verspottete Funktion keine exportierte const-Funktion ist

Gibt es also eine Lösung, um eine Funktion _innerhalb desselben Moduls_ zu verspotten?

Nach langem Suchen besteht die Lösung für dieses Problem darin, Ihre Exporte in einem einzigen Objekt zu speichern, auf das Ihre Funktion und Ihre Mocks verweisen können. Die folgenden Artikel kamen alle zu demselben Konsens.

https://github.com/facebook/jest/issues/936#issuecomment -438975674
https://medium.com/@qjli/how-to-mock-specific-module-function-in-jest-715e39a391f4
https://luetkemj.github.io/170421/mocking-modules-in-jest

Es überrascht mich, dass niemand eine andere Lösung erwähnt hat, die, wenn sie für Ihren Code praktikabel ist, all diese Probleme vollständig beseitigt und das Testen des gewünschten Codes sehr, sehr einfach macht:

_Bewegen Sie die einzelne Funktion, die Sie nachahmen möchten, in ein eigenes Modul._

Ernsthaft. Wenn Ihr Modul so geschrieben ist, dass Sie seine inneren Teile getrennt von anderen inneren Teilen testen müssen, dann verstößt Ihre Klasse mit ziemlicher Sicherheit gegen das Prinzip der Einzelverantwortung (ja, es ist keine echte Klasse, aber das Modul funktioniert wie eine Klasse, Module, die ein Einheitscontainer von Code sind). Teilen Sie diesen Sauger auf, und boom, Sie können sich über die Anforderungen lustig machen.

Wenn die verspottete Funktion auf einer Reihe von privaten Zuständen beruht, ist dies immer noch kein guter Grund, Ihr Modul nicht irgendwie aufzuteilen. Allein die Tatsache, dass es auf einem Haufen interner Zustände beruht, impliziert für mich, dass die Anliegen des Moduls nicht klar durchdacht sind. Vielleicht gibt es sogar noch ein drittes Modul auszugliedern, das eine Art Datenklasse oder DTO darstellt, die als Argument übergeben werden kann.

Exportieren Sie auch Funktionen nur zum Testen, die sonst privat wären? Warum ruft externer Code die simulierte Funktion direkt auf, aber auch die anderen Funktionen, die sie selbst aufrufen müssen? Ich wette, es gibt hier eine Art Verbindungsabbruch. Vielleicht muss die verspottete Funktion bleiben, aber alle Funktionen müssen in zwei Hälften geteilt werden, wobei die Hälfte entfernt wird und in ein anderes Modul geht. Du hast die Idee.

Wenn das Testen sehr schwierig wird, ist dies fast immer ein Zeichen dafür, dass ein Refactoring erforderlich ist...

Kein Testspiel erforderlich:

const tomfoolery = require('tomfoolery'); // no longer required

Nachdem ich diesen Thread gelesen und die vorgeschlagenen Lösungen getestet habe, kann ich das immer noch nicht zum Laufen bringen. Von dem, was ich gelesen habe, haben einige Leute dies geschafft, aber ich kann nicht herausfinden, wie.

Könnte mir jemand den Code sagen, den ich dem folgenden Beispiel hinzufügen muss, damit die Tests bestehen?

// a.js
export const foo = () => 'foo-' + bar()
export const bar = () => 'bar'
// a.test.js
import {
  foo,
  bar
} from './a'

describe('foo', () => {
  it('should return foo-MOCKED_BAR', () => {
    expect(foo()).toBe('foo-MOCKED_BAR')
  })

  it('should have the mocked implementation of bar', () => {
    expect(bar()).toBe('MOCKED_BAR')
  })
})

describe('bar', () => {
  it('should have the original implementation of bar', () => {
    expect(bar()).toBe('bar')
  })
})

describe('foo and bar together', () => {
  it('should have the original implementation of both', () => {
    expect(foo()).toBe('foo-bar')
  })
})

Vielen Dank!

Ich wollte nur eine einzelne lodash-Methode wie lodash.random verspotten und konnte dies leicht tun mit:

module.js

const lodash = require('lodash');

module.exports = function() {
  return lodash.random();
}

test.js

const lodash = require('lodash');
const module = require('./module.js);

it('mocks lodash', () => {
    jest.spyOn(lodash, 'random').mockImplementationOnce(() => {
      return 2;
    });

    expect(module()).toEqual(2)
});

Ich hoffe, das hilft :)

Etwas, das für unser Team, das mit Typoskript arbeitet, funktioniert hat, war, ein const zu erstellen, das wir exportieren, anstatt die Funktion direkt zu exportieren.
Funktioniert nicht:
export function doSomething(a, b) {}
Arbeiten:
export const doSomething = function (a, b) {}

Ich habe den Export wie bei @arbielsk geändert, es funktioniert! Aber ich weiß nicht, was der Unterschied zwischen zwei Arten von Export ist...

@dgrcode Haben Sie jemals eine Lösung für Ihr Beispiel gefunden? Soweit ich das beurteilen kann, wird das, was Sie versuchen, nicht durch Spott über Jest unterstützt. Insbesondere denke ich foo und bar im selben Modul und daher kann die Ansicht von bar foo von bar nicht verspottet werden.

Ich glaube, Ihre Optionen sind entweder:
1) Reorganisieren Sie Ihren Code so, dass foo ein Modul importiert, das bar
2) Verwenden Sie babel-plugin-rewire

Bitte korrigiert mich, wenn ich Missverständnisse habe!

Ich hatte eine etwas andere Anforderung: Ich wollte ein ganzes Modul _außer_ für eine Funktion verspotten. Mit der Lösung von @MajorBreakfast als Ausgangspunkt bin ich auf Folgendes gekommen:

jest.mock('my-module', () => ({
  ...jest.genMockFromModule('my-module'),
  myFunction: jest.requireActual('my-module').myFunction
}))

@dgrcode Haben Sie jemals eine Lösung für Ihr Beispiel gefunden? Soweit ich das beurteilen kann, wird das, was Sie versuchen, nicht durch Spott über Jest unterstützt. Insbesondere _glaube_ ich, dass das Mock-In im Grunde genommen das Umverdrahten von Importen ist, so dass externe Ansichten des Moduls die verspotteten Methoden sehen. In Ihrem Beispiel befinden sich jedoch foo und bar im selben Modul und daher kann die Ansicht von bar foo von bar nicht verspottet werden.

Ich glaube, Ihre Optionen sind entweder:

  1. Ordnen Sie Ihren Code neu, sodass foo ein Modul importiert, das bar
  2. Verwenden Sie babel-plugin-rewire

Bitte korrigiert mich, wenn ich Missverständnisse habe!

Das war eigentlich mein Fall
Mein Verständnis, wie Module verspottet werden können, war völlig durcheinander

Grundsätzlich
Wenn 2 Funktionen im selben Modul sind und sich gegenseitig aufrufen

Wenn foo bar anruft

function bar() {
  return 'some-result'
}

function foo(){
  return bar()  // <-- the function I want to mock 
}

Füge bar in eine neue Datei ein (um verspottet zu werden)

Ich habe die Methode bar in eine neue Datei verschoben und konnte jetzt viele der obigen Beispiele verwenden
Großen Dank an @yoni-abtech, der mir das verständlich gemacht hat

So wie ich es sehe, nachdem ich diesen ganzen Thread gelesen und immer wieder getestet habe, gibt es 3 Möglichkeiten....

Option 1 - Deklarieren Sie alle Funktionen mit const

Dies erfordert, dass Sie die Verwendung von Funktionsausdrücken über Deklarationen vorschreiben. Glücklicherweise hat Ihnen die func-style eslint-Regel den Rücken gekehrt.

Mit export const können Sie spyOn Funktionen verwenden, die von anderen Funktionen innerhalb des _same_ Moduls verwendet werden.

// hello.js
export const message = () => {
  return 'Hello world';
}

export const foo = () => {
  return message();
}
// hello.test.js
import * as testModule from './hello.js';

describe('test spyon with function expressions', function () {
  afterAll(() => {
    jest.restoreAllMocks();
  });
  it('should NOT mock message in foo', function () {
    const actual = testModule.foo();

    expect(actual).toBe('Hello world');
  });

  it('should mock message in foo', function () {
    jest.spyOn(testModule, 'message').mockReturnValue('my message');

    const actual = testModule.foo();

    expect(actual).toBe('my message');
    expect(testModule.message).toHaveBeenCalledTimes(1);
  });
});

Option 2 - Verwenden Sie das rewire Babel-Plugin

Wenn Sie die Verwendung von Funktionsausdrücken (dh die Verwendung von const ) nicht vorschreiben möchten, könnte dies ein guter Ansatz sein.

Auf diese Weise können Sie Funktionen aus demselben Modul _neu verdrahten_ (auch bekannt als Mock). Ich könnte mir vorstellen, dass der Code wie folgt aussieht, habe ihn aber nicht getestet. Auch aus ihren Dokumenten sieht es so aus, als könnten Sie Funktionen im selben Modul neu verdrahten, die nicht einmal aus dem Modul exportiert werden 👍, stellen Sie sich das folgende Beispiel vor, ohne die Funktion message zu exportieren.

Beispiel:

// hello.js
export function message() {
  return 'Hello world';
}

export function foo() {
  return message();
}
// hello.test.js
import * as testModule from './hello.js';

describe('test rewire api', function() {
  it('should NOT mock message in foo', function () {
    const actual = testModule.foo();

    expect(actual).toBe('Hello world');
  });

  it('should mock message in foo', function () {
    testModule.__RewireAPI__.__Rewire__('message', jest.fn().mockReturnValue('my message'));

    const actual = testModule.foo();

    expect(actual).toBe('my message');
    expect(testModule.message).toHaveBeenCalledTimes(1);
    testModule.__RewireAPI__.__ResetDependency__('message');
  });
});

Beispiele finden Sie in den Dokumenten .

Hinweis: Erfordert Babel-Transpilation

Option 3 - Trennen Sie alle Funktionen in separate Module/Dateien

Diese Option ist die ungünstigste, würde aber mit der typischen mock Funktionalität eindeutig gut funktionieren.


PS: Obwohl dieser Abschnitt sehr aufschlussreich und oft unterhaltsam war, hoffe ich, dass diese Zusammenfassung die Notwendigkeit für andere verringert, den ganzen Thread zu lesen. ️

Danke @nickofthyme , du hast gerade ein paar Tage damit beendet, meinen Kopf dagegen zu schlagen.

@nickofthyme Ihr option 1 schlägt sowohl in meiner App als auch in create react app fehl:

 FAIL  src/hello.test.js
  test spyon with function expressions
    ✓ should NOT mock message in foo (3ms)
    ✕ should mock message in foo (6ms)

  ● test spyon with function expressions › should mock message in foo

    expect(received).toBe(expected) // Object.is equality

    Expected: "my message"
    Received: "Hello world"

      17 |     const actual = testModule.foo();
      18 |
    > 19 |     expect(actual).toBe("my message");
         |                    ^
      20 |     expect(testModule.message).toHaveBeenCalledTimes(1);
      21 |   });
      22 | });

      at Object.toBe (src/hello.test.js:19:20)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        1.848s
Ran all test suites related to changed files.

@danielhusar Du

Ich habe die in Option 1 beschriebene Verspottung hier zum yarn test:hello .

Ergebnis

> yarn test:hello
yarn run v1.16.0
$ jest --config=jest.config.js -t=hello --verbose
 PASS  src/hello/hello.test.ts
  test hello
    ✓ should NOT mock message in foo (3ms)
    ✓ should mock message in foo (1ms)

Test Suites: 1 skipped, 1 passed, 1 of 2 total
Tests:       1 skipped, 2 passed, 3 total
Snapshots:   0 total
Time:        1.392s
Ran all test suites with tests matching "hello".
✨  Done in 2.36s.

Es erfordert die Verwendung einer benutzerdefinierten jest.config.js Datei mit ts-jest und einem direkten Aufruf von jest --config=./jest.config.js , nicht über react-scripts . Ich bin mir nicht sicher, wie Jest in react-scripts konfiguriert ist, aber ich denke, es gibt eine Möglichkeit, die Konfiguration irgendwie zu aktualisieren.

Dieser Fix entfernt die Transformationen für *.css und *.svg Dateien, ignorieren Sie also die App.tsx Fehler.

Gibt es etwas Besonderes, das getan werden muss, damit es funktioniert?
Ich würde sagen, ich habe ein ziemlich Standard-Setup (ohne ts) und es funktioniert nicht sofort.

Ich schaue mir das heute Abend etwas genauer an und schaue ob das möglich ist.

@danielhusar Ich habe nachgesehen und konnte keine Out-of-the-Box-Lösung finden. Der Schlüssel ist die jest config transformer die CRA Ihnen erlaubt , in package.json#jest zu überschreiben. Die js- und ts-Dateien werden mit babel-jest transpiliert, aber react-scripts hindert Sie daran, eine .babelrc Konfigurationsdatei zu verwenden und die test Umgebung festzulegen, die sie in react-scripts test festlegen. hier .

Ich wünschte, ich könnte tiefer graben, aber ich habe gerade keine Zeit.

Hmm Ich habe immer noch Probleme damit, dass es funktioniert (bei meinem benutzerdefinierten Setup, nicht beim Cra).
(neueste Version von Witz und Babel-Jest)

Das ist meine Witze-Konfiguration:

module.exports = {
  name: 'my-app',
  testURL: 'http://localhost/',
  setupFiles: ['<rootDir>/setup-jest.js'],
  setupFilesAfterEnv: ['<rootDir>/setup-test.js'],
  testMatch: ['**/__tests__/**/*.test.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
  testEnvironment: 'jest-environment-jsdom-fifteen',
  snapshotSerializers: ['enzyme-to-json/serializer'],
  globals: {
    ENV: 'test',
  },
  transform: {
    '^.+\\.[t|j]sx?$': 'babel-jest',
  },
};

Und mein Baberc:

{
  "presets": [
    "@babel/react",
    ["@babel/env", {
      "corejs": "3",
      "useBuiltIns": "entry",
      "loose": true,
      "exclude": [
        "es.string.split"
      ]
    }],
    "@babel/flow"
  ],
  "plugins": [
    "array-includes",
    "lodash",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-class-properties",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-object-rest-spread",
    "@babel/plugin-proposal-optional-chaining"
  ],
  "env": {
    "test": {
      "plugins": ["dynamic-import-node"]
    }
  }
}

Für jeden, der mit Option 1 zu kämpfen hat, ist es wichtig, die () => { return expression } anstelle der () => (expression) Funktion zu verwenden.

Ich habe Option 1 zum Laufen gebracht, indem ich sie geändert habe zu:

import * as test from './test';

export const message = () => {
    return 'Hello world';
  }

  export const foo = () => {
    return test.message();
  }

Nicht schön, aber es sollte funktionieren.

@nickofthyme , Option 1 ist richtig, wie Sie es haben. Aber wenn Sie den Code ändern in:

const foo = () => {}
export { foo }

Dann bricht es. Vermutlich, weil Sie ein neues Objektliteral erstellen und dieses exportieren.

Interessante Beobachtung. Danke @maletor

Jest hat in seiner Dokumentation ein sehr einfaches und klares Beispiel dafür, wie man ein Modul teilweise nachahmt. Dies funktioniert sowohl mit ES-Import- als auch mit Node-require-Anweisungen.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

@johncmunson Das ist ein guter Punkt. Dieses Beispiel, das Sie zum Verspotten eines Moduls gezeigt haben, funktioniert jedoch nur, wenn Sie jest.mock _once_ ausführen müssen und keine der verspotteten Methoden einen anderen Export aus diesem Modul verwendet.

Nehmen Sie das Beispiel von oben ... Ich habe bar hinzugefügt, um zu zeigen, wie ich das Modul zwischen foo und bar unterschiedlich verspotten möchte.

export const message = (): string => {
  return 'Hello world';
}

export const foo = (): string => {
  return message();
}

export const bar = (): (() => string) => {
  return foo;
}

Die Verwendung von jest.mock mit jest.requireActual meiner Meinung nach so aussehen.

import * as mockTestModule from './hello';

jest.mock('./hello');
const actualTestModule = jest.requireActual('./hello');

describe('test hello', function () {
  afterAll(() => {
    jest.restoreAllMocks();
  });

  // first test doesn't depend on any other method of the module so no mocks
  it('should NOT mock message in foo', function () {
    const actual = actualTestModule.foo();

    expect(actual).toBe('Hello world');
  });

  // the second I want to mock message in foo
  it('should mock message in foo', function () {
    jest.spyOn(mockTestModule, 'message').mockReturnValue('my message');
    const actual = actualTestModule.foo();

    expect(actual).toBe('my message'); // fails
    expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
  });

  it('should mock foo in bar', function () {
    jest.spyOn(mockTestModule, 'foo').mockReturnValue('my message');
    const actual = actualTestModule.bar();

    expect(actual()).toBe('my message'); // fails
    expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
  });
});

Ich habe sogar versucht, sie separat mit jest.doMock verspotten und habe immer noch das gleiche Ergebnis erhalten.


Klicken Sie hier, um den Code zu sehen

```ts
import * als testModule from './hello';

describe('test hallo', function() {
afterAll(() => {
jest.restoreAllMocks();
});

it('should NOT mock message in foo', function () {
  const actual = testModule.foo();

  expect(actual).toBe('Hello world');
});

it('should mock message in foo', function () {
  jest.doMock('./hello', () => {
    // Require the original module to not be mocked...
    const originalModule = jest.requireActual('./hello');

    return {
      ...originalModule,
      message: jest.fn().mockReturnValue('my message'),
    };
  });
  const actual = testModule.foo();

  expect(actual).toBe('my message'); // fails
  expect(testModule.message).toHaveBeenCalledTimes(1); // never called
});

it('should mock foo in bar', function () {
  jest.doMock('./hello', () => {
    // Require the original module to not be mocked...
    const originalModule = jest.requireActual('./hello');

    return {
      ...originalModule,
      foo: jest.fn().mockReturnValue('my message'),
    };
  });
  const actual = testModule.bar()();

  expect(actual).toBe('my message'); // fails
  expect(testModule.foo).toHaveBeenCalledTimes(1); // never called
});

});
```

Das Problem bei diesem Ansatz besteht darin, dass, wenn das eigentliche Modul erforderlich ist und dann foo , immer noch die eigentliche Funktion message aufgerufen wird und nicht die Mock.

Ich wünschte, es wäre so einfach, aber soweit ich das sehe, hilft dies nicht für die Beispiele aus diesem Thread. Wenn ich hier etwas vermisse, lass es mich wissen. Ich gebe gerne Fehler zu.

Für jeden, der auf der Suche nach einer Lösung darauf stößt, scheint Folgendes für mich zu funktionieren, wenn ich viele const / Funktionen in eine Datei exportiere und sie in eine Datei importiere, die ich teste

function mockFunctions() {
  const original = require.requireActual('../myModule');
  return {
    ...original, //Pass down all the exported objects
    test: jest.fn(() => {console.log('I didnt call the original')}),
    someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
  }
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
`

Folgendes funktioniert und ist etwas kürzer:

const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');

In Typoskript nur import statt require :

import * as module from './module';

Dies hat den Vorteil, dass es das Leben leicht macht, ursprüngliche Funktionen wiederherzustellen und nachzuahmen.

Für jeden, der auf der Suche nach einer Lösung darauf stößt, scheint Folgendes für mich zu funktionieren, wenn ich viele const / Funktionen in eine Datei exportiere und sie in eine Datei importiere, die ich teste

function mockFunctions() {
  const original = require.requireActual('../myModule');
  return {
    ...original, //Pass down all the exported objects
    test: jest.fn(() => {console.log('I didnt call the original')}),
    someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
  }
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
`

Folgendes funktioniert und ist etwas kürzer:

const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');

In Typoskript nur import statt require :

import * as module from './module';

Dies hat den Vorteil, dass es das Leben leicht macht, ursprüngliche Funktionen wiederherzustellen und nachzuahmen.

Oh ja, auch diese Methode funktioniert nicht, wenn Ihr Objekt nur getter definiert hat. Die Fehlermeldung könnte wie folgt aussehen:

Test suite failed to run

    TypeError: Cannot set property useContent of #<Object> which has only a getter

In diesem Fall müssen Sie wahrscheinlich jest.mock(..) . :verbeugung_mann:

Meine Mocks funktionieren mit folgendem:

import { unsubscribe } from "../lib/my-lib"
import { MyComponent } from "./index"

test("unsubscribe gets called", () => {
    const module = require("../lib/my-lib")
    jest.spyOn(
        module,
        "unsubscribe"
    ).mockImplementation(() => jest.fn())

    const { getByTestId } = render(() => <MyComponent  />)

    let button = getByTestId("trigger.some.action.button")

    fireEvent.press(button)

    expect(unsubscribe).toHaveBeenCalled()
})

Es sieht nicht so elegant aus und lässt sich nicht so leicht skalieren, funktioniert aber sehr gut und passt im Moment für meine Fälle.. aber wenn jemand eine andere Syntax vorschlagen kann, wäre das großartig! Dies ist die einzige Syntax, die zu funktionieren scheint.

es6 Modulcode:

export const funcA = () => {};
export const funcB = () => {
  funcA();
};

Nach der Übertragung in CommonJS:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.funcB = exports.funcA = void 0;

var funcA = function funcA() {};

exports.funcA = funcA; // You can mock or add a spy  on this `funcA`

var funcB = function funcB() {
  funcA();  // This is still original `funcA`
};

exports.funcB = funcB;

Es gibt viele Möglichkeiten, diese Situation zu lösen.

  1. Sie müssen den Code wie folgt ändern, damit Sie das verspottete/ausspionierte funcA
function funcA() {}
exports.funcA = funcA;

function funcB() {
  exports.funcA(); // Now, this `exports.funcA` is added a spy or mocked. Keep the same reference to `funcA`
}
exports.funcB = funcB;

Oder,

export let funcA = () => {};
export const funcB = () => {
  exports.funcA();
};

Ergebnisse des Unit-Tests:

 PASS  myModule.test.ts (9.225s)
  funcB
    ✓ should call funcA (3ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |     100 |      100 |     100 |     100 |                   
 myModule.ts |     100 |      100 |     100 |     100 |                   
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.967s
  1. Verwenden Sie das Rewire- Paket, um funcA zu verspotten
    ...

Außerdem müssen Sie sich diese Dokumentation ansehen: https://nodejs.org/api/modules.html#modules_exports_shortcut , um zu sehen, was genau require macht

Die Lösung in diesem Stackoverflow-Beitrag hat für mich funktioniert
https://stackoverflow.com/a/534002206/1217998

Grundsätzlich konvertieren Sie zuerst alle Funktionen, die Sie in jest.fn umwandeln möchten

jest.mock('../../utils', () => {
  const actualUtils = jest.requireActual('../../utils');
  const originalImplementation = actualUtils.someFun;

  return {
    ...actualUtils,
    someFun: jest.fn()
      .mockImplementation(originalImplementation),
  };
});
const utils = require('../../utils');

Dann kannst du es ganz normal verwenden, wenn du willst oder es so verspotten

jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);

Sie können dies verwenden, um die ursprüngliche Implementierung zu erhalten

beforeEach(() => {
    jest.clearAllMocks();
  });

Die Lösung in diesem Stackoverflow-Beitrag hat für mich funktioniert
https://stackoverflow.com/a/534002206/1217998

Grundsätzlich konvertieren Sie zuerst alle Funktionen, die Sie in jest.fn umwandeln möchten

jest.mock('../../utils', () => {
  const actualUtils = jest.requireActual('../../utils');
  const originalImplementation = actualUtils.someFun;

  return {
    ...actualUtils,
    someFun: jest.fn()
      .mockImplementation(originalImplementation),
  };
});
const utils = require('../../utils');

Dann kannst du es ganz normal verwenden, wenn du willst oder es so verspotten

jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);

Sie können dies verwenden, um die ursprüngliche Implementierung zu erhalten

beforeEach(() => {
    jest.clearAllMocks();
  });

Dankeschön!

Jest hat in seiner Dokumentation ein sehr einfaches und klares Beispiel dafür, wie man ein Modul teilweise nachahmt. Dies funktioniert sowohl mit ES-Import- als auch mit Node-require-Anweisungen.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

Funktioniert nicht, wenn die simulierte Funktion aus dem Modul heraus aufgerufen wird.

Außerdem habe ich festgestellt, dass es manchmal nützlich sein kann, eine Funktion so zu simulieren, dass Sie die ursprüngliche Funktion nicht ändern, sondern die Funktion mit einigen benutzerdefinierten (zusätzlichen) Variablen aufrufen:

jest.mock('./someModule', () => {
  const moduleMock = require.requireActual('./someModule');
  return {
    ...moduleMock,
    // will mock this function 
    someFunction: (args) =>
      moduleMock.someFunction({
        ...args,
        customArgument,
      }),
  };
});

In meinem Fall musste ich die Come-Konfiguration übergeben, um zu funktionieren, ohne die die Standardeinstellung verwendet würde.

Dies ist die einzige Möglichkeit, die ich gefunden habe. Wenn Sie also bessere oder einfachere Ideen haben, freue ich mich, zu hören :)

Dies beantwortet nicht die Frage / das Problem des OP, sondern ist eine Lösung mit einer gewissen Umgestaltung. Ich habe festgestellt, dass es am einfachsten ist, meine Funktionen in verschiedene Dateien aufzuteilen und diese Importe dann zu verspotten.

// package.json
...
"scripts": {
    "test": "jest",

...
"devDependencies": {
    "@babel/preset-env": "^7.11.5",
    "jest": "^24.9.0",
...

```js
// babel.config.js

module.exports = {
Voreinstellungen: [
[
'@babel/preset-env',
{
Ziele: {
Knoten: 'aktuell',
},
},
],
],
};

```js
// module-utils.js

export const isBabaYaga = () => {
  return false
}

// module.js

import { isBabaYaga } from './module-utils'

export const isJohnWickBabaYaga = () => {
  return isBabaYaga()
}
// modules.test.js

import { isJohnWickBabaYaga } from './module';

jest.mock('./module-utils', () => ({
    isBabaYaga: jest.fn(() => true)
}))

test('John Wick is the Baba Yaga', () => {

    // when
    const isBabaYaga = isJohnWickBabaYaga()

    // then
    expect(isBabaYaga).toBeTruthy()
});
PASS  src/module.test.js
✓ John Wick is the Baba Yaga (4ms)

Ich bin vor kurzem auf dieses Problem gestoßen. Keine der vorgeschlagenen Lösungen funktioniert für mich, da ich den Code nicht ändern kann. Auch das babel-plugin-rewire funktioniert bei mir nicht. Gibt es andere Lösungen, um zu testen, ob eine Funktion von einer anderen Funktion innerhalb desselben Moduls aufgerufen wurde? Dies scheint ehrlich gesagt so zu sein, als ob es funktionieren sollte oder dass es ein Babel-Plugin geben sollte, das dies tut. Jede Hilfe wäre sehr dankbar!

Ich bin vor kurzem auf dieses Problem gestoßen. Keine der vorgeschlagenen Lösungen funktioniert für mich, da ich den Code nicht ändern kann. Auch das babel-plugin-rewire funktioniert bei mir nicht. Gibt es andere Lösungen, um zu testen, ob eine Funktion von einer anderen Funktion innerhalb desselben Moduls aufgerufen wurde? Dies scheint ehrlich gesagt so zu sein, als ob es funktionieren sollte oder dass es ein Babel-Plugin geben sollte, das dies tut. Jede Hilfe wäre sehr dankbar!

Haben Sie https://github.com/facebook/jest/issues/936#issuecomment -659597840 überprüft? Es gibt ein minimales Repro, das Funktionsaufrufe in derselben Datei verspottet.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen