Búsqueda de sitios web

javascript - ¿Cómo puedo simular una importación de módulo ES6 usando Jest?


Quiero probar que uno de mis módulos ES6 llame a otro módulo ES6 de una manera particular. Con Jasmine esto es muy fácil --

El código de la aplicación:

// myModule.js
import dependency from './dependency';

export default (x) => {
  dependency.doSomething(x * 2);
}

Y el código de prueba:

//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    spyOn(dependency, 'doSomething');

    myModule(2);

    expect(dependency.doSomething).toHaveBeenCalledWith(4);
  });
});

¿Cuál es el equivalente con Jest? Siento que esto es algo tan simple de querer hacer, pero me he estado arrancando el cabello tratando de entenderlo.

Lo más cerca que he llegado es reemplazando los import por require, y moviéndolos dentro de las pruebas/funciones. Ninguna de las cuales es algo que quiera hacer.

// myModule.js
export default (x) => {
  const dependency = require('./dependency'); // Yuck
  dependency.doSomething(x * 2);
}

//myModule-test.js
describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    jest.mock('../dependency');

    myModule(2);

    const dependency = require('../dependency'); // Also yuck
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

Para obtener puntos extra, me encantaría hacer que todo funcione cuando la función dentro de dependency.js sea una exportación por defecto. Sin embargo, sé que espiar exportaciones por defecto no funciona en Jasmine (o al menos nunca pude lograr que funcionara), así que no tengo esperanzas de que sea posible en Jest tampoco.

Respuesta #1

Editar: Han pasado varios años y esta ya no es realmente la forma correcta de hacer esto (y probablemente nunca lo fue, mi error).

Mutar un módulo importado es desagradable y puede llevar a efectos secundarios como pruebas que pasan o fallan dependiendo del orden de ejecución.

Dejo esta respuesta en su forma original por motivos históricos, pero realmente deberías usar jest.spyOn o jest.mock. Consulta la documentación de jest o las otras respuestas en esta página para más detalles.

La respuesta original es la siguiente:


He podido resolver esto utilizando un truco que implica import *. ¡Incluso funciona tanto para exportaciones nombradas como por defecto!

Para una exportación nombrada:

// dependency.js
export const doSomething = (y) => console.log(y)
// myModule.js
import { doSomething } from './dependency';

export default (x) => {
  doSomething(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.doSomething = jest.fn(); // Mutate the named export

    myModule(2);

    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

O para una exportación por defecto:

// dependency.js
export default (y) => console.log(y)
// myModule.js
import dependency from './dependency'; // Note lack of curlies

export default (x) => {
  dependency(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.default = jest.fn(); // Mutate the default export

    myModule(2);

    expect(dependency.default).toBeCalledWith(4); // Assert against the default
  });
});

Respuesta #2

Tienes que simular el módulo y configurar el espía tú mismo:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}))

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

Respuesta #3

Avanzando rápidamente hasta 2020, encontré esta publicación de blog como la solución: Jest mock default and named export

Usando solo la sintaxis de módulos ES6:

// esModule.js
export default 'defaultExport';
export const namedExport = () => {};

// esModule.test.js
jest.mock('./esModule', () => ({
  __esModule: true, // this property makes it work
  default: 'mockedDefaultExport',
  namedExport: jest.fn(),
}));

import defaultExport, { namedExport } from './esModule';
defaultExport; // 'mockedDefaultExport'
namedExport; // mock function

También hay una cosa que necesitas saber (lo cual me tomó un tiempo entender) y es que no puedes llamar a jest.mock() dentro del test; debes llamarlo en el nivel superior del módulo. Sin embargo, puedes llamar a mockImplementation() dentro de tests individuales si quieres configurar diferentes mocks para distintos tests.

Respuesta #4

Para simular la exportación por defecto de un módulo de dependencia ES6 usando Jest:

import myModule from '../myModule';
import dependency from '../dependency';

jest.mock('../dependency');

// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);

describe('myModule', () => {
  it('calls the dependency once with double the input', () => {
    myModule(2);

    expect(dependency).toHaveBeenCalledTimes(1);
    expect(dependency).toHaveBeenCalledWith(4);
  });
});

Las otras opciones no funcionaron en mi caso.

Respuesta #5

Agregando más a la respuesta de Andreas. Tuve el mismo problema con el código ES6, pero no quería mutar las importaciones. Eso parecía poco elegante. Así que hice esto:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
  });
});

Y agregué el archivo dependency.js en la carpeta "__ mocks __" paralela al archivo dependency.js. Esto funcionó para mí. Además, esto me dio la opción de devolver datos adecuados desde la implementación simulada. Asegúrate de dar la ruta correcta al módulo que deseas simular.

Respuesta #6

Ninguna de las respuestas aquí parecía funcionar para mí (la función original siempre se importaba en lugar del mock), y parece que el soporte de ESM en Jest todavía está en progreso.

Después de descubrir este comentario, me enteré de que jest.mock() en realidad no funciona con importaciones regulares, porque las importaciones siempre se ejecutan antes del mock (esto ahora también está documentado oficialmente). Debido a esto, estoy importando mis dependencias usando await import(). Esto incluso funciona con un await de nivel superior, así que solo tengo que adaptar mis importaciones:

import { describe, expect, it, jest } from '@jest/globals';

jest.unstable_mockModule('../dependency', () => ({
  doSomething: jest.fn()
}));

const myModule = await import('../myModule');
const dependency = await import('../dependency');

describe('myModule', async () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

Respuesta #7

La pregunta ya está respondida, pero puedes resolverla así:

Archivo dependency.js

const doSomething = (x) => x
export default doSomething;

Archivo myModule.js

import doSomething from "./dependency";

export default (x) => doSomething(x * 2);

Archivo myModule.spec.js

jest.mock('../dependency');
import doSomething from "../dependency";
import myModule from "../myModule";

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    doSomething.mockImplementation((x) => x * 10)

    myModule(2);

    expect(doSomething).toHaveBeenCalledWith(4);
    console.log(myModule(2)) // 40
  });
});

Artículos relacionados