2016-09-22 4 views
14

Ich erstelle ein Projekt mit create-app-component, das eine neue App mit Build-Skripten (Babel, Webpack, Jest) konfiguriert.Kann ein Modul nicht mit Jest verhöhnen und Funktionsaufrufe testen

Ich schrieb eine React-Komponente, die ich versuche zu testen. Die Komponente benötigt eine andere Javascript-Datei, die eine Funktion freigibt.

Mein Search.js Datei

export { 
    search, 
} 

function search(){ 
    // does things 
    return Promise.resolve('foo') 
} 

Meine Komponente reagieren:

import React from 'react' 
import { search } from './search.js' 
import SearchResults from './SearchResults' 

export default SearchContainer { 
    constructor(){ 
    this.state = { 
     query: "hello world" 
    } 
    } 

    componentDidMount(){ 
    search(this.state.query) 
     .then(result => { this.setState({ result, })}) 
    } 

    render() { 
    return <SearchResults 
      result={this.state.result} 
      /> 
    } 
} 

In meinen Unit-Tests, ich, dass die Methode search überprüfen will mit den richtigen Argumenten aufgerufen wurde.

Meine Tests in etwa so aussehen, dass:

import React from 'react'; 
import { shallow } from 'enzyme'; 
import should from 'should/as-function'; 

import SearchResults from './SearchResults'; 

let mockPromise; 

jest.mock('./search.js',() => { 
    return { search: jest.fn(() => mockPromise)}; 
}); 

import SearchContainer from './SearchContainer'; 

describe('<SearchContainer />',() => { 
    it('should call the search module',() => { 
    const result = { foo: 'bar' } 
    mockPromise = Promise.resolve(result); 
    const wrapper = shallow(<SearchContainer />); 

    wrapper.instance().componentDidMount(); 

    mockPromise.then(() => { 
     const searchResults = wrapper.find(SearchResults).first(); 
     should(searchResults.prop('result')).equal(result); 
    })  
    }) 
}); 

Ich hatte schon eine harte Zeit, herauszufinden, wie jest.mock Arbeit zu machen, weil es Variablen erfordert von mock vorangestellt werden.

Aber wenn ich Argumente zu der Methode search testen möchte, muss ich die verspottete Funktion in meinen Tests verfügbar machen.

Wenn ich den spöttischen Teil verwandeln, um eine Variable zu verwenden:

const mockSearch = jest.fn(() => mockPromise) 
jest.mock('./search.js',() => { 
    return { search: mockSearch}; 
}); 

ich diesen Fehler:

TypeError: (0 , _search.search) is not a function

Was auch immer ich versuche, den Zugang zum jest.fn zu haben, und die Argumenten testen, ich kann es nicht funktionieren lassen.

Was mache ich falsch?

Antwort

24

Das Problem

Der Grund Sie diesen Fehler immer zu tun hat mit, wie verschiedene Operationen gehisst werden.

Obwohl in Ihrem ursprünglichen Code nur Sie importieren SearchContainernach Wert mockSearch zuweisen und Scherz des rufenden mock, die specs weisen darauf hin, dass: Before instantiating a module, all of the modules it requested must be available.

Daher wird zum Zeitpunkt SearchContainer importiert, und wiederum importiert search, Ihre mockSearch Variable ist immer noch undefined.

Man könnte dies seltsam finden, da es auch zu implizieren scheint search.js ist noch nicht verspottet, und so würde Spott überhaupt nicht funktionieren. Glücklicherweise stellt (babel-) scherz sicher, Aufrufe an mock und ähnliche Funktionen sogar höhere als die Importe zu hissen, so dass das Spotten funktioniert.

Nichtsdestotrotz wird die Zuweisung mockSearch, auf die sich die Mock-Funktion bezieht, nicht mit dem mock-Aufruf ausgelöst.So wird die Reihenfolge der relevanten Vorgänge werden so etwas wie:

  1. ein Mock-Fabrik Set für ./search.js
  2. Import aller Abhängigkeiten, die die Pseudo-Fabrik für eine Funktion aufrufen, wird die Komponente
  3. Vergeben Sie einen Wert zu geben, zu mockSearch

Wenn Schritt 2 der Fall ist, wird die Funktion zum search Komponente übergeben undefiniert, und die Zuordnung in Schritt 3 ist zu spät, das zu ändern.

Lösung

Wenn Sie die Mock-Funktion als Teil des mock Anrufs erstellen (so dass es zu gehisst werden), wird es einen gültigen Wert hat, wenn es durch das Komponentenmodul importiert wird, als früh Beispiel zeigt.

Wie Sie bereits darauf hingewiesen haben, beginnt das Problem, wenn Sie die verspottete Funktion in Ihren Tests verfügbar machen möchten. Es gibt eine naheliegende Lösung: Importieren Sie das Modul, das Sie bereits verspottet haben, separat.

Da Sie jetzt Scherz spöttisch wissen passieren eigentlich vor Importen, ein trivialer Ansatz wäre:

import { search } from './search.js'; // This will actually be the mock 

jest.mock('./search.js',() => { 
    return { search: jest.fn(() => mockPromise) }; 
}); 

[...] 

beforeEach(() => { 
    search.mockClear(); 
}); 

it('should call the search module',() => { 
    [...] 

    expect(search.mock.calls.length).toBe(1); 
    expect(search.mock.calls[0]).toEqual(expectedArgs); 
}); 

In der Tat könnten Sie ersetzen mögen:

import { search } from './search.js'; 

mit:

const { search } = require.requireMock('./search.js'); 

Dies sollte keinen funktionalen Unterschied machen, könnte aber das, was Sie tun, ein wenig expliziter machen es (und sollte jedem helfen, ein Typ-Prüfsystem wie Flow zu verwenden, so dass es nicht denkt, dass Sie versuchen, Scheinfunktionen auf dem ursprünglichen search aufzurufen).

Zusätzlicher Hinweis

All dies ist nur unbedingt notwendig, wenn, was Sie verspotten müssen sich die Standard-Export eines Moduls ist. Andernfalls (wie @publicJorn hervorhebt), können Sie einfach das spezifische relevante Mitglied in den Tests neu zuweisen:

import * as search from './search.js'; 

beforeEach(() => { 
    search.search = jest.fn(() => mockPromise); 
}); 
+0

Vielen Dank für diese sehr detaillierte Antwort. Ich werde definitiv deine Lösung versuchen. – ghusse

+0

Während die vorherige Lösung technisch funktionieren sollte (und ich schließlich sogar herausgefunden habe, warum die 'var' notwendig war), habe ich jetzt die Antwort bearbeitet, um eine viel weniger hässliche zu verwenden, IMO. – Tomty

+0

Danke für Ihre Hilfe. Mit der zweiten Lösung bekomme ich einen Fehler 'kann die Suche nach Eigenschaften der Suche nicht festlegen '. Die erste Lösung funktioniert: Ich muss die Lib importieren und den Jest ein für allemal aufrufen (nicht im beforeEach-Trigger). – ghusse

Verwandte Themen