que modulos from es6 convertir javascript unit-testing mocha ecmascript-6

javascript - modulos - ¿Cómo burlarse de las importaciones de un módulo ES6?



modulos es6 (5)

@carpeliam es correcto, pero tenga en cuenta que si desea espiar una función en un módulo y usar otra función en ese módulo que llama a esa función, debe llamar a esa función como parte del espacio de nombres de exportaciones; de lo contrario, el espía no se usará.

Ejemplo equivocado:

// mymodule.js export function myfunc2() {return 2;} export function myfunc1() {return myfunc2();} // tests.js import * as mymodule describe(''tests'', () => { beforeEach(() => { spyOn(mymodule, ''myfunc2'').and.returnValue = 3; }); it(''calls myfunc2'', () => { let out = mymodule.myfunc1(); // out will still be 2 }); });

Ejemplo correcto:

export function myfunc2() {return 2;} export function myfunc1() {return exports.myfunc2();} // tests.js import * as mymodule describe(''tests'', () => { beforeEach(() => { spyOn(mymodule, ''myfunc2'').and.returnValue = 3; }); it(''calls myfunc2'', () => { let out = mymodule.myfunc1(); // out will be 3 which is what you expect }); });

Tengo los siguientes módulos ES6:

network.js

export function getDataFromServer() { return ... }

widget.js

import { getDataFromServer } from ''network.js''; export class Widget() { constructor() { getDataFromServer("dataForWidget") .then(data => this.render(data)); } render() { ... } }

Estoy buscando una forma de probar Widget con una instancia simulada de getDataFromServer . Si usara <script> s separados en lugar de módulos ES6, como en Karma, podría escribir mi prueba como:

describe("widget", function() { it("should do stuff", function() { let getDataFromServer = spyOn(window, "getDataFromServer").andReturn("mockData") let widget = new Widget(); expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget"); expect(otherStuff).toHaveHappened(); }); });

Sin embargo, si estoy probando módulos ES6 individualmente fuera de un navegador (como con Mocha + babel), escribiría algo como:

import { Widget } from ''widget.js''; describe("widget", function() { it("should do stuff", function() { let getDataFromServer = spyOn(?????) // How to mock? .andReturn("mockData") let widget = new Widget(); expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget"); expect(otherStuff).toHaveHappened(); }); });

De acuerdo, pero ahora getDataFromServer no está disponible en la window (bueno, no hay ninguna window ), y no conozco una forma de inyectar cosas directamente en el propio alcance de widget.js .

Entonces, ¿a dónde voy desde aquí?

  1. ¿Hay alguna manera de acceder al alcance de widget.js , o al menos reemplazar sus importaciones con mi propio código?
  2. Si no, ¿cómo puedo hacer que Widget comprobable?

Cosas que consideré:

a. Inyección manual de dependencias.

Elimine todas las importaciones de widget.js y espere que la persona que llama proporcione los departamentos.

export class Widget() { constructor(deps) { deps.getDataFromServer("dataForWidget") .then(data => this.render(data)); } }

Me incomoda mucho estropear la interfaz pública de Widget como esta y exponer los detalles de implementación. No vayas.

si. Exponer las importaciones para permitir burlarse de ellos.

Algo como:

import { getDataFromServer } from ''network.js''; export let deps = { getDataFromServer }; export class Widget() { constructor() { deps.getDataFromServer("dataForWidget") .then(data => this.render(data)); } }

entonces:

import { Widget, deps } from ''widget.js''; describe("widget", function() { it("should do stuff", function() { let getDataFromServer = spyOn(deps.getDataFromServer) // ! .andReturn("mockData"); let widget = new Widget(); expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget"); expect(otherStuff).toHaveHappened(); }); });

Esto es menos invasivo pero requiere que escriba una gran cantidad de repeticiones para cada módulo, y todavía existe el riesgo de que use getDataFromServer lugar de deps.getDataFromServer todo el tiempo. Estoy inquieto al respecto, pero esa es mi mejor idea hasta ahora.


Comencé a emplear la import * as obj estilo import * as obj dentro de mis pruebas, que importa todas las exportaciones de un módulo como propiedades de un objeto que luego se puede burlar. Creo que esto es mucho más limpio que usar algo como recablear o proxyquire o cualquier técnica similar. Lo he hecho con mayor frecuencia cuando necesito burlarme de las acciones de Redux, por ejemplo. Esto es lo que podría usar para su ejemplo anterior:

import * as network from ''network.js''; describe("widget", function() { it("should do stuff", function() { let getDataFromServer = spyOn(network, "getDataFromServer").andReturn("mockData") let widget = new Widget(); expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget"); expect(otherStuff).toHaveHappened(); }); });

Si su función es una exportación por defecto, entonces import * as network from ''./network'' produciría {default: getDataFromServer} y puede burlarse de network.default.


He encontrado que esta sintaxis funciona:

Mi modulo:

// mymod.js import shortid from ''shortid''; const myfunc = () => shortid(); export default myfunc;

Código de prueba de mi módulo:

// mymod.test.js import myfunc from ''./mymod''; import shortid from ''shortid''; jest.mock(''shortid''); describe(''mocks shortid'', () => { it(''works'', () => { shortid.mockImplementation(() => 1); expect(myfunc()).toEqual(1); }); });

Ver el doc .


Implementé una biblioteca que intenta resolver el problema de la burla en tiempo de ejecución de las importaciones de la clase Typecript sin necesidad de que la clase original conozca ninguna inyección explícita de dependencia.

La biblioteca utiliza la import * as sintaxis y luego reemplaza el objeto exportado original con una clase stub. Conserva la seguridad de tipo, por lo que sus pruebas se interrumpirán en el momento de la compilación si se actualizó el nombre de un método sin actualizar la prueba correspondiente.

Esta biblioteca se puede encontrar aquí: ts-mock-imports .


La respuesta de @ vdloo me llevó en la dirección correcta, pero el uso de las palabras clave "exportar" del módulo ES6 "exportar" en el mismo archivo no funcionó para mí (webpack v2 o posterior se queja). En cambio, estoy usando una exportación predeterminada (variable con nombre) que envuelve todas las exportaciones individuales de módulos con nombre y luego importo la exportación predeterminada en mi archivo de pruebas. Estoy usando la siguiente configuración de exportación con mocha / sinon y el stubbing funciona bien sin necesidad de volver a cablear, etc.

// MyModule.js let MyModule; export function myfunc2() { return 2; } export function myfunc1() { return MyModule.myfunc2(); } export default MyModule = { myfunc1, myfunc2 } // tests.js import MyModule from ''./MyModule'' describe(''MyModule'', () => { const sandbox = sinon.sandbox.create(); beforeEach(() => { sandbox.stub(MyModule, ''myfunc2'').returns(4); }); afterEach(() => { sandbox.restore(); }); it(''myfunc1 is a proxy for myfunc2'', () => { expect(MyModule.myfunc1()).to.eql(4); }); });