webentwicklung-frage-antwort-db.com.de

So ändern Sie die Scheinimplementierung pro Test [Jestjs]

Ich möchte die Implementierung einer verspotteten Abhängigkeit ändern auf einer pro einzelnen Testbasis um das Verhalten der Standard-Verspottung erweitern und es rückgängig machen zurück zur ursprünglichen Implementierung, wenn der folgende Test ausgeführt wird.

Kurz gesagt, das versuche ich zu erreichen:

  1. mock abhängigkeit
  2. Scheinimplementierung ändern/erweitern in einem einzigen Test
  3. zurücksetzen zum ursprünglichen Mock, wenn der nächste Test ausgeführt wird

Ich verwende derzeit Jest v21.

So würde ein typischer Scherztest aussehen:

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

Ich habe ein paar Strategien ausprobiert, aber keine Lösung gefunden, die ich als zufriedenstellend bezeichnen könnte.


1 - mockFn.mockImplementationOnce (fn)

Profis

  • Kehrt nach dem ersten Aufruf zur ursprünglichen Implementierung zurück

Nachteile

  • Es wird unterbrochen, wenn der Test mehrmals b aufruft
  • Die ursprüngliche Implementierung wird erst wiederhergestellt, wenn b nicht aufgerufen wird (undicht im nächsten 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 (Modulname, Werkseinstellung, Optionen)

Profis

  • Verspottet jeden Test explizit erneut

Nachteile

  • Es kann keine Standard-Mock-Implementierung für alle Tests definiert werden
  • Eine Standardimplementierung, die die Deklaration jeder gespielten Methode erzwingt, kann nicht erweitert werden

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 - Manuelles Verspotten mit Setter-Methoden (wie erklärt hier )

Profis

  • Vollständige Kontrolle über verspottete Ergebnisse

Nachteile

  • Posten Kesselschild-Code
  • Langfristig schwer zu pflegen

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 (Objekt, Methodenname)

Nachteile

  • Ich kann mockImplementation nicht auf den ursprünglichen verspotteten Rückgabewert zurücksetzen, was sich auf die folgenden Tests auswirkt

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

Vielen Dank im Voraus für jede Eingabe/Anregung!

18
Andrea Carraro

Ein schönes Muster für den Schreibtest ist das Erstellen einer Setup-Factory-Funktion, die die Daten zurückgibt, die Sie zum Testen des aktuellen Moduls benötigen.

Nachfolgend finden Sie einige Beispielcodes, die Ihrem zweiten Beispiel folgen. Sie können jedoch Standardwerte und Überschreibungswerte wiederverwenden.

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

Etwas spät zur Party, aber wenn jemand anderes Probleme damit hat.

Wir verwenden TypeScript, ES6 und Babel für die reaktionsgenaue Entwicklung.

In der Regel machen wir externe NPM-Module im Stammverzeichnis __mocks__ nach.

Ich wollte eine bestimmte Funktion eines Moduls in der Auth-Klasse von aws-amplify für einen bestimmten Test überschreiben.

    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

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

2

Verwenden Sie mockFn.mockImplementation (fn) .

Setzen Sie die Standardimplementierung in beforeEach. Das Mock wird vor jedem Test darauf zurückgesetzt.

Verwenden Sie zum Überschreiben mockImplementation im Test.

Dadurch wird das Verhalten des Modells für alle/alle Aufrufe im Test überschrieben und vor dem nächsten Test durch die beforeEach-Implementierung überschrieben.

Zum Beispiel:

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