web-dev-qa-db-fra.com

Comment changer d'implémentation factice test par test [Jestjs]

J'aimerais modifier modifier l'implémentation d'une dépendance simuléesur un base de test uniquepar étendre le comportement par défaut de mocket srevenir à l'implémentation d'originelorsque le test suivant s'exécute.

Plus brièvement c'est ce que j'essaie de réaliser:

  1. mockdependency
  2. changer/étendre la maquetteen un seul test
  3. retourneà l'original lors de l'exécution du prochain test

J'utilise actuellement Jest v21.

Voici à quoi ressemblerait un test Jest typique:

__mocks__/myModule.js

const myMockedModule = jest.genMockFromModule('../myModule');

myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);

export default myMockedModule;

__tests__/myTest.js

import myMockedModule from '../myModule';

// Mock myModule
jest.mock('../myModule');

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

describe('MyTest', () => {
  it('should test with default mock', () => {
    myMockedModule.a(); // === true
    myMockedModule.b(); // === true
  });

  it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
    // Extend change mock
    myMockedModule.a(); // === true
    myMockedModule.b(); // === 'overridden'
    // Restore mock to original implementation with no side eeffects
  });

  it('should revert back to default myMockedModule mock', () => {
    myMockedModule.a(); // === true
    myMockedModule.b(); // === true
  });
});

J'ai essayé quelques stratégies, mais je n'ai trouvé aucune solution que je puisse définir satisfaisante.


1 - mockFn.mockImplementationOnce (fn)

pros

  • Revenir à l'implémentation d'origine après le premier appel

contre

  • Cela cassera si le test appelle b plusieurs fois
  • Il ne reviendra pas à l'implémentation d'origine tant que la variable b n'aura pas été appelée (fuite lors du prochain test)

code:

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  myMockedModule.b.mockImplementationOnce(() => 'overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});

2 - jest.doMock (nomModule, usine, options)

pros

  • Re-moque explicitement à chaque test

contre

  • Impossible de définir une implémentation factice par défaut pour tous les tests
  • Impossible d'étendre une implémentation par défaut, obligeant à re-déclarer chaque méthode simulée

code:

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  jest.doMock('../myModule', () => {
    return {
      a: jest.fn(() => true,
      b: jest.fn(() => 'overridden',
    }
  });

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});

3 - Mocking manuel avec méthodes setter (comme expliqué ici )

pros

  • Contrôle complet des résultats simulés

contre

  • Lot de code standard
  • Difficile à maintenir à long terme

code:

__mocks__/myModule.js

const myMockedModule = jest.genMockFromModule('../myModule');

let a = true;
let b = true;

myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);

myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
  a = true;
  b = true;
};
export default myMockedModule;

__tests__/myTest.js

it('should override myModule.b mock result (and leave the other methods untouched)', () => {
  myModule.__setB('overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'

  myModule.__reset();
});

4 - jest.spyOn (object, methodName)

contre

  • Je ne peux pas revenir mockImplementation à la valeur de retour fictive d'origine, ce qui affecte les tests suivants

code:

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

// Mock myModule
jest.mock('../myModule');

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');

  myMockedModule.a(); // === true
  myMockedModule.b(); // === 'overridden'

  // How to get back to the original mocked value?
});

Merci d'avance pour toute contribution/suggestion!

18
Andrea Carraro

Un bon modèle pour le test d’écriture consiste à créer une fonction d’usine d’installation qui renvoie les données nécessaires au test du module actuel.

Vous trouverez ci-dessous un exemple de code qui suit votre deuxième exemple, bien qu'il permette de fournir des valeurs par défaut et des valeurs de remplacement de manière réutilisable.

const spyReturns = returnValue => jest.fn(() => returnValue);

describe("scenario", () => {
  const setup = (mockOverrides) => {
    const mockedFunctions =  {
      a: spyReturns(true),
      b: spyReturns(true),
      ...mockOverrides
    }
    return {
      mockedModule: jest.doMock('../myModule', () => mockedFunctions);
    }
  }

  it("should return true for module a", () => {
    const {mockedModule} = setup();
    expect(mockedModule.a()).toEqual(true)
  });

  it("should return override for module a", () => {
    const EXPECTED_VALUE = "override"
    const {mockedModule} = setup({ a: spyReturns(EXPECTED_VALUE)});

    expect(mockedModule.a()).toEqual(EXPECTED_VALUE)
  })
});
6
user1095118

Un peu tard pour la fête, mais si quelqu'un d'autre a des problèmes avec cela.

Nous utilisons TypeScript, ES6 et babel pour le développement réactif natif.

Nous modifions généralement les modules NPM externes dans le répertoire racine __mocks__.

Je voulais remplacer une fonction spécifique d'un module de la classe Auths aws-amplify pour un test spécifique.

    import { Auth } from 'aws-amplify';
    import GetJwtToken from './GetJwtToken';
    ...
    it('When idToken should return "123"', async () => {
      const spy = jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
        getIdToken: () => ({
          getJwtToken: () => '123',
        }),
      }));

      const result = await GetJwtToken();
      expect(result).toBe('123');
      spy.mockRestore();
    });

Gist: https://Gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2

Tutoriel: https://medium.com/p/b4ac52a005d#19c5

2
Thomas Hagström

Utilisez mockFn.mockImplementation (fn) .

Mettez l'implémentation par défaut dans la beforeEach. La maquette sera réinitialisée à cela avant chaque test.

Pour remplacer, utilisez mockImplementation dans le test.

Cela annulera le comportement de la maquette pour tous les appels du test et sera écrasé par l'implémentation beforeEach avant le prochain test.

Par exemple:

import { funcToMock } from './somewhere';
jest.mock('./somewhere');

beforeEach(() => {
  funcToMock.mockImplementation(() => { /* default implementation */ });
});

test('case that needs a different implementation of funcToMock', () => {
  funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
  // ...
});
0
A Jar of Clay