una - tablas en javascript ejemplos
Cómo simular funciones en el mismo módulo usando jest. (5)
El problema parece estar relacionado con cómo espera que se resuelva el alcance de la barra.
Por un lado, en module.js
usted exporta dos funciones (en lugar de un objeto que contiene estas dos funciones). Debido a la forma en que se exportan los módulos, la referencia al contenedor de las cosas exportadas es la exports
como usted lo mencionó.
Por otro lado, maneja su exportación (que tiene un module
alias) como un objeto que tiene estas funciones y trata de reemplazar una de sus funciones (la barra de funciones).
Si observa detenidamente su implementación foo, en realidad está manteniendo una referencia fija a la función de barra.
Cuando cree que reemplazó la función de barra con una nueva, simplemente reemplazó la copia de referencia en el alcance de su módulo.test.js
Para hacer que foo use otra versión de barra, tienes dos posibilidades:
En module.js exportar una clase o una instancia, manteniendo ambos métodos foo y bar:
Module.js:
export class MyModule { function bar () { return ''bar''; } function foo () { return `I am foo. bar is ${this.bar()}`; } }
Tenga en cuenta el uso de esta palabra clave en el método foo.
Module.test.js:
import { MyModule } from ''../src/module'' describe(''MyModule'', () => { //System under test : const sut:MyModule = new MyModule(); let barSpy; beforeEach(() => { barSpy = jest.spyOn( sut, ''bar'' ).mockImplementation(jest.fn()); }); afterEach(() => { barSpy.mockRestore(); }); it(''foo'', () => { sut.bar.mockReturnValue(''fake bar''); expect(sut.foo()).toEqual(''I am foo. bar is fake bar''); }); });
Como ha dicho, vuelva a escribir la referencia global en el contenedor de
exports
globales. Esta no es una forma recomendada, ya que posiblemente introducirás comportamientos extraños en otras pruebas si no restableces correctamente las exportaciones a su estado inicial.
¿Cuál es la mejor manera de burlar correctamente el siguiente ejemplo?
El problema es que después del tiempo de importación, foo
conserva la referencia a la bar
original no bloqueada.
módulo.js:
export function bar () {
return ''bar'';
}
export function foo () {
return `I am foo. bar is ${bar()}`;
}
module.test.js:
import * as module from ''../src/module'';
describe(''module'', () => {
let barSpy;
beforeEach(() => {
barSpy = jest.spyOn(
module,
''bar''
).mockImplementation(jest.fn());
});
afterEach(() => {
barSpy.mockRestore();
});
it(''foo'', () => {
console.log(jest.isMockFunction(module.bar)); // outputs true
module.bar.mockReturnValue(''fake bar'');
console.log(module.bar()); // outputs ''fake bar'';
expect(module.foo()).toEqual(''I am foo. bar is fake bar'');
/**
* does not work! we get the following:
*
* Expected value to equal:
* "I am foo. bar is fake bar"
* Received:
* "I am foo. bar is bar"
*/
});
});
¡Gracias!
EDITAR: podría cambiar:
export function foo () {
return `I am foo. bar is ${bar()}`;
}
a
export function foo () {
return `I am foo. bar is ${exports.bar()}`;
}
pero esto es p. feo en mi opinión para hacer en todas partes: /
Fwiw, la solución que decidí fue usar la inyección de dependencia , estableciendo un argumento predeterminado.
Así que cambiaría
export function bar () {
return ''bar'';
}
export function foo () {
return `I am foo. bar is ${bar()}`;
}
a
export function bar () {
return ''bar'';
}
export function foo (_bar = bar) {
return `I am foo. bar is ${_bar()}`;
}
Esto no es un cambio importante en la API de mi componente, y puedo anular fácilmente la barra en mi prueba haciendo lo siguiente
import { foo, bar } from ''../src/module'';
describe(''module'', () => {
it(''foo'', () => {
const dummyBar = jest.fn().mockReturnValue(''fake bar'');
expect(foo(dummyBar)).toEqual(''I am foo. bar is fake bar'');
});
});
Esto tiene la ventaja de llevar a un código de prueba ligeramente más agradable también :)
Si define sus exportaciones, puede hacer referencia a sus funciones como parte del objeto de exportaciones. Luego puedes sobrescribir las funciones en tus simulacros individualmente. Esto se debe a que la importación funciona como referencia, no como copia.
módulo.js:
exports.bar () => {
return ''bar'';
}
exports.foo () => {
return `I am foo. bar is ${exports.bar()}`;
}
module.test.js:
describe(''MyModule'', () => {
it(''foo'', () => {
let module = require(''./module'')
module.bar = jest.fn(()=>{return ''fake bar''})
expect(module.foo()).toEqual(''I am foo. bar is fake bar'');
});
})
Tuve este mismo problema y, debido a los estándares de alineación del proyecto, la definición de una clase o la reescritura de referencias en las exports
no eran opciones aptas para revisión de código, incluso si las definiciones de alineación no las impedían. Lo que encontré como una opción viable es usar el babel-rewire-plugin que es mucho más limpio, al menos en apariencia. Aunque encontré esto usado en otro proyecto al que tuve acceso, noté que ya estaba en una respuesta en una pregunta similar que he vinculado here . Este es un fragmento ajustado a esta pregunta (y sin usar espías) provisto de la respuesta vinculada como referencia (también agregué puntos y coma además de eliminar espías porque no soy un pagano):
import __RewireAPI__, * as module from ''../module'';
describe(''foo'', () => {
it(''calls bar'', () => {
const barMock = jest.fn();
__RewireAPI__.__Rewire__(''bar'', barMock);
module.foo();
expect(bar).toHaveBeenCalledTimes(1);
});
});
here
Una solución alternativa puede ser importar el módulo a su propio archivo de código y usar la instancia importada de todas las entidades exportadas. Me gusta esto:
import * as thisModule from ''./module'';
export function bar () {
return ''bar'';
}
export function foo () {
return `I am foo. bar is ${thisModule.bar()}`;
}
Ahora la bar
burla es realmente fácil, porque foo
también está utilizando la instancia exportada de la bar
:
import * as module from ''../src/module'';
describe(''module'', () => {
it(''foo'', () => {
spyOn(module, ''bar'').and.returnValue(''fake bar'');
expect(module.foo()).toEqual(''I am foo. bar is fake bar'');
});
});
Importar el módulo en su propio código parece extraño, pero debido al soporte de ES6 para las importaciones cíclicas, funciona realmente sin problemas.