Jest: Comment se moquer d'une fonction de module spécifique ?

Créé le 25 avr. 2016  ·  116Commentaires  ·  Source: facebook/jest

Je me bats avec quelque chose qui, je pense, devrait être à la fois facile et évident, mais pour une raison quelconque, je ne peux pas le comprendre.

J'ai un module. Il exporte plusieurs fonctions. Voici myModule.js :

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

Je démasque le module pour le tester.

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

Cependant, je dois me moquer de foo , car cela fait des appels ajax à mon backend. Je veux que chaque fonction de ce fichier reste non moquée, attendez-vous à foo , dont je veux qu'elle soit moquée. Et les fonctions bar et baz font des appels internes à foo , donc quand mon test appelle bar() , le bar moqué appellera le moqué foo .

Il apparaît dans la documentation de jest que les appels à unmock et mock opèrent sur l'ensemble du module. Comment puis-je me moquer d'une fonction spécifique ? Diviser arbitrairement mon code en modules séparés afin qu'ils puissent être testés correctement est ridicule.

Commentaire le plus utile

Tu peux faire:

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

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

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

Tous les 116 commentaires

Après une analyse plus approfondie, il semble que jest-mock génère un AST pour l'ensemble du module, puis utilise cet AST pour créer un module simulé conforme aux exportations d'origine : https://github.com/facebook/jest/tree/master/packages /blague-dérision

D'autres frameworks de test, tels que le simulacre de Python (https://docs.python.org/3/library/unittest.mock-examples.html), vous permettent de simuler des fonctions spécifiques. Il s'agit d'un concept de test fondamental.

Je recommande fortement la possibilité de se moquer d'une partie d'un module. Je pense que jest-mock devrait être modifié pour ignorer de manière conditionnelle les exportations de mocking et faire référence à l'implémentation d'origine.

Tu peux faire:

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

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

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

Je pense que vous avez un malentendu fondamental sur le fonctionnement de require . Lorsque vous appelez require() , vous n'obtenez pas d'instance du module. Vous obtenez un objet avec des références aux fonctions du module. Si vous écrasez une valeur dans le module requis, votre propre référence est écrasée, _mais l'implémentation conserve les références d'origine_.

Dans votre exemple, si vous appelez myModule.foo() , oui, vous appellerez la version simulée. Mais si vous appelez myModule.bar() , qui appelle en interne foo() , le foo auquel il fait référence _n'est pas votre version écrasée_. Si vous ne me croyez pas, vous pouvez le tester.

Par conséquent, l'exemple que vous avez décrit est inadéquat pour le problème que j'ai. Savez-vous quelque chose que je ne sais pas ?

@cpojer

Je crois que je comprends assez bien. Cependant, la façon dont babel compile les modules ne facilite pas la compréhension et je comprends votre confusion. Je ne sais pas exactement comment cela se comporterait dans un véritable environnement ES2015 avec des modules, principalement parce qu'un tel environnement n'existe pas actuellement (sauf peut-être les dernières versions de Chrome Canary, que je n'ai pas encore essayées). Afin d'expliquer ce qui se passe, nous devons regarder la sortie compilée de votre code babel. Cela ressemblera à quelque chose comme ceci :

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

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

Dans ce cas, il est en effet exact que vous ne pouvez pas vous moquer de foo et je m'excuse de ne pas avoir lu votre problème initial correctement, mais il n'a fait aucune hypothèse sur la façon dont foo été appelé, donc j'ai supposé que c'était exports.foo() . Soutenir ce qui précède en se moquant d'une fonction après avoir demandé un module est impossible en JavaScript - il n'y a (presque) aucun moyen de récupérer la liaison à laquelle foo fait référence et de la modifier.

Cependant, si vous modifiez votre code en ceci :

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

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

et puis dans votre fichier de test vous faites :

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

cela fonctionnera comme prévu. C'est ce que nous faisons chez Facebook où nous n'utilisons pas ES2015.

Bien que les modules ES2015 puissent avoir des liaisons immuables pour ce qu'ils exportent, le code compilé sous-jacent sur lequel babel compile actuellement n'impose aucune de ces contraintes. Je ne vois actuellement aucun moyen de prendre en charge exactement ce que vous demandez dans un environnement de module ES2015 strict avec des modules pris en charge de manière native. Le fonctionnement de jest-mock est qu'il exécute le code du module de manière isolée, puis récupère les métadonnées d'un module et crée des fonctions fictives. Encore une fois, dans ce cas, il n'aura aucun moyen de modifier la liaison locale de foo . Si vous avez des idées sur la façon de mettre cela en œuvre efficacement, veuillez contribuer ici ou avec une pull request. Je voudrais vous rappeler que nous avons un code de conduite pour ce projet que vous pouvez lire ici : https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct

La bonne solution dans votre exemple n'est pas de se moquer de foo mais de se moquer de l'API de niveau supérieur que foo appelle (telle que XMLHttpRequest ou l'abstraction que vous utilisez à la place).

@cpojer Merci pour votre explication détaillée. Je suis désolé si je vous ai offensé avec ma langue, je suis très efficace avec ma rédaction technique et je veux faire passer mon point de vue dès que possible. Pour mettre les choses en perspective, j'ai passé 5 heures à essayer de comprendre ce problème et j'ai écrit 2 commentaires détaillés, puis vous l'avez clôturé avec un bref message qui a complètement raté le but de mes deux déclarations. C'est pourquoi mon message suivant disait que vous aviez un "malentendu fondamental", parce que soit 1) vous n'avez pas compris ce que je voulais dire, soit 2) vous n'avez pas compris require() , ce qui était heureusement l'option 1.

Je vais réfléchir à une solution possible à mon problème, pour le contourner pour l'instant, je me suis moqué d'une API de niveau inférieur, mais il devrait certainement y avoir un moyen de moquer la fonction directement, car ce serait très utile.

Je suis d'accord qu'il serait utile de pouvoir le faire, mais il n'y a pas de bon moyen dans JS de le faire sans analyse statique (probablement lente) au départ :(

@cpojer : Je ne sais pas si sauter ici 5 mois plus tard est la voie à suivre, mais je n'ai trouvé aucune autre conversation à ce sujet.

En partant de votre suggestion ci-dessus, j'ai fait ceci pour simuler une fonction d'une autre dans le même module :

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

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

    someModule.function1(...);

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

Cela fonctionne pour le seul test, mais je n'ai pas trouvé de moyen d'y parvenir de manière isolée au seul bloc it(...); . Comme écrit ci-dessus, cela affecte chaque test, ce qui rend difficile le test du vrai function2 dans un autre test. Des conseils ?

Vous pouvez appeler .mockClear sur la fonction dans beforeEach ou appeler jest.clearAllMocks() si vous utilisez Jest 16.

Salut @cpojer ! J'utilise Jest 16. Ni jest.clearAllMocks() ni someModule.function2.mockClear() fonctionnent pour moi. Ils ne fonctionnent que lorsque la maquette est un module entier, et non une fonction d'un module importé. Dans mon projet, la fonction reste moquée dans les tests suivants. Si cela n'est pas prévu, je verrai si je peux reproduire dans un petit exemple de projet et créer un nouveau problème. Bonne idée?

@cpojer -

La bonne solution dans votre exemple n'est pas de se moquer de foo mais de se moquer de l'API de niveau supérieur que foo appelle (telle que XMLHttpRequest ou l'abstraction que vous utilisez à la place).

Je suis nouveau sur Jest et je suis aux prises avec un problème similaire. J'utilise Axios , qui sous le capot utilise XMLHttpRequest , et je ne veux pas railler axios , mais pour se moquer de la réelle XMLHttpRequest . Il semble que je devrais implémenter ses méthodes par moi-même, quelque chose comme ça . Est-ce la bonne approche ?

Merci!

ouais, quelque chose comme ça devrait vous mettre sur la bonne voie ! :) Utilisez jest.fn comme une API plus agréable, cependant :D

@cpojer concernant votre commentaire ici : https://github.com/facebook/jest/issues/936#issuecomment -214939935

Comment feriez-vous cela avec ES2015 ?

// 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")

Pour tous ceux qui rencontrent cela à la recherche d'une solution, ce qui suit semble fonctionner pour moi lors de l'exportation de nombreuses const/fonctions dans un fichier et de leur importation dans un fichier que je 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');

@ainesophaur , je ne sais pas ce que je fais mal ici. Mais il semble que cela ne fonctionne pas
Je suis actuellement sur jest 18.1 (et 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();

Le test échouera alors avec :

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

@huyph, vous

@ainesophaur ... euh. Je pensais que vos codes ci-dessus étaient pour se moquer de la méthode test() ? cette partie : test: jest.fn(() => {console.log('I didnt call the original')}),

@ainesophaur J'ai aussi essayé votre code. Mais cela n'a pas fonctionné pour moi. Il n'exécute jamais la fonction de simulation. Ainsi, l'attente n'est jamais satisfaite.

Je pense que cela est inhérent à la façon dont nécessite des travaux comme indiqué ci-dessus ... J'aimerais qu'il y ait une solution pour cela.

@cpojer Y a-t-il quelque chose de nouveau concernant les modules partiellement moqueurs?

@rantonmattei & @huyph Je devrais voir un extrait de vos définitions

Se moquer d'un fichier à partir d'une dépendance

La définition de la fonction réelle dans cet exemple est effectuée par react-native. Je me moque du fichier "react-native/Libraries/Utilities/dismissKeyboard.js"

Ceci est un fichier fictif sous __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;

Je ne trouve pas le fichier de test que j'ai utilisé pour ce qui précède, mais c'était quelque chose comme exiger le module, je le trouverais dans __mocks__ et ensuite je pourrais faire quelque chose comme

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

Se moquer d'un fichier que vous contrôlez
Définition de la fonction réelle

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

Fichier de test avec maquette

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});
});

merci pour le partage @ainesophaur. Je n'arrive toujours pas à le faire fonctionner avec jest 18.1. Voici mes 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();
});

En session.js

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

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

Je ne trouve pas de moyen de résoudre ce problème. Puis-je rouvrir ceci s'il vous plaît? @cpojer

@huyph la façon dont vous faites l'exportation saveSession va appeler le restartCheckExpiryDateTimeout défini localement au lieu de passer par le module et d'appeler module.restartCheckExpiryDateTimeout -- donc votre module.restartCheckExpiryDateTimeout moqué saveSession car saveSession appelle la fonction définie restartCheckExpiryDateTimeout .

J'affecterais saveSession à un const, puis je ferais saveSession.restartCheckExpiryDateTimeout = () => {...logic} . .puis à partir de saveSession.saveSession , appelez saveSession.restartCheckExpiryDateTimeout au lieu de seulement restartCheckExpiryDateTimeout . Exportez votre nouveau const au lieu de la fonction actuelle saveSession qui définit ensuite vos méthodes. Ensuite, lorsque vous appelez votre someVar.saveSession() il appellera en interne saveSession.restartCheckExpiryDateTimeout() qui est maintenant moqué.

J'aurais dû ajouter que restartCheckExpiryDateTimeout() est une fonction exportée. Pas une fonction définie localement dans saveSession() ... (Mise à jour de mon commentaire ci-dessus). Dans ce cas, je pense que module.saveSession() devrait appeler le bon module.restartCheckExpiryDateTimeout() qui est moqué.

Mais je vais donner ce que vous suggérez ci-dessus. Déplacer à la fois saveSession() et restartCheckExpiryDateTimeout() vers un autre const. Merci

Je comprends que ce n'est pas défini dans le cadre de saveSession. saveSession est
appelant la méthode sœur dans la portée parent. J'ai rencontré ça plusieurs fois
et ce que j'ai suggéré a fonctionné pour cela

Le 8 mai 2017 à 20h38, "Huy Pham" [email protected] a écrit :

J'aurais dû ajouter que restartCheckExpiryDateTimeout() est un export
fonction. Pas une fonction définie localement dans saveSession()...

Je vais cependant donner ce que vous suggérez ci-dessus. Merci

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300029003 , ou couper le son
le fil
https://github.com/notifications/unsubscribe-auth/AEeBdsmpOOmzvcUHB3D_-Z7MChIzt10Pks5r37WYgaJpZM4IPGAH
.

Je viens d'essayer ça... j'ai trouvé ça :

Cela ne fonctionne PAS : (c'est-à-dire que le restartCheckExpiryDateTimeout() d'origine est toujours appelé)

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

Cela fonctionne : (c'est-à-dire que le faux restartCheckExpiryDateTimeout() est appelé à la place). La différence est l'utilisation de function() au lieu de la forme de flèche, et l'utilisation de this. au lieu de session.

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

Cela pourrait être un problème avec la transpilation de ces codes ....

Essayez de les exporter en tant qu'objet de classe au lieu de pojo. je crois que le
transpiler hisse les variables différemment. Nous allons arriver à un travail
test, promis.. Cela fait environ six mois que je suis sur le projet
qui a utilisé la plaisanterie mais je me souviens bien de ce problème et je me souviens finalement
trouver une solution.

Le 9 mai 2017 à 00h53, "Huy Pham" [email protected] a écrit :

Je viens d'essayer ça... j'ai trouvé ça :

Cela ne fonctionne PAS : (c'est-à-dire que le restartCheckExpiryDateTimeout() d'origine est
toujours appelé)

session d'exportation = {
saveSession : () => {
session.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout : () => {},
}

Cela ne fonctionne PAS : (c'est-à-dire que le restartCheckExpiryDateTimeout() d'origine est
toujours appelé)

session d'exportation = {
saveSession : function() {
this.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout : () => {},
}

Cela pourrait être un problème avec la transpilation de ces codes ....

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300060975 , ou couper le son
le fil
https://github.com/notifications/unsubscribe-auth/AEeBdrRQExycPYiGtvm7qYi5G87w6b6Oks5r3_FlgaJpZM4IPGAH
.

@sorahn même problème. es6 + babel , Comment se moquer ?
@cpojer Est-ce que cela signifie es6 + babel , export const function xx() {} , exporter plusieurs fonctions , Jest n'a aucun moyen de se moquer d'une fonction dans un module (fichier) appelé par une autre fonction dans le même module (fichier) ? Je teste, il semble que j'ai raison. Juste pour le motif commonjs , Jest peut se moquer de la fonction avec succès, comme votre exemple.

@ainesophaur ne fonctionne pas.

module:

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

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

test:

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);

  });

});

résultat du test:

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

Regardez mes derniers commentaires ci-dessus. Plus précisément le dernier. Ton
les méthodes exportées appellent la méthode sœur à portée locale par rapport à la
méthode exportée réelle (c'est là que se trouve votre maquette)

Le 31 mai 2017 à 02h00, "novaline" [email protected] a écrit :

@ainesophaur https://github.com/ainesophaur ne fonctionne pas.

module:

export const getMessage = (num : nombre) : chaîne => {
renvoie Her name is ${genName(num)} ;
} ;
fonction d'exportation genName(num: number): chaîne {
retourner 'novaline';
}

test:

fonction mockFunctions() {
const original = require.requireActual('../moduleA');
revenir {
...original,
genName : jest.fn(() => 'emilie')
}
}jest.mock('../moduleA', () => mockFunctions());const moduleA = require('../moduleA');
describe('fonction fictive', () => {

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);

});

});

résultat du test:

ÉCHEC jest-examples/__test__/mock-function-0.spec.ts
● fonction de simulation › 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)

fonction de simulation
✓ t-0 (1ms)
t-1 (22ms)

Suites de tests : 1 échec, 1 au total
Tests : 1 échoué, 1 réussi, 2 au total
Instantanés : 0 au total
Temps : 0,215 s, estimé à 1 s

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/facebook/jest/issues/936#issuecomment-305091749 , ou muet
le fil
https://github.com/notifications/unsubscribe-auth/AEeBdv6SafXlTtKo3DNeFWhbL6gV9l0Gks5r_QHjgaJpZM4IPGAH
.

@ainesophaur : J'ai tenté export class Session { } . Et ça ne marche pas pour moi.

La seule approche qui fonctionne pour moi est dans mon commentaire ci-dessus : où la syntaxe function est utilisée à la place de la flèche () => . Ici:

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

C'est sur Jest 20.0.3

Ce que je fais, c'est créer un wrapper const pour les fonctions, puis exporter ce wrapper (comme export const fns). Ensuite, à l'intérieur du module, utilisez fns.functionName, puis je peux jest.fn() la fonction fns.functionName

Lorsque nous écrivons la fonction fictive d'un module défini par l'utilisateur qui est écrit en tapuscrit et lorsque nous appelons la fonction fictive, c'est la fonction d'origine couverte dans le rapport de couverture car nous appelons la version simulée de la fonction.

J'ai 2 fonctions qui ont été importées à l'origine dans les tests comme
import { getCurrentDate, getStartEndDatesForTimeFrame } from ./../_helpers/date';
Comme vous le voyez, getStartEndDatesForTimeFrame dépend de getCurrentDate . Avec la configuration suivante, le test getCurrentDate fonctionne bien et utilise la version simulée. D'un autre côté, pour une raison quelconque, le test getStartEndDatesForTimeFrame n'utilise pas le getCurrentDate moqué mais l'implémentation d'origine, donc mon test échoue. J'ai essayé de nombreuses configurations différentes (comme Date.now = jest.fn(() => "2017-11-16T20:33:09.071Z"); mais je n'ai pas réussi à le faire fonctionner. Des idées ?

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 :(
  });
});

Ainsi, le getStartEndDatesForTimeFrame échoue car il utilise l'heure actuelle et non l'heure simulée.

J'ai réussi à le faire fonctionner en suivant une suggestion de @ainesophaur - en exportant toutes les fonctions au sein d'un objet et en appelant les méthodes de ces objets exportés au lieu des méthodes sœurs à portée locale :

// 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 C'est la seule façon dont j'ai pu le faire aussi. Y a-t-il une perte de performance ou quelque chose comme ça lorsque nous utilisons cette méthode ? Il semble "incorrect" de modifier le code pour pouvoir le tester.

J'aimerais vraiment un moyen d'écrire simplement:
jest.mock('src/folder/file.func, () => {return 'whatever i want'})

l'élément clé ici étant le .func

@miluoshi @Rdlenke si votre code se compose d'exportations nommées, vous pouvez également import * as model puis écraser model.generateImagePreview = jest.fn(() => Promise.resolve);

Comment testeriez-vous cela avec sinon? Comme mentionné précédemment (voir https://github.com/facebook/jest/issues/936#issuecomment-214939935), la façon dont ESM fonctionne rend impossible de se moquer de func2 dans func1 , donc Je ne dirais pas nécessairement que c'est basique.

Peut-être qu'un mod babel pourrait être écrit pour lire toutes les fonctions "testImport"
et réécrit le code pour exporter les fonctions dans le module avant la
test en cours d'exécution ?

Le lundi 18 décembre 2017 à 17h00, Jim Moody [email protected] a écrit :

Tu as raison @SimenB https://github.com/simenb , j'avais changé quelque chose
dans mon test entre le passage à Sinon, il semblerait que cela ait réussi.
Quand j'ai rétabli cela, cela ne fonctionne toujours pas. Je suppose que ce n'est pas un problème
qui a été résolu.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/facebook/jest/issues/936#issuecomment-352488400 , ou couper le son
le fil
https://github.com/notifications/unsubscribe-auth/AQRY9a5-s2_bjCWKNw5WiAJW-JeBf8W3ks5tBpoygaJpZM4IPGAH
.

--

Darren Cresswell
Développeur de contrat | Développeur Limité
Courriel : [email protected]
Téléphone:
Site Web : http://www.develler.co.uk

Pensez à l'environnement avant d'imprimer cet e-mail
AVERTISSEMENT : Les virus informatiques peuvent être transmis par courrier électronique. Le destinataire
devrait vérifier cet e-mail et toutes les pièces jointes pour la présence de virus.
Develer Limited n'accepte aucune responsabilité pour tout dommage causé par un virus
transmis par cet e-mail. Il n'est pas garanti que la transmission des e-mails soit
sécurisé ou sans erreur car les informations pourraient être interceptées, corrompues, perdues,
détruits, arrivés en retard ou incomplets, ou contiennent des virus. L'expéditeur
décline donc toute responsabilité en cas d'erreur ou d'omission dans le
contenu de ce message, résultant de la transmission d'un e-mail.

AVERTISSEMENT : Bien que Develer Limited ait pris des précautions raisonnables pour
assurez-vous qu'aucun virus n'est présent dans cet e-mail, la société ne peut pas accepter
responsabilité pour toute perte ou dommage résultant de l'utilisation de cet e-mail ou
pièces jointes.

Develer Limited est une société anonyme enregistrée en Angleterre et au Pays de Galles. |
Numéro d'enregistrement de la société 09817616 | Siège social : SUITE 1 SECONDE
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, ROYAUME-UNI, BH7 7DU

merci @ainesophaur pour la solution de contournement.

Au cas où quelqu'un trouverait un exemple de travail non asynchrone utile, voici le mien :

//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 Si j'ai bien compris le problème, cela devrait fonctionner pour votre exemple :

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

(cela ne fonctionne que si les fonctions sont exportées en tant que const, comme dans votre exemple)

@dinvlad mon héros !

À la suite de la réponse de page de l' page des fonctions fictives pourrait être une amélioration des documents jest sur la moquerie :

  • jest.isMockFunction(fn)
  • jest.genMockFromModule(NomModule)
  • jest.mock (nom du module, usine, options)
  • jest.unmock(NomModule)
  • jest.doMock(Nom du module, usine, options)
  • jest.dontMock(NomModule)

Mon cas d'utilisation est qu'en tant que nouvel utilisateur de jest, je migre du code moka + sinon.js vers jest. J'avais déjà des espions et des attentes alors j'ai pensé que ce serait facile. Mais après avoir lu ce fil et lu la documentation de jest sur les fonctions Mock, j'avais l'impression qu'utiliser jest de cette façon pourrait nécessiter une réécriture de mes tests ou une compréhension détaillée d'ESM ou de Babel ... ou d'une autre confusion.

Merci pour Jest - cela rend mes tests plus faciles à écrire/comprendre et plus rapides à exécuter. :)

Les relations publiques clarifiant les documents sont les bienvenues ! ??

Pour simuler uniquement des modules spécifiques avec la syntaxe du module ES, vous pouvez utiliser require.requireActual pour restaurer les modules d'origine, puis écraser celui que vous souhaitez simuler :

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!');
});

Se sent inversé, mais c'est la façon la plus simple que j'ai rencontrée. Chapeau à @joshjg.

Je suis perdu quelque part dans la longue discussion, j'avais juste une question, y a-t-il de toute façon à tester si l'implémentation réelle de la fonction est appelée ?

D'après ce que je comprends, si j'ai besoin d'utiliser jest.fn() cela écrasera la fonction d'origine, mais si je ne l'utilise pas, la console me donnera une erreur en disant que ce doit être un jest.fn() function or a spy

J'essaie de tester un middleware où la demande sera transmise, donc si je m'en moque, toute la logique sera perdue et les données ne seront pas transmises au prochain middleware. Si je ne m'en moque pas, en l'important, puis-je tester que cette fonction a été appelée ?

Vous pouvez utiliser jest.spyOn , peut-être ? Par défaut, il appelle la fonction sous-jacente

Merci pour l'aide, j'ai essayé, mais le test suggère qu'il n'a jamais été appelé même s'il a été appelé car j'ai mis le fichier console.log et il s'est imprimé

fichier d'essai

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()
      })
    })

gestionnaire d'erreurs

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

console

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 |     })

La fonction s'appelle-t-elle handleClientError ou logError ?

@WangHansen D'après votre exemple, votre code devrait être expect(errorHandler.handleClientError).toBeCalled() // > true

@WangHansen pourriez-vous ajouter .mockImplementation() à votre jest.spyOn() ? En tant que personne venant de Jasmine, j'ai trouvé cette astuce cruciale pour obtenir les mêmes fonctionnalités que les espions de Jasmine. Par exemple

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

Si vous _n'utilisez pas_ mockImplementation() , alors jest.spyOn() produit un objet qui n'est _pas_ un simulacre (afaiu) et il s'en remet en fait à l'implémentation native. Si vous devez conserver l'implémentation native, cela vaut peut-être la peine d'utiliser

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

Pas sûr que ce soit nécessaire, mais assez sûr que cela devrait fonctionner.

Revenons à la demande initiale...

Ne pourriez-vous pas simplement envelopper un proxy autour d'un import * ? par exemple

importer * en tant que test de './myfile.js' ;

gestionnaire const = {
/** Intercepts : obtention des propriétés */
get(cible, propKey, récepteur) {
console.log( GET ${propKey} );
retour 123 ;
},

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

const p = nouveau Proxy(test);

Le mardi 30 janvier 2018 à 16h24, Denis Loginov [email protected]
a écrit:

@WangHansen https://github.com/wanghansen pourriez-vous ajouter
.mockImplementation() à votre jest.spyOn() ? En tant que personne venant de
Jasmine, j'ai trouvé cette astuce cruciale pour obtenir la même fonctionnalité que
Les espions de Jasmine. Par exemple

const mockModuleFunction = plaisanterie
.spyOn(module, 'fonction')
.mockImplementation(() => 'bonjour');...expect(mockModuleFunction.mock).toBeCalled();

Si vous n'utilisez pas mockImplementation(), alors jest.spyOn() produit un
objet qui n'est pas un simulacre (afaiu) et il s'en remet en fait au natif
la mise en oeuvre. Si vous devez conserver l'implémentation native, c'est peut-être
vaut la peine d'être utilisé

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

Pas sûr que ce soit nécessaire, mais assez sûr que cela devrait fonctionner.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/facebook/jest/issues/936#issuecomment-361648414 , ou couper le son
le fil
https://github.com/notifications/unsubscribe-auth/AQRY9VXyHNYatwOOY6EV637WGQH9k5Plks5tP0I9gaJpZM4IPGAH
.

--

Darren Cresswell
Développeur de contrat | Développeur Limité
Courriel : [email protected]
Téléphone:
Site Web : http://www.develler.co.uk

Pensez à l'environnement avant d'imprimer cet e-mail
AVERTISSEMENT : Les virus informatiques peuvent être transmis par courrier électronique. Le destinataire
devrait vérifier cet e-mail et toutes les pièces jointes pour la présence de virus.
Develer Limited n'accepte aucune responsabilité pour tout dommage causé par un virus
transmis par cet e-mail. Il n'est pas garanti que la transmission des e-mails soit
sécurisé ou sans erreur car les informations pourraient être interceptées, corrompues, perdues,
détruits, arrivés en retard ou incomplets, ou contiennent des virus. L'expéditeur
décline donc toute responsabilité en cas d'erreur ou d'omission dans le
contenu de ce message, résultant de la transmission d'un e-mail.

AVERTISSEMENT : Bien que Develer Limited ait pris des précautions raisonnables pour
assurez-vous qu'aucun virus n'est présent dans cet e-mail, la société ne peut pas accepter
responsabilité pour toute perte ou dommage résultant de l'utilisation de cet e-mail ou
pièces jointes.

Develer Limited est une société anonyme enregistrée en Angleterre et au Pays de Galles. |
Numéro d'enregistrement de la société 09817616 | Siège social : SUITE 1 SECONDE
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, ROYAUME-UNI, BH7 7DU

@dinvlad @iampeterbanjo @SimenB Merci encore pour toute votre aide, mais malheureusement, aucune des méthodes que vous avez suggérées n'a fonctionné. Je me demande si c'est parce que la fonction est appelée sous la forme next(err) . La logique est que lorsqu'une requête échoue, elle sera transmise au errorHandler en appelant return next(err) . Bien sûr, la fonction est appelée car lorsque j'ai ajouté console.log , elle s'imprime. Mais les tests suggèrent qu'il ne s'appelle jamais

@dinvlad J'ai essayé votre méthode, cela n'a pas fonctionné, mais merci pour votre aide quand même. Je me demandais juste pourquoi devez-vous appeler mockImplementation , selon le document officiel sur jest.spyOn , vous ne l'appelez que lorsque vous souhaitez remplacer la fonction d'origine.

@WangHansen oui, vous avez raison, cela n'est nécessaire que lorsque l'on veut remplacer la méthode d'origine. Je lançais juste une idée pour ces situations.

L'asynchronicité est l'une des raisons pour lesquelles cela a peut-être échoué pour vous. Si votre méthode utilise des rappels et/ou des promesses (ou async/wait), vous devez vous assurer que vos attentes sont réellement exécutées avant que votre méthode de test ne se termine. Il existe une méthode spéciale expect.assertions(N) pour affirmer cela. Assurez-vous également que votre attente n'est exécutée qu'après l'appel du code à l'intérieur des rappels/promesses. Je suis sûr que vous avez regardé cela, mais juste pour la référence, https://facebook.github.io/jest/docs/en/asynchronous.html

Il est regrettable que se moquer de fns utilisé en interne comme le décrit @seibelj ne soit pas possible sans changer le module impl.

À mon avis, les tests ne devraient pas déterminer comment la logique métier est implémentée 😕 (du moins pas à ce degré)

Existe-t-il des plans pour mettre en œuvre un tel comportement en plaisantant ?

Salut,
Un peu tard pour toute la discussion, mais en lisant toute la discussion - je n'ai toujours pas réussi à le faire.
La solution prometteuse de @greypants n'a pas vraiment fonctionné pour moi, car elle appelle toujours la fonction d'origine.
Quelque chose a-t-il changé depuis le début de cette discussion ? Est-ce que j'ai raté quelque chose ?

J'ai un peu adapté la solution de @greypants et cela a fonctionné pour moi. Voici mon code si ça peut aider quelqu'un :

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)
})

Cela semble toujours un peu bidon et les documents n'étaient pas très utiles. Je suis reconnaissant envers Jest et l'équipe derrière cela, mais cela semble être un cas d'utilisation courant qui devrait au moins avoir une solution "officielle".

@sarahdayan , et qui que ce soit d'intérêt -
J'ai fini par utiliser babel-plugin-rewire.
Il m'a fallu un certain temps pour trouver ce plugin, mais c'était une solution suffisamment cohérente pour ne pas me sentir hacker.

Dans la plupart des cas, nous voulons ne pas moquer une ou plusieurs fonctions d'un module. Si vous utilisez le système de moquerie de Global Jest, vous pouvez y parvenir en utilisant genMockFromModule et requireActual . Voici l'exemple :

//  __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;

Cette solution permet d'utiliser des simulations pour d'autres fonctions du module, en utilisant l'implémentation originale de someFunction lorsque tout le module est moqué et permet également de se moquer de la fonction someFunction avec mockImplementationOnce ou mockImplementation API.

J'ai lu toutes les conversations ci-dessus, mais aucune solution ne fonctionne pour moi.
Si vous cherchez toujours une solution pour ce cas de test, la réponse est babel-plugin-rewire , ce plugin est destiné à résoudre le cas de scénario dont nous avons discuté.
S'il vous plaît jeter un oeil sur cette lib, vous me remercierez plus tard.

Donc pour résumer tout le fil ci-dessus :

  1. Supposons que vous ayez un module m avec les fonctions f , g et hg et h appellent f . Nous aimerions nous moquer de f pour que g et h appellent la simulation au lieu du vrai f . Malheureusement, ce n'est pas possible de le faire directement à moins que f soit toujours appelé via exports comme cpojer l'a décrit . C'est impossible si votre module utilise la syntaxe d'import/export ES6 dans TypeScript (et je suppose que c'est la même chose dans Babel).
  2. Cependant, supposons que nous déplacions f vers un autre module m2 . Alors m aura une déclaration comme import {f} from 'm2' et quand g et h appellent f , ils appellent en fait m2.fm2 = require('./m2') (c'est à quoi ressemblera la traduction Babel/TypeScript). Cela permet de se moquer de f manière fiable comme décrit par greypants . En d'autres termes, vous ne pouvez simuler des appels de manière fiable que s'ils traversent une limite de module . Remarque : la solution de greypants produit maintenant ce message d'erreur : "La fabrique de modules de jest.mock() n'est pas autorisée à référencer des variables hors champ - Accès aux variables non valide : __assign". Je soupçonne que c'est un bogue dans Jest ; comme solution de contournement, utilisez Object.assign comme indiqué ci-dessous.
  3. Mais si, au lieu de vous moquer d'une ou deux fonctions, vous voulez vous moquer de tout sauf d' une ou deux fonctions, utilisez un code comme celui de darkowic .

Exemple de (2) :

~~~js
// module m.js
importer {f} de './m2'
fonction d'exportation g() { return 'f renvoyé ' + f(); } ;

// module m2.js
export function f() { return 'le vrai f'; }

// test.js
importer * en tant que m à partir de './m'

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

test('moqué', () => {
expect(mg()).toEqual('f retourné MOCK');
});
~~~

En testant cela, je suis tombé sur #2649 : appeler jest.mock intérieur d'un test n'a aucun effet, et si vous l'appelez à la portée globale, vous ne pouvez pas unmock avant d'autres tests. Très ennuyant.

Merci!! @sarahdayan
Je cherchais ça depuis un moment

Si les docs manquent, les PR sont toujours les bienvenus pour les clarifier

Salut à tous!

J'ai joué un peu et j'ai eu l'idée suivante pour résoudre ce problème :

  • simuler un module, mais le module simulé a le module d'origine dans la chaîne de prototype.
  • fournir une méthode pour ajouter des propriétés au module simulé (qui remplacera les propriétés du prototype)
  • fournissent également une méthode pour supprimer les propriétés du module simulé (pour réutiliser celles du prototype).
// 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");
});

Mes 2 centimes :

J'ai testé beaucoup de solutions (sinon toutes) et la seule qui a fonctionné pour moi est celle-ci (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");
});

J'en suis venu à la conclusion que ce n'est pas une bonne idée d'essayer de le faire parce que :

  • test.js sait trop sur les détails d'implémentation de importedModule.js
  • la solution est trop fragile et personne ne comprendra le but du mockProxy en regardant importedModule.js

Quelqu'un a-t-il trouvé une solution pour cela qui fonctionne?

J'utilise:

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

@jamesone avez-vous lu ceci https://github.com/facebook/jest/issues/936#issuecomment -410080252 ?

Cela a fonctionné pour moi, basé sur la réponse de @thomaskempel :

Dans mon cas, je voulais me moquer d'une dépendance dans node_modules, appelons-la 'shared-components'. Il exporte un certain nombre de composants nommés. Je voulais me moquer de quelques-unes de ces exportations nommées et laisser le reste comme la vraie chose.

Donc dans __mocks__/shared-components.js j'ai :

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

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

Dans mon cas, j'écrasais les implémentations. Espérons que cela aide quelqu'un à l'avenir.

J'ai été confronté au même problème récemment, la conversation dans ce fil m'a aidé à mieux comprendre et j'ai résumé mes conclusions ici https://medium.com/@DavideRama/mock -spy-exported-functions-within-a-single- module-en-jest-cdf2b61af642

Inspiré de la solution de @qwertie

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

@MajorBreakfast qui fonctionne avec React.lazy ?

const mockLazy = jest.fn();

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

Je reçois toujours ReferenceError: React is not defined .

Pour simuler un module de fonction d'exportation indépendant :
Exportez toutes les fonctions individuelles faisant partie de l'exportation par défaut à partir du fichier.

Exemple:
dataDao.js

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

Vous pouvez maintenant importer toutes les fonctions du fichier dans votre jest test par nommage par défaut ;

dataDao.spec.js

importer * en tant que dataDao à partir de '../dataDao' ;
// Espionner les modules référençant le nom par défaut attribué à l'import
jest.spyOn(dataDao, 'getData')
jest.spyOn(dataDao, 'setData')
jest.spyOn(dataDao, 'deleteData')

@vchinthakunta , cela peut fonctionner, mais cela ressemble à une violation d'un objectif majeur de la syntaxe d'exportation/importation : les autres modules ne pourront plus importer des méthodes ou des champs de données spécifiques via

import { justThisThing } from 'someModule';

Est-ce que j'ai raté quelque chose là-bas ?

@MajorBreakfast qui fonctionne avec React.lazy ?

const mockLazy = jest.fn();

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

Je reçois toujours ReferenceError: React is not defined .

Je pense que « React » devrait être en minuscule ici car il fait référence à l'importation ?

jest.mock('react'...)

J'ai fait fonctionner mon code en utilisant ce qui suit, ce qui est plus simple que les autres solutions que j'ai vues ici. Cela ne nécessite pas que vous utilisiez require ou que vous configuriez des exportations default .

helpers/navigation.js

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

...

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

composant qui utilise navigation.js

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 est hissé, utilisez doMock ou

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

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

Cela semble être un problème assez courant. Y a-t-il quelque chose à ce sujet dans les docs de plaisanterie?

Alors, existe-t-il une solution pour se moquer d'une fonction au sein d'un même module ?

La méthode suivante a fonctionné pour moi, l'astuce consiste à réinitialiser la fonction simulée à la fin du test.
Cet exemple se moque de la fonction de vérification du module jsonwebtoken.

  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
  })

La méthode suivante a fonctionné pour moi, l'astuce consiste à réinitialiser la fonction simulée à la fin du test.
Cet exemple se moque de la fonction de vérification du module jsonwebtoken.

  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
  })

où utilisez-vous le const verify ? de toute façon, cela ne fonctionnera que si votre fonction simulée n'est pas une fonction const exportée

Alors, existe-t-il une solution pour se moquer d'une fonction _dans le même module_ ?

Après de nombreuses recherches, la solution à ce problème consiste à stocker vos exportations dans un seul objet auquel votre fonction et vos simulations peuvent faire référence. Les articles ci-dessous sont tous parvenus au même consensus.

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

C'est surprenant pour moi que personne n'ait mentionné une solution différente qui, si elle est réalisable pour votre code, élimine complètement tous ces problèmes et rend le test du code souhaité très, très simple :

_Déplacez la fonction unique que vous souhaitez simuler dans son propre module._

Sérieusement. Si votre module est écrit de telle sorte que vous devez en tester les parties internes séparément des autres parties internes, alors il est presque certain que votre classe viole le principe de responsabilité unique (oui, ce n'est pas une vraie classe, mais le module fonctionne comme une classe, modules étant un conteneur unitaire de code). Divisez cette ventouse et boum, vous pouvez vous moquer des besoins.

Si la fonction simulée repose sur un tas d'états privés, ce n'est toujours pas une bonne raison de ne pas diviser votre module d'une manière ou d'une autre. Le fait même qu'il repose sur un tas d'états internes implique, pour moi, que les soucis du module ne sont pas clairement réfléchis. Peut-être y a-t-il même un troisième module à séparer, qui représente une sorte de classe de données ou DTO, qui peut être transmis en tant qu'argument.

De plus, exportez-vous des fonctions uniquement à des fins de test qui seraient autrement privées ? Pourquoi le code externe appellerait-il directement la fonction simulée, mais appellerait également les autres fonctions qui elles-mêmes ont besoin de l'appeler ? Je parie qu'il y a une sorte de déconnexion ici. Peut-être que la fonction simulée doit rester, mais toutes les fonctions doivent être divisées en deux, la moitié supprimée allant dans un autre module. Vous avez eu l'idée.

Lorsque les tests deviennent très difficiles, c'est presque toujours le signe qu'une refactorisation est nécessaire...

Aucun test n'est requis :

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

Après avoir lu ce fil et testé les solutions proposées, je n'arrive toujours pas à le faire fonctionner. D'après ce que j'ai lu, certaines personnes ont fait ce travail mais je ne peux pas comprendre comment.

Quelqu'un pourrait-il me dire le code que je dois ajouter à l'exemple suivant pour que les tests réussissent ?

// 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')
  })
})

Merci!

Je voulais seulement me moquer d'une seule méthode lodash comme lodash.random et j'ai pu le faire facilement avec :

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)
});

J'espère que cela pourra aider :)

Quelque chose qui a fonctionné pour notre équipe travaillant avec dactylographe était de créer un const nous exportons au lieu d'exporter la fonction directement.
Ca ne fonctionne pas:
export function doSomething(a, b) {}
Travail:
export const doSomething = function (a, b) {}

J'ai changé l'export comme @arbielsk , ça marche ! Mais je ne sais pas quelle est la différence entre deux types d'exportation...

@dgrcode Avez-vous déjà trouvé une solution à votre exemple ? Pour autant que je sache, ce que vous essayez de faire n'est pas pris en charge par la moquerie via Jest. Plus précisément, je pense que se moquer consiste simplement à recâbler les importations afin que les vues externes du module voient les méthodes moquées. Cependant, dans votre exemple, foo et bar sont dans le même module et donc la vue de foo de bar ne peut pas être simulée.

Je pense que vos options sont soit :
1) Réorganisez votre code pour que foo importe un module qui inclut bar
2) Utiliser babel-plugin-rewire

Merci de me corriger si j'ai mal compris !

J'avais une exigence légèrement différente : je voulais simuler un module entier _sauf_ pour une fonction. En utilisant la solution de

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

@dgrcode Avez-vous déjà trouvé une solution à votre exemple ? Pour autant que je sache, ce que vous essayez de faire n'est pas pris en charge par la moquerie via Jest. Plus précisément, je pense que se moquer consiste simplement à recâbler les importations afin que les vues externes du module voient les méthodes moquées. Cependant, dans votre exemple, foo et bar sont dans le même module et donc la vue de foo de bar ne peut pas être simulée.

Je pense que vos options sont soit :

  1. Réorganisez votre code pour que foo importe un module qui inclut bar
  2. Utiliser babel-plugin-rewire

Merci de me corriger si j'ai mal compris !

c'était effectivement mon cas
Ma compréhension de la façon dont les modules pouvaient être moqués était complètement foirée 🤦‍♂

Essentiellement
Si 2 fonctions sont dans le même module, et s'appellent

Si foo appelle bar

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

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

Mettez bar dans un nouveau fichier (à moquer)

J'ai déplacé la méthode bar dans un nouveau fichier, et maintenant je pourrais utiliser plusieurs des exemples ci-dessus
Un grand merci à @yoni-abtech de me faire comprendre ça 🤣

La façon dont je le vois après avoir lu tout ce fil et testé maintes et maintes fois, il y a 3 options ....

Option 1 - déclarer toutes les fonctions en utilisant const

Cela vous oblige à imposer l'utilisation d' expressions de déclarations . Heureusement, la règle func-style eslint vous soutient.

L'utilisation de export const vous permet de spyOn fonctions qui sont utilisées par d'autres fonctions à partir du module _same_ .

// 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 - Utiliser le plugin rewire babel

Si vous ne voulez pas imposer l'utilisation d'expressions de fonction (c'est-à-dire en utilisant const ), cela pourrait être une bonne approche.

Cela vous permet de _rewire_ (alias mock) les fonctions du même module. Je pourrais imaginer que le code ressemblerait à quelque chose comme ça ci-dessous, mais je ne l'ai pas testé. De plus, à partir de leurs documents, il semble que vous puissiez recâbler des fonctions dans le même module qui ne sont même pas exportées depuis le module 👍, imaginez l'exemple ci-dessous sans exporter la fonction message .

Exemple:

// 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');
  });
});

Voir la doc pour des exemples.

Remarque : Nécessite une transpilation babel

Option 3 - Séparez toutes les fonctions en modules/fichiers séparés

Cette option est la moins favorable mais fonctionnerait clairement très bien avec la fonctionnalité typique mock .


PS : Bien que cette piste ait été très instructive et souvent divertissante, j'espère que ce synopsis atténue le besoin pour les autres de lire l'intégralité du fil. ✌️

Merci @nickofthyme , vous venez de terminer quelques jours à me cogner la tête contre ça.

@nickofthyme votre option 1 échoue dans mon application, et create react app :

 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 Vous semblez avoir raison. Désolé, j'aurais dû tester cela avec CRA.

J'ai obtenu que la moquerie décrite dans l'option 1 fonctionne ici . Testez-le en utilisant le script yarn test:hello .

Résultat

> 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.

Cela nécessite d'utiliser un fichier jest.config.js en utilisant ts-jest et en appelant directement jest --config=./jest.config.js , pas via react-scripts . Je ne sais pas comment jest est configuré dans le react-scripts mais je pense qu'il peut y avoir un moyen de mettre à jour la configuration d'une manière ou d'une autre.

Ce correctif supprime les transformations pour les fichiers *.css et *.svg , donc ignorez les erreurs App.tsx .

Y a-t-il quelque chose de particulier à faire pour que cela fonctionne?
Je dirais que j'ai une configuration assez standard (sans ts) et que cela ne fonctionne pas immédiatement.

Je vais regarder ça un peu plus ce soir et voir si c'est possible.

@danielhusar J'ai examiné la nuit dernière et je n'ai pas pu obtenir de solution transformer que CRA vous permet de remplacer dans package.json#jest . Les fichiers js et ts sont transpilés à l'aide de babel-jest mais react-scripts vous empêche d'utiliser un fichier de configuration .babelrc et de définir l' env test qu'ils ont défini dans react-scripts test ici .

J'aimerais pouvoir creuser plus profondément mais je n'ai pas le temps en ce moment.

Hmm, j'ai encore du mal à le faire fonctionner (sur ma configuration personnalisée, pas sur le cra).
(dernière version de jest et babel-jest)

C'est ma config de plaisanterie :

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',
  },
};

Et ma babelrc :

{
  "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"]
    }
  }
}

Pour toute personne aux prises avec l' option 1 , il est important d'utiliser la fonction () => { return expression } au lieu de la fonction () => (expression) .

J'ai fait fonctionner l'option 1 en la modifiant en :

import * as test from './test';

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

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

Pas joli mais ça devrait marcher.

@nickofthyme , l' option 1 est correcte telle que vous l'avez. Mais si vous changez le code en :

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

Puis ça casse. Vraisemblablement parce que vous créez un nouveau littéral d'objet et l'exportez.

Observation intéressante. Merci @maletor

Jest a un exemple très simple et clair dans sa documentation sur la façon de se moquer partiellement d'un module. Cela fonctionne à la fois avec les instructions ES import et Node require.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

@johncmunson C'est un bon point. Cependant, cet exemple que vous avez montré de moquerie d'un module ne fonctionne que si vous n'avez besoin d'exécuter jest.mock _une fois_ et qu'aucune des méthodes moquées n'utilise une autre exportation de ce module.

Prenez l'exemple ci-dessus... J'ai ajouté bar pour montrer comment je veux me moquer du module différemment entre foo et bar .

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

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

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

Utiliser jest.mock avec jest.requireActual je pense que j'irais quelque chose comme ça.

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
  });
});

J'ai même essayé de les moquer séparément avec jest.doMock et j'ai toujours obtenu le même résultat.


Cliquez pour voir le code

```ts
importer * en tant que module de test depuis './hello' ;

describe('test bonjour', fonction () {
aprèsTout(() => {
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
});

});
```

Le problème avec cette approche est que le fait d'exiger le module réel, puis d'appeler foo , appelle toujours la fonction réelle message et non la simulation.

J'aimerais que ce soit aussi simple, mais d'après ce que je vois, cela n'aide pas pour les exemples de ce fil. S'il me manque quelque chose ici, veuillez me le faire savoir. J'admettrai volontiers ma faute.

Pour tous ceux qui rencontrent cela à la recherche d'une solution, ce qui suit semble fonctionner pour moi lors de l'exportation de nombreuses const/fonctions dans un fichier et de leur importation dans un fichier que je 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');
`

Ce qui suit fonctionne et est un peu plus court :

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

En Typescript, juste import au lieu de require :

import * as module from './module';

Cela a l'avantage de rendre la vie facile pour restaurer les fonctions d'origine et effacer les simulacres.

Pour tous ceux qui rencontrent cela à la recherche d'une solution, ce qui suit semble fonctionner pour moi lors de l'exportation de nombreuses const/fonctions dans un fichier et de leur importation dans un fichier que je 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');
`

Ce qui suit fonctionne et est un peu plus court :

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

En Typescript, juste import au lieu de require :

import * as module from './module';

Cela a l'avantage de rendre la vie facile pour restaurer les fonctions d'origine et effacer les simulacres.

Oh, oui aussi cette méthode ne fonctionne pas si votre objet n'a défini que getter . Le message d'erreur pourrait être comme ci-dessous :

Test suite failed to run

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

Probablement besoin d'utiliser jest.mock(..) pour ce cas. :bowing_man:

Mes simulations fonctionnent en utilisant les éléments suivants :

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()
})

Cela ne semble pas si élégant et ne s'adapte pas si facilement, mais cela fonctionne très bien et convient à mon cas pour l'instant.. mais si quelqu'un peut suggérer une autre syntaxe, ce serait génial! C'est la seule syntaxe qui semble fonctionner.

code du module es6 :

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

Après transpilation vers 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;

Il existe de nombreuses façons de résoudre cette situation.

  1. Vous devez changer le code comme ceci, vous pouvez donc utiliser le funcA moqué/espionné
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;

Ou,

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

résultats des tests unitaires :

 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. utiliser le package rewire pour se moquer de funcA
    ...

De plus, vous devez jeter un œil à cette documentation : https://nodejs.org/api/modules.html#modules_exports_shortcut , pour voir ce que fait exactement require

La solution dans ce post stackoverflow a fonctionné pour moi
https://stackoverflow.com/a/53402206/1217998

Fondamentalement, vous convertissez d'abord toutes les fonctions que vous souhaitez convertir en jest.fn

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

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

Ensuite, vous pouvez l'utiliser comme d'habitude si vous le souhaitez ou vous en moquer comme ça

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

Vous pouvez l'utiliser pour obtenir l'implémentation d'origine

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

La solution dans ce post stackoverflow a fonctionné pour moi
https://stackoverflow.com/a/53402206/1217998

Fondamentalement, vous convertissez d'abord toutes les fonctions que vous souhaitez convertir en jest.fn

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

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

Ensuite, vous pouvez l'utiliser comme d'habitude si vous le souhaitez ou vous en moquer comme ça

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

Vous pouvez l'utiliser pour obtenir l'implémentation d'origine

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

Merci!

Jest a un exemple très simple et clair dans sa documentation sur la façon de se moquer partiellement d'un module. Cela fonctionne à la fois avec les instructions ES import et Node require.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

Ne fonctionne pas lorsque la fonction simulée est appelée depuis le module.

De plus, j'ai trouvé qu'il peut parfois être utile de simuler la fonction de la même manière que vous ne modifiez pas la fonction d'origine mais appelez la fonction avec des variables personnalisées (supplémentaires):

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

Dans mon cas, j'avais besoin de passer la configuration pour fonctionner sans laquelle elle utiliserait celle par défaut.

C'est la seule façon que j'ai trouvée de le faire, donc si vous avez des idées meilleures ou plus faciles, je serai ravi de les entendre :)

FWIW J'ai rassemblé diverses approches avec des exemples exécutables dans https://github.com/magicmark/jest-how-do-i-mock-x/blob/master/src/function-in-same-module/README. Maryland

Cela ne répond pas à la question/au problème du PO, mais c'est une solution avec une certaine refactorisation. J'ai trouvé que séparer mes fonctions dans différents fichiers, puis se moquer de ces importations, est la chose la plus simple à faire.

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

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

```js
// babel.config.js

module.exports = {
préconfigurations: [
[
'@babel/preset-env',
{
cibles : {
nœud : 'actuel',
},
},
],
],
} ;

```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)

J'ai récemment rencontré ce problème. Aucune des solutions proposées ne fonctionne pour moi car je ne parviens pas à modifier le code. Le babel-plugin-rewire ne fonctionne pas non plus pour moi. Existe-t-il d'autres solutions pour tester qu'une fonction a été appelée par une autre fonction dans le même module ? Cela semble honnêtement que cela devrait fonctionner ou qu'il devrait y avoir un plugin babel qui le fait. Toute aide serait très appréciée!

J'ai récemment rencontré ce problème. Aucune des solutions proposées ne fonctionne pour moi car je ne parviens pas à modifier le code. Le babel-plugin-rewire ne fonctionne pas non plus pour moi. Existe-t-il d'autres solutions pour tester qu'une fonction a été appelée par une autre fonction dans le même module ? Cela semble honnêtement que cela devrait fonctionner ou qu'il devrait y avoir un plugin babel qui le fait. Toute aide serait très appréciée!

Avez-vous consulté https://github.com/facebook/jest/issues/936#issuecomment -659597840 ? Il y a une reproduction minimale qui se moque des appels de fonctions dans le même fichier.

Cette page vous a été utile?
0 / 5 - 0 notes