una termine que llamar función funcion evento esperar desde como boton archivo javascript unit-testing reactjs mocking jestjs

javascript - termine - No se puede burlar de un módulo con bromas y hacer llamadas a funciones de prueba



llamar funcion javascript html sin evento (1)

Creo un proyecto usando create-app-component , que configura una nueva aplicación con scripts de compilación (babel, webpack, broma).

Escribí un componente React que estoy tratando de probar. El componente requiere otro archivo javascript, lo que expone una función.

Mi archivo search.js

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

Mi componente de reacción:

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

En mis pruebas unitarias, quiero verificar que se haya llamado a la search del método con los argumentos correctos.

Mis pruebas se ven así:

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

Ya tuve un momento difícil para descubrir cómo hacer que jest.mock funcione, porque requiere que las variables sean prefijadas por mock .

Pero si quiero probar argumentos para la search métodos, necesito hacer que la función de burla esté disponible en mis pruebas.

Si transformo la parte burlona, ​​para usar una variable:

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

Me sale este error:

TypeError: (0, _search.search) no es una función

Cualquier cosa que intento para tener acceso al jest.fn y probar los argumentos, no puedo hacer que funcione.

¿Qué estoy haciendo mal?


El problema

La razón por la que está obteniendo ese error tiene que ver con la forma en que se elevan varias operaciones.

Aunque en su código original solo importa SearchContainer después de asignar un valor a mockSearch y llamar a mock de broma, las especificaciones señalan que: Before instantiating a module, all of the modules it requested must be available .

Por lo tanto, en el momento en que se importa SearchContainer y, a su vez, importa la search , su variable de mockSearch search aún no está definida.

Uno podría encontrar esto extraño, ya que también parece implicar que search.js todavía no se burla, y que burlarse no funcionaría en absoluto. Afortunadamente, (babel-) broma se asegura de izar las llamadas a mock y funciones similares incluso mayores que las importaciones, para que la burla funcione.

Sin embargo, la asignación de mockSearch , a la que se hace referencia mediante la función del simulacro, no se izará con la llamada mock . Entonces, el orden de las operaciones relevantes será algo así como:

  1. Establecer una fábrica simulada para ./search.js
  2. Importe todas las dependencias, que llamarán a la fábrica de simulacros para una función para dar el componente
  3. Asignar un valor a mockSearch

Cuando se produce el paso 2, la función de search que se pasa al componente no estará definida, y la asignación en el paso 3 es demasiado tarde para cambiar eso.

Solución

Si crea la función de simulacro como parte de la mock llamada (de manera que también se izará), tendrá un valor válido cuando sea importada por el módulo de componente, como muestra su ejemplo anterior.

Como señaló, el problema comienza cuando desea que la función de burla esté disponible en sus pruebas. Hay una solución obvia para esto: importar por separado el módulo que ya se ha burlado.

Como ahora sabes que la burla en realidad sucede antes de las importaciones, un enfoque trivial sería:

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

De hecho, es posible que desee reemplazar:

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

Con:

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

Esto no debería hacer ninguna diferencia funcional, pero podría hacer que lo que estás haciendo sea un poco más explícito (y debería ayudar a cualquiera que use un sistema de verificación de tipo como Flow, por lo que no cree que estés tratando de llamar funciones simuladas en la search original).

Nota adicional

Todo esto es estrictamente necesario si lo que necesita para simular es la exportación predeterminada de un módulo. De lo contrario (como señala @publicJorn), simplemente puede reasignar el miembro relevante específico en las pruebas, de esta forma:

import * as search from ''./search.js''; beforeEach(() => { search.search = jest.fn(() => mockPromise); });