¿Cuál es la mejor manera de simular correctamente el siguiente ejemplo?
El problema es que después del tiempo de importación, foo mantiene la referencia a la bar original sin burlar.
module.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" */ }); });Podría cambiar:
export function foo () { return `I am foo. bar is ${bar()}`; }para:
export function foo () { return `I am foo. bar is ${exports.bar()}`; }pero esto es bastante feo en mi opinión para hacer en todas partes.
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 burlarse de la bar es realmente fácil, porque foo también usa 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 importaciones cíclicas, funciona muy bien.
El problema parece estar relacionado con cómo espera que se resuelva el alcance de la barra.
Por un lado, en module.js exportas 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 se exports como lo mencionó.
Por otro lado, usted maneja su exportación (que le dio un alias al module ) como un objeto que contiene estas funciones y trata de reemplazar una de sus funciones (la barra de funciones).
Si observa detenidamente la implementación de foo, en realidad tiene 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 module.test.js
Para hacer que foo use otra versión de bar, tiene dos posibilidades:
En module.js, exporte una clase o una instancia, manteniendo tanto el método foo como el de bar:
Módulo.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.
Módulo.prueba.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 dijiste, vuelve a escribir la referencia global en el contenedor de exports globales. Esta no es una forma recomendada de hacerlo, ya que posiblemente introducirá comportamientos extraños en otras pruebas si no restablece correctamente las exportaciones a su estado inicial.
Fwiw, la solución que decidí fue usar la inyección de dependencia , estableciendo un argumento predeterminado.
Entonces cambiaría
export function bar () { return 'bar'; } export function foo () { return `I am foo. bar is ${bar()}`; }para
export function bar () { return 'bar'; } export function foo (_bar = bar) { return `I am foo. bar is ${_bar()}`; }Este 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 conducir a un código de prueba un poco más agradable también :)