<p>L'usine jest.mock ne fonctionne pas dans un test</p>

Créé le 12 janv. 2017  ·  38Commentaires  ·  Source: facebook/jest

Vous souhaitez demander une fonctionnalité ou signaler un bug ?
Bogue

Quel est le comportement actuel ?

Il semble que la façon de créer une maquette avec une usine ne fonctionne pas dans test ou it . Cela ne fonctionne que lorsque le mock est défini au niveau racine du fichier.

Voici mon exemple de maquette :

jest.mock('services/feature', () => ({
    isEnabled: () => true
}));

Quel est le comportement attendu ?

Se moquer d'un fichier dans un test devrait fonctionner.

Veuillez fournir votre configuration exacte de Jest et mentionner votre Jest, votre nœud, votre version de fil/npm et votre système d'exploitation.

Jest 18.0.0, nœud 7.4, macOS

Confirmed Discussion

Commentaire le plus utile

Pour modifier la valeur de retour d'un simulacre entre les tests, vous pouvez faire quelque chose comme ceci :

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

Tous les 38 commentaires

jest.mock appels jest.doMock . Avez-vous essayé cela?

Même chose avec doMock .

Dans les docs, je peux lire

Remarque : Lorsque vous utilisez babel-jest , les appels à simuler seront automatiquement hissés en haut du bloc de code. Utilisez doMock si vous voulez explicitement éviter ce comportement.

Mais... Un test est un bloc de code, non ? Donc dans mon cas, je ne m'attends pas à voir de différences.

Voici un test complet

it('renders with the enabled feature', () => {
  jest.mock('services/feature', () => ({
      isEnabled: () => true
  }));

  const component = renderer.create(
      <MyComponent />
  );

  const tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

Pourriez-vous fournir une reproduction de cela dans un référentiel GH ?

Sûr. Le voici : https://github.com/tleunen/jest-issue-2582

Les deux tests rendent "Désactivé" même si l'un d'entre eux a alors une simulation pour rendre "Activé" à la place.
Voir ceux-ci :
https://github.com/tleunen/jest-issue-2582/blob/master/src/MyComponent.js
https://github.com/tleunen/jest-issue-2582/blob/master/src/__tests__/MyComponent.spec.js

Merci.

Un conseil @thymikee @cpojer pour ce problème ?
J'ai plusieurs tests dans le même fichier et j'aimerais avoir des réponses fictives différentes pour chacun d'eux.

Heureux d'avoir trouvé ce problème, je me casse la tête pourquoi jest.mock() ne fonctionnait pas dans mon champ describe . Je l'ai déplacé vers le haut (en dessous de mes importations dans le fichier de test) et cela fonctionne.

Pour moi, cela s'applique également à jest.mock() sans usine, en utilisant un dossier __mocks__ contenant le fichier simulé.

--Éditer

Il est évidemment nécessaire de hisser l'instruction jest.mock() avant les instructions import. Cependant @tleunen , cela signifie probablement qu'il n'est pas possible de se moquer du même fichier plus d'une fois, avec des réponses différentes. Peut-être que vous êtes mieux servi en rendant la fonction d'usine plus dynamique, afin qu'elle puisse vous fournir des résultats différents dans chaque cas de test.

Dans mon esprit, cela le rendrait plus explicite si jest.mock() était toujours mis en dehors des blocs describe et it . Mais cela devrait alors être clairement indiqué dans la doc

Donc jest.mock est hissé à la portée de la fonction, c'est pourquoi cela ne fonctionnera pas avec les require (et certainement pas les import qui sont hissés à la portée du module) si vous appelez-le dans une fonction autre que describe (qui est spécialement traitée par Jasmine).
Votre appel jest.mock sera hissé au sommet de cette même fonction (pas du module), c'est pourquoi cela ne fonctionnera pas comme vous le souhaitez.

En général, nous vous conseillons de configurer différentes simulations dans beforeEach et afterEach si vous voulez qu'elles soient différentes selon les cas de test.

@cpojer pourrait expliquer cela en détail, et si nous voulons hisser les appels vers les portées supérieures.

C'est parce que vous avez besoin de vos modules lors de l'initialisation du module (à l'aide de l'importation). jest.mock est appelé bien plus tard. La façon de résoudre ce problème est :

beforeEach(() => { // or the specific test
  jest.mock('MyModule', () => …);
  const MyModule = require('MyModule');
  …
});

etc.

Si vous deviez faire cela avantChaque, je ne sais pas comment vous différencieriez les tests (alors, comment donneriez-vous une simulation différente pour chaque test ?)

Le mettre à l'intérieur des tests eux-mêmes fonctionne bien sûr.

Pouvez-vous ensuite passer le module simulé à l'intérieur du composant testé ?

Que se passe-t-il si je n'importe pas/exige le fichier que je veux simuler (par exemple une dépendance d'un autre fichier que j'importe) mais que je veux qu'il soit limité à un bloc describe/it ? Ou même si je veux me moquer différemment pour le test beforeEach/beforeAll ? Ces cas sont-ils possibles ?

// A.js depends on B.js
import A from './A';

describe('myTest', () => {

    describe('myFirstScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                myFirstMethod: jest.fn(),
            }));
        });

        // tests here
    });

    describe('mySecondScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                mySecondMethod: jest.fn(),
            }));
        });

        // tests here
    });
});

Dans ce cas, vous devez exiger A après avoir moqué B. (N'utilisez pas import , mais require ).

exiger A après s'être moqué de B

n'a pas fonctionné pour moi, voyait toujours la maquette originale de B passer au résultat

@GoldAnna avez-vous trouvé une solution de contournement pour ce problème ?

@alayor je n'ai pas

Autant de fois que j'ai rencontré ce problème, je suis maintenant convaincu que je ne teste pas correctement, que je n'utilise pas jest comme l'avaient prévu les auteurs, ou une combinaison des deux. Une autre preuve est que presque aucun des exemples liés à des simulations dans la documentation de jest ne me semble être des exemples du monde réel... ils le sont probablement et mon approche est probablement incorrecte.

Cela dit, ce qui suit fonctionne pour moi et tous les tests suivants réussissent. Notez que pour revenir à la version originale de ModuleB , je dois appeler à la fois jest.resetModules() et jest.unmock('./moduleB') ... l'ordre de ceux-ci n'a pas d'importance.

// Module A
const ModuleB = require('./moduleB');
const ModuleA = function() {
  this.title = new ModuleB().title;
  return this;
};
module.exports = ModuleA;

// Module B
const ModuleB = function() {
  this.title = 'Module B - Original'
  return this;
};
module.exports = ModuleB;

// Tests
describe('Jest a few tests', () => {
  it('should do something', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 1'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 1');
  });

  it('should do something else', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 2'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 2');
  });

  it('should do something original', () => {
    jest.resetModules();
    jest.unmock('./moduleB');
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Original');
  });
});

Hey,

Rien ici ne fonctionne pour moi.
Quelqu'un peut-il expliquer pourquoi il n'est pas possible d'utiliser jest.mock à l'intérieur?

Merci

Je pense que c'est toujours un problème qui devrait peut-être être rouvert en tant que demande de fonctionnalité. Je veux écrire des tests de manière isolée. Vider des choses dans un dossier de simulations ou recâbler avec un avantChacun se termine généralement par un tiroir indésirable d'instanciations de données fictives/étranges qui se trimballe dans chaque test. Je veux écrire une petite maquette et faire un petit test.

Nous n'avons aucun moyen réel d'isoler les tests individuels (considérant test.concurrent ). Si nous adoptions une API similaire à tap ou ava, ce serait possible, mais je ne pense pas que ce soit possible avec les contraintes imposées par l'API mondiale actuelle. Super heureux d'avoir tort !

J'ai eu du mal avec plusieurs méthodes ici pour refactoriser un test précédemment écrit en moka et proxyquire, pour finir par séparer le test en différents fichiers pour différentes simulations.

@gcox Avez-vous essayé de vous moquer (et d'exiger le moduleA après) au niveau supérieur du module, puis de modifier l'implémentation de la simulation pour chaque test nécessitant un comportement différent? Vous pouvez utiliser mockImplementation pour le faire. C'est ainsi que nous avons résolu ce problème dans nos suites de tests.

@antgonzales Je pense que c'est difficile à faire si les importations de modules sont hissées au sommet d'un module. Modifier les références d'un module précédemment importé semble non trivial voire impossible dans node.

@jkomusin Certainement. Ce n'est tout simplement pas ce que le PO demandait, OMI. Je crois qu'ils demandaient, en particulier, "Comment puis-je forcer require('whatever') à renvoyer une simulation différente sur plusieurs tests adjacents plutôt que sur le même objet ?".

Pour modifier la valeur de retour d'un simulacre entre les tests, vous pouvez faire quelque chose comme ceci :

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

La réponse @SimenB a également fonctionné et est tellement plus simple!

@rafaeleyng mais @SimenB ne fonctionne pas si ce que vous exportez depuis le module n'est pas une fonction...

L' exemple

@schumannd a raison. Veuillez rendre la documentation officielle plus claire et nette.

Les PR sont toujours les bienvenus pour améliorer les documents 🙂

Exemple de module basé sur
https://github.com/facebook/jest/issues/2582#issuecomment -378677440 ❤️

Heya, j'ai trouvé ceci via le problème lié et je l'ai compris:

jest.mock('child_process')
const childProcess = require('child_process')

describe('foo', () => {
  test('bar', () => {
    childProcess.execSync.mockImplentation(jest.fn().mockReturnValueOnce('wibble'))
    // code that itself requires child_process
    expect(childProcess.execSync.mock.results[0]).toEqual('wibble')
  })

  test('baz', () => {
    childProcess.execSync.mockImplentation(jest.fn().mockReturnValueOnce('wobble'))
    // code that itself requires child_process
    expect(childProcess.execSync.mock.results[0]).toEqual('wobble')
  })
})

Si vous devez simuler plusieurs fonctions/méthodes sur le code requis, vous devez les configurer séparément au lieu d'utiliser l'usine lorsque vous le faites globalement.

Si comme moi, vous n'avez besoin que d'une seule fonction, vous pouvez tout simplifier :

jest.mock('child_process')
const { execSync } = require('child_process')

describe('foo', () => {
  test('bar', () => {
    execSync.mockImplentation(jest.fn().mockReturnValueOnce('wibble'))
    // code that itself requires child_process
    expect(execSync.mock.results[0]).toEqual('wibble')
  })

  test('baz', () => {
    execSync.mockImplentation(jest.fn().mockReturnValueOnce('wobble'))
    // code that itself requires child_process
    expect(execSync.mock.results[0]).toEqual('wobble')
  })
})

qu'en est-il de la moquerie des dépendances non fonctionnelles comme les fichiers json ou les constantes mappées. Veuillez rendre la documentation plus claire sur la façon de gérer cela ...

@gcox Votre solution est la seule que j'ai trouvée lorsqu'un module importé en cours de test importe un autre module pour lequel j'ai eu une simulation manuelle dans un dossier __mocks__ .

Par exemple, le fichier de test appelle jest.mock('./ModuleA') , qui a une simulation dans __mocks__/ModuleA.js . Mais ModuleA n'est pas en cours de test, ModuleB - qui nécessite ModuleA - est ce qui est en cours de test. Sans votre solution, ModuleB obtiendrait l'implémentation réelle de ModuleA, pas la simulation.

Cela semble être un comportement vraiment étrange de la part de la plaisanterie. Je m'attendrais à ce que lorsque j'appelle jest.mock sur un module, tout module en cours de test qui a une dépendance sur le module moqué utilise le mock. Cela semble-t-il bizarre que cela ne fonctionne pas de cette façon, ou est-ce que je m'y prends complètement de manière incorrecte ?

@SimenB Dans votre exemple, vous avez besoin du module

@SimenB a travaillé votre solution plus simple pour moi. Merci! Wish pourrait le trouver plus tôt avant que je passe du temps avec de nombreuses façons différentes

Dans mon cas, je me moquais et importais le mauvais chemin vers le module que je voulais remplacer. J'avais une casse différente dans le chemin du fichier, donc mon éditeur a pensé que tout allait bien, mais qu'il échouait.

Était

const { stubMethod } = require("./path/to/File");
jest.mock("./path/to/File");

Remplacé par (changer la casse de File en file ):

const { stubMethod } = require("./path/to/file");
jest.mock("./path/to/file");

J'espère que ceci aide quelqu'un d'autre.

Jasmine spyOn() n'a pas ce problème _dans_ les tests. Jest utilise soi-disant le jasmin par défaut ? Pourquoi cela ne fonctionne-t-il pas à l'intérieur du test ?

Exemple de jasmin :

spyOn(require('moduleB'), 'functionA').and.callFake(() => true);

Ce que je ne comprends pas très bien en ce qui concerne les modules moqueurs, c'est qu'il est toujours décrit comme si vous vouliez utiliser le module principalement dans vos fonctions de test. Par exemple, si vous regardez l'exemple de code pour la fonction doMock dans la doc :

test('moduleName 2', () => {
  jest.doMock('../moduleName', () => {
    return {
      __esModule: true,
      default: 'default2',
      foo: 'foo2',
    };
  });
  return import('../moduleName').then(moduleName => {
    expect(moduleName.default).toEqual('default2');
    expect(moduleName.foo).toEqual('foo2');
  });
});

Dans cet exemple, vous avez accès à la nouvelle version simulée de "moduleName" à l'intérieur de la fonction de test uniquement. Pour moi, il est bien plus important que mon code "non-test" ait également accès à la version simulée du module. Par exemple, une classe que j'utilise dans mon test, qui a importé "moduleName" utiliserait toujours la version originale, pas la version simulée.

Semble être lié : https://github.com/facebook/jest/issues/3236

Astuce - si vous avez un module qui exporte une valeur primitive, par exemple :

```auth.js
export const isUserAdmin = getSetting('admin');

And you want to use a mock value instead, in the test, then a simple require and assignment seems to do the trick:

```deleteUser.js
const auth = require('../auth');

describe('deleteUser', () => {
  it('can delete if admin', () => {
    auth.isUserAdmin = true;

    // call method that depends on isUserAdmin value
    // and assert on the result
  });
});

Donc en gros, si vous voulez vous moquer d'un objet qui est utilisé indirectement par le code que vous testez, la fonction jest.doMock() fonctionnera pas :

import myModuleToTest from './myModuleTotest'

describe('Given my module', () => {
  it('property1 will work as expect', () => {
    // Testing parts of the module that don't need to be mocked
  })

  it('property2 will work as expected', () => {
    jest.doMock('./myOtherModule', () => {
      return {
        __esModule: true,
        default: 'default2',
        foo: 'foo2',
      };
    });

    import('./myOtherModule').then(myOtherModule => {
      // I'm not interested on the mocked module myOtherModule but on the module that makes use of it
      myModuleToTest.doSomethingToSomeProperty(); // At this point myOtherModule's original module and not its mocked version will be used by myModuleToTest
      expect(myModuleToTest.someProperty).toBe('thisWillFail'); // The test won't pass because the mocked version wasn't used
    });
  });
});

À partir de Jest 26, il n'y a aucun moyen de se moquer plus d'une fois d'un module exportant un Object qui est utilisé indirectement (je veux dire se moquer d'autre chose qu'un Function car il n'y a pas de mockFn.mockImplementation(fn) pour Objects moqué). Est-ce correct ou j'ai raté quelque chose ? La seule solution est alors d'avoir plus d'un fichier de test pour tester le même module.

Vous devez importer myModuleToTest après votre maquette car s'il importe
avant la moquerie n'a pas de sens. Donc, n'utilisez pas import .... sur le dessus ou
à l'intérieur du rappel parce qu'il est de toute façon hissé.

Le mar 7 juil. 2020, 22:24 Antonio Redondo [email protected]
a écrit:

Donc en gros, si vous voulez vous moquer d'un objet qui est utilisé indirectement par le
le code que vous testez la fonction jest.doMock() ne fonctionnera pas :

importer myModuleToTest depuis './myModuleTotest'
it('fonctionnera', () => {
jest.doMock('./monAutreModule', () => {
revenir {
__esModule : vrai,
par défaut : 'default2',
foo: 'foo2',
} ;
});

return import('../myOtherModule').then(myOtherModule => {
// Je ne suis pas intéressé par le module moqué myOtherModule mais par le module qui l'utilise
myModuleToTest.doSomethingToSomeProperty(); // À ce stade, le module d'origine de myOtherModule et non sa version simulée sera utilisé par myModuleToTest
expect(myModuleToTest.someProperty).toBe('thisWillFail'); // Le test ne passera pas car la version simulée n'a pas été utilisée
});});

À partir de Jest 26, il n'y a aucun moyen de se moquer d'un module exportant un objet (je
signifie autre chose qu'une fonction) qui est utilisé indirectement. Est-ce
correct ou j'ai raté quelque chose? La seule solution alors est d'avoir plus
plus d'un fichier de test pour tester le même module.

-
Vous recevez ceci parce que vous avez commenté.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/facebook/jest/issues/2582#issuecomment-655110424 , ou
Se désabonner
https://github.com/notifications/unsubscribe-auth/AAJR3UW6HARW44ZLKAUB7PLR2N77NANCNFSM4C4I7QSQ
.

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