Jest: ¿Cómo simular una función de módulo específico?

Creado en 25 abr. 2016  ·  116Comentarios  ·  Fuente: facebook/jest

Estoy luchando con algo que creo que debería ser fácil y obvio, pero por alguna razón no puedo resolverlo.

Tengo un modulo. Exporta múltiples funciones. Aquí está myModule.js :

export function foo() {...}
export function bar() {...}
export function baz() {...}

Desmock el módulo para probarlo.

jest.unmock('./myModule.js');

Sin embargo, necesito simular foo , porque hace llamadas ajax a mi backend. Quiero que todas las funciones de este archivo permanezcan sin burlarse, espero que foo , que quiero que se burlen. Y las funciones bar y baz hacen llamadas internamente a foo , por lo que cuando mi prueba llame a bar() , el bar no manipulado llamará al burlado foo .

Aparece en la documentación de broma que las llamadas a unmock y mock operan en todo el módulo. ¿Cómo puedo burlarme de una función específica? Es ridículo dividir arbitrariamente mi código en módulos separados para que puedan probarse correctamente.

Comentario más útil

Tu puedes hacer:

jest.unmock('./myModule.js');

const myModule = require('myModule');
myModule.foo = jest.fn();

Ver http://facebook.github.io/jest/docs/api.html#mock -functions

Todos 116 comentarios

Tras un análisis más profundo, parece que jest-mock genera un AST para todo el módulo, luego usa ese AST para crear un módulo simulado que se ajusta a las exportaciones originales: https://github.com/facebook/jest/tree/master/packages / broma-simulacro

Otros marcos de prueba, como el simulacro de Python (https://docs.python.org/3/library/unittest.mock-examples.html), le permiten simular funciones específicas. Este es un concepto de prueba fundamental.

Recomiendo encarecidamente la posibilidad de simular una parte de un módulo. Creo que jest-mock debería cambiarse para ignorar condicionalmente las exportaciones de mocking y hacer referencia a la implementación original.

Tu puedes hacer:

jest.unmock('./myModule.js');

const myModule = require('myModule');
myModule.foo = jest.fn();

Ver http://facebook.github.io/jest/docs/api.html#mock -functions

Creo que tienes un malentendido fundamental de cómo funciona require . Cuando llamas a require() , no obtienes una instancia del módulo. Obtienes un objeto con referencias a las funciones del módulo. Si sobrescribe un valor en el módulo requerido, su propia referencia se sobrescribe, _pero la implementación mantiene las referencias originales_.

En su ejemplo, si llama a myModule.foo() , sí, llamará a la versión simulada. Pero si llama a myModule.bar() , que internamente llama a foo() , el foo que hace referencia _no es su versión sobrescrita_. Si no me cree, puede probarlo.

Por lo tanto, el ejemplo que describiste es inadecuado para el problema que tengo. ¿Sabes algo que yo no?

@cpojer

Creo que entiendo esto bastante bien. Sin embargo, la forma en que babel compila los módulos no hace que esto sea más fácil de entender y entiendo su confusión. No sé exactamente cómo se comportaría esto en un entorno ES2015 real con módulos, principalmente porque ese entorno no existe en este momento (excepto quizás las últimas versiones de Chrome Canary, que aún no he probado). Para explicar lo que sucede, tenemos que mirar la salida compilada de su código babel. Se verá algo como esto:

var foo = function foo() {};
var bar = function bar() { foo(); };

exports.foo = foo;
exports.bar = bar;

En este caso, de hecho es correcto que no puede burlarse de foo y me disculpo por no leer correctamente su problema inicial, sin embargo, no hizo ninguna suposición sobre cómo se llamó foo , así que asumí que era exports.foo() . Apoyar lo anterior burlándose de una función después de requerir un módulo es imposible en JavaScript; no hay (casi) forma de recuperar el enlace al que se refiere foo y modificarlo.

Sin embargo, si cambia su código a esto:

var foo = function foo() {};
var bar = function bar() { exports.foo(); };

exports.foo = foo;
exports.bar = bar;

y luego en tu archivo de prueba haces:

var module = require('../module');
module.foo = jest.fn();
module.bar();

funcionará como se esperaba. Esto es lo que hacemos en Facebook donde no usamos ES2015.

Si bien los módulos ES2015 pueden tener enlaces inmutables para lo que exportan, el código compilado subyacente con el que compila babel en este momento no impone ninguna de esas restricciones. Actualmente, no veo forma de admitir exactamente lo que está pidiendo en un entorno de módulo ES2015 estricto con módulos compatibles de forma nativa. La forma en que funciona jest-mock es que ejecuta el código del módulo de forma aislada y luego recupera los metadatos de un módulo y crea funciones simuladas. Nuevamente, en este caso no habrá forma de modificar el enlace local de foo . Si tiene ideas sobre cómo implementar esto de manera efectiva, contribuya aquí o con una solicitud de extracción. Me gustaría recordarle que tenemos un código de conducta para este proyecto que puede leer aquí: https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct

La solución correcta en su ejemplo no es burlarse de foo, sino burlarse de la API de nivel superior a la que llama foo (como XMLHttpRequest o la abstracción que usa en su lugar).

@cpojer Gracias por su explicación detallada. Lamento si te ofendí con mi lenguaje, soy muy eficiente con mi redacción de ingeniería y quiero transmitir mi punto lo antes posible. Para poner las cosas en perspectiva, pasé 5 horas tratando de entender este problema y escribí 2 comentarios detallados, luego lo cerró con un breve mensaje que perdió completamente el sentido de mis dos declaraciones. Es por eso que mi siguiente mensaje decía que tuviste un "malentendido fundamental", porque 1) no entendiste el punto que estaba diciendo, o 2) no entendiste require() , que afortunadamente era la opción 1.

Consideraré una posible solución a mi problema, para solucionarlo por ahora me burlé de una API de nivel inferior, pero definitivamente debería haber una manera de burlarse de la función directamente, ya que sería bastante útil.

Estoy de acuerdo en que sería útil poder hacer esto, pero no hay una buena manera en JS de hacerlo sin un análisis estático (probablemente lento) por adelantado :(

@cpojer : No estoy seguro de si saltar aquí 5 meses después es el camino a seguir, pero no pude encontrar ninguna otra conversación sobre esto.

Partiendo de su sugerencia anterior, hice esto para simular una función de otra en el mismo módulo:

jest.unmock('./someModule.js');
import someModule from './someModule.js';

it('function1 calls function 2', () => {
    someModule.function2 = jest.fn();

    someModule.function1(...);

    expect(someModule.function2).toHaveBeenCalledWith(...);
});

Esto funciona para una prueba, pero no he encontrado una manera de lograr esto de una manera que esté aislada solo del bloque it(...); . Como se escribió anteriormente, afecta a todas las pruebas, lo que dificulta probar el function2 real en otra prueba. ¿Algun consejo?

Puede llamar a .mockClear en la función en beforeEach o llamar a jest.clearAllMocks() si está usando Jest 16.

¡Hola @cpojer! Estoy usando Jest 16. Ni jest.clearAllMocks() ni someModule.function2.mockClear() funcionan para mí. Solo funcionan cuando el simulacro es un módulo completo, no una función de un módulo importado. En mi proyecto, la función permanece simulada en pruebas posteriores. Si esto no se espera, veré si puedo replicar en un pequeño proyecto de muestra y crear un nuevo problema. ¿Buena idea?

@cpojer -

La solución correcta en su ejemplo no es burlarse de foo, sino burlarse de la API de nivel superior a la que llama foo (como XMLHttpRequest o la abstracción que usa en su lugar).

Soy nuevo en Jest y estoy luchando con un problema similar. Estoy usando axios , que debajo del capó usa XMLHttpRequest , y no quiero burlarme de axios , sino burlarme del XMLHttpRequest real. Parece que tendría que implementar sus métodos por mí mismo, algo como esto . ¿Es este el enfoque correcto?

¡Gracias!

sí, ¡algo como esto debería llevarlo por el camino correcto! :) Use jest.fn como una API más agradable, aunque: D

@cpojer con respecto a su comentario aquí: https://github.com/facebook/jest/issues/936#issuecomment -214939935

¿Cómo harías eso con ES2015?

// myModyle.js
export foo = (string) => "foo-" + string
export bar = (string2) => foo(string2)

// myModule.test.js
var module = require('./myModule');

// how do I mock foo here so this test passes?
expect(bar("hello")).toEqual("test-hello")

Para cualquiera que se encuentre con esto en busca de una solución, lo siguiente parece funcionar para mí al exportar muchas const / functions en un archivo y al importarlas en un archivo que estoy probando

`` javascript function mockFunctions() { const original = require.requireActual('../myModule'); return { ...original, //Pass down all the exported objects test: jest.fn(() => {console.log('I didnt call the original')}), someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}), } jest.mock('../myModule', () => mockFunctions()); const storage = require.requireMock('../myModule');

@ainesophaur , no estoy seguro de qué estoy haciendo mal aquí. Pero parece que no funciona
Actualmente estoy en broma 18.1 (y create-react-app 0.9.4)

...<codes from comment above>..

// Let's say the original myModule has a function doSmth() that calls test()
storage.doSmth();
expect(storage.test).toHaveBeenCalled();

La prueba fallará con:

expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called.

@huyph tendrías que burlarte de tu método doSmth y tu método de prueba para que Jest pruebe si fue llamado. Si puede proporcionar el fragmento de su código de burla, puedo comprobar lo que está mal

@ainesophaur ... uhm. Pensé que sus códigos anteriores son para burlarse del método test() . esta parte: test: jest.fn(() => {console.log('I didnt call the original')}),

@ainesophaur También probé tu código. Pero no funcionó para mí. Nunca ejecuta la función de simulación. Entonces, la expectativa nunca se cumple.

Creo que esto es inherente a la forma en que requieren trabajos como se indicó anteriormente ... Desearía que hubiera una solución para esto.

@cpojer ¿Hay algo nuevo con respecto a los módulos de burla parcial?

@rantonmattei & @huyph Tendría que ver un fragmento de tus definiciones simuladas y la prueba que estás ejecutando. Debe definir su simulacro antes de que se requiera / importe el archivo de implementación real. Ha pasado un tiempo desde que trabajé con JEST, pero recuerdo que finalmente logré que se burlara de todo lo que necesitaba, ya sea una biblioteca node_modules o un archivo en mi aplicación. Tengo un poco de tiempo en el cajero automático, pero aquí están algunas de las pruebas de un proyecto en el que trabajé usando Jest.

Burlarse de un archivo de una dependencia

La definición de la función real en este ejemplo se realiza mediante react-native. Me estoy burlando del archivo "react-native / Libraries / Utilities / dispatsKeyboard.js"

Este es un archivo simulado bajo __mocks__/react-native/Libraries/Utilities/dismissKeyboard.js

function storeMockFunctions() {
  return jest.fn().mockImplementation(() => true);
}
jest.mock('react-native/Libraries/Utilities/dismissKeyboard', () => storeMockFunctions(), { virtual: true });
const dismissKeyboard = require('react-native/Libraries/Utilities/dismissKeyboard');
exports = module.exports = storeMockFunctions;

No puedo encontrar el archivo de prueba que usé para lo anterior, pero era algo así como requerir el módulo, jest lo encontraría en __mocks__ y luego podría hacer algo como

expect(dismissKeyboard.mock.calls).toHaveLength(1);

Burlarse de un archivo que controlas
Definición de función real

export const setMerchantStores = (stores) => storage.set('stores', stores);

Archivo de prueba con simulacro

const { storeListEpic, offerListEpic } = require('../merchant');

function storeMockFunctions() {
  const original = require.requireActual('../../common/Storage');
  return {
    ...original,
    setMerchantStores: jest.fn((...args) => original.setMerchantStores(...args)),
    setMerchantOffers: jest.fn((...args) => original.setMerchantOffers(...args)),
  };
}
jest.mock('../../common/Storage', () => storeMockFunctions());
import * as storage from '../../common/Storage';

afterEach(() => {
  storage.setMerchantStores.mockClear();
});

it('handle storeListEpic type STORE_LIST_REQUEST -> STORE_LIST_SUCCESS', async () => {
  const scope = nock('http://url')
  .get('/api/merchant/me/stores')
  .reply(200, storeData);
  const result = await storeListEpic(ActionsObservable.of(listStores())).toPromise();
  expect(storage.setMerchantStores.mock.calls).toHaveLength(1);
  expect(await storage.getMerchantStores()).toEqual({ ids: storesApiData.result, list: storesApiData.entities.store});
});

gracias por compartir @ainesophaur. Todavía no puedo hacer que funcione con Jest 18.1. Aquí están mis códigos:

it('should save session correctly', () => {

  function mockFunctions() {
    const original = require.requireActual('./session');
    return {
      ...original,
      restartCheckExpiryDateTimeout: jest.fn((() => {
        console.log('I didn\'t call the original');
      })),
    }
  }

  jest.mock('./session', () => mockFunctions());
  const mockSession = require('./session');

  // NOTE: saveSession() will call the original restartCheckExpiryDateTimeout() instead of my
  // mock one. However, mockSession.restartCheckExpiryDateTimeout() does call the mock one
  mockSession.saveSession('', getTomorrowDate(), 'AUTH');

  // mockSession.restartCheckExpiryDateTimeout(); // does print out "I didn't call the original"

  expect(mockSession.restartCheckExpiryDateTimeout).toHaveBeenCalled();
});

En session.js

export function saveSession(sessionId, sessionExpiryDate, authToken) {
  ....
  restartCheckExpiryDateTimeout(sessionExpiryDate);
  ...
}
....

export function restartCheckExpiryDateTimeout(...) {
....
}

No encuentro la forma de resolver este problema. ¿Puedo reabrir esto por favor? @cpojer

@huyph, la forma en que está haciendo la exportación saveSession va a llamar al restartCheckExpiryDateTimeout localmente definido en lugar de pasar por el módulo y llamar a module.restartCheckExpiryDateTimeout , por lo que su module.restartCheckExpiryDateTimeout simulado saveSession ya que saveSession está llamando a la función restartCheckExpiryDateTimeout definida real.

Asignaría saveSession a una constante y luego haría saveSession.restartCheckExpiryDateTimeout = () => {...logic} . .luego desde dentro de saveSession.saveSession , llame a saveSession.restartCheckExpiryDateTimeout lugar de solo restartCheckExpiryDateTimeout . Exporte su nueva constante en lugar de la función actual saveSession que luego define sus métodos. Luego, cuando llame a su someVar.saveSession() , llamará internamente a saveSession.restartCheckExpiryDateTimeout() que ahora se burla.

Debería haber agregado que restartCheckExpiryDateTimeout() es una función exportada. No es una función definida localmente dentro de saveSession() ... (Actualicé mi comentario anterior). En ese caso, creo que module.saveSession() debería llamar al module.restartCheckExpiryDateTimeout() correcto que se burla.

Pero daré una oportunidad a lo que sugieres anteriormente. Mover saveSession() y restartCheckExpiryDateTimeout() a otra const. Gracias

Entiendo que no está definido en el alcance de saveSession. saveSession es
llamando al método hermano en el ámbito principal. Me encontré con esto muchas veces
y lo que sugerí funcionó para ello

El 8 de mayo de 2017 a las 8:38 p.m., "Huy Pham" [email protected] escribió:

Debería haber agregado que restartCheckExpiryDateTimeout () es un exportado
función. No es una función definida localmente dentro de saveSession () ...

Sin embargo, daré una oportunidad a lo que sugieres anteriormente. Gracias

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300029003 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/AEeBdsmpOOmzvcUHB3D_-Z7MChIzt10Pks5r37WYgaJpZM4IPGAH
.

Intenté eso ... encontré que:

Esto NO funciona: (es decir, se sigue llamando al restartCheckExpiryDateTimeout () original)

export session = {
   saveSession: () => {
      session.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Esto funciona: (es decir, en su lugar se llama al simulacro de reinicioCheckExpiryDateTimeout ()). La diferencia es el uso de function() lugar de la forma de flecha, y el uso de this. lugar de session.

export session = {
   saveSession: function() {
      this.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Podría ser un problema con la transpiración de estos códigos ...

Intente exportarlos como un objeto de clase en lugar de pojo. Creo que el
transpiler eleva las variables de manera diferente. Llegaremos a un trabajo
prueba, lo prometo ... Ha pasado aproximadamente medio año desde que estuve en el proyecto
que usó broma pero recuerdo bien este problema y lo recuerdo eventualmente
encontrar una solución.

El 9 de mayo de 2017 a las 12:53 a. M., "Huy Pham" [email protected] escribió:

Intenté eso ... encontré que:

Esto NO funciona: (es decir, el restartCheckExpiryDateTimeout () original es
todavía me llaman)

exportar sesión = {
saveSession: () => {
session.restartCheckExpiryDateTimeout ();
},
restartCheckExpiryDateTimeout: () => {},
}

Esto NO funciona: (es decir, el restartCheckExpiryDateTimeout () original es
todavía me llaman)

exportar sesión = {
saveSession: function () {
this.restartCheckExpiryDateTimeout ();
},
restartCheckExpiryDateTimeout: () => {},
}

Podría ser un problema con la transpiración de estos códigos ...

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/facebook/jest/issues/936#issuecomment-300060975 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/AEeBdrRQExycPYiGtvm7qYi5G87w6b6Oks5r3_FlgaJpZM4IPGAH
.

@sorahn mismo problema. es6 + babel , ¿Cómo burlarse?
@cpojer ¿ es6 + babel , export const function xx() {} , exportar muchas funciones, Jest no tiene forma de simular una función en un módulo (archivo) llamado por otra función en el mismo módulo (archivo)? Lo pruebo, parece que estoy en lo cierto. Solo por el patrón commonjs , Jest puede burlarse de la función con éxito, como su ejemplo.

@ainesophaur no funciona.

módulo:

export const getMessage = (num: number): string => {
  return `Her name is ${genName(num)}`;
};

export function genName(num: number): string {
  return 'novaline';
}

prueba:

function mockFunctions() {
  const original = require.requireActual('../moduleA');
  return {
    ...original,
    genName: jest.fn(() => 'emilie')
  }
}
jest.mock('../moduleA', () => mockFunctions());
const moduleA = require('../moduleA');

describe('mock function', () => {

  it('t-0', () => {
    expect(jest.isMockFunction(moduleA.genName)).toBeTruthy();
  })

  it('t-1', () => {

    expect(moduleA.genName(1)).toBe('emilie');
    expect(moduleA.genName).toHaveBeenCalled();
    expect(moduleA.genName.mock.calls.length).toBe(1);
    expect(moduleA.getMessage(1)).toBe('Her name is emilie');
    expect(moduleA.genName.mock.calls.length).toBe(2);

  });

});

resultado de la prueba:

FAIL  jest-examples/__test__/mock-function-0.spec.ts
  ● mock function › t-1

    expect(received).toBe(expected)

    Expected value to be (using ===):
      "Her name is emilie"
    Received:
      "Her name is novaline"

      at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)

  mock function
    ✓ t-0 (1ms)
    ✕ t-1 (22ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        0.215s, estimated 1s

Mira mis últimos comentarios arriba. Concretamente el último. Tu
Los métodos exportados están llamando al método hermano de ámbito local frente al
método exportado real (que es donde está su simulacro)

El 31 de mayo de 2017 a las 2:00 a. M., "Novaline" [email protected] escribió:

@ainesophaur https://github.com/ainesophaur no funciona.

módulo:

export const getMessage = (num: number): string => {
return Her name is ${genName(num)} ;
};
función de exportación genName (num: number): string {
return 'novaline';
}

prueba:

function mockFunctions () {
const original = require.requireActual ('../ moduleA');
regreso {
...original,
genName: jest.fn (() => 'emilie')
}
} jest.mock ('../ moduleA', () => mockFunctions ()); const moduleA = require ('../ moduleA');
describir ('función simulada', () => {

it ('t-0', () => {
esperar (jest.isMockFunction (moduleA.genName)). toBeTruthy ();
})

it ('t-1', () => {

expect(moduleA.genName(1)).toBe('emilie');
expect(moduleA.genName).toHaveBeenCalled();
expect(moduleA.genName.mock.calls.length).toBe(1);
expect(moduleA.getMessage(1)).toBe('Her name is emilie');
expect(moduleA.genName.mock.calls.length).toBe(2);

});

});

resultado de la prueba:

FAIL jest-examples / __ test __ / mock-function-0.spec.ts
● función simulada ›t-1

expect(received).toBe(expected)

Expected value to be (using ===):
  "Her name is emilie"
Received:
  "Her name is novaline"

  at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
  at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)

función simulada
✓ t-0 (1 ms)
✕ t-1 (22 ms)

Grupos de pruebas: 1 falló, 1 en total
Pruebas: 1 falló, 1 pasó, 2 en total
Instantáneas: 0 en total
Tiempo: 0,215 s, estimado 1 s

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/facebook/jest/issues/936#issuecomment-305091749 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/AEeBdv6SafXlTtKo3DNeFWhbL6gV9l0Gks5r_QHjgaJpZM4IPGAH
.

@ainesophaur : export class Session { } . Y no me funciona.

El único enfoque que funciona para mí es en mi comentario anterior: donde se usa la sintaxis function lugar de la flecha () => . Aquí:

export const session = {
   saveSession: function() {
      this.restartCheckExpiryDateTimeout();
   },
   restartCheckExpiryDateTimeout: () => {},
}

Esto está en Jest 20.0.3

Lo que hago es crear un contenedor const para las funciones y luego exportar ese contenedor (como export const fns). Luego, dentro del módulo, use fns.functionName, y luego puedo jest.fn () la función fns.functionName

Cuando escribimos una función simulada de un módulo definido por el usuario que está escrito en mecanografiado y cuando llamamos a la función simulada, es la función original cubierta en el informe de cobertura porque estamos llamando a la versión simulada de la función.

Tengo 2 funciones que se importaron originalmente en las pruebas como
import { getCurrentDate, getStartEndDatesForTimeFrame } from ./../_helpers/date';
Como puede ver, getStartEndDatesForTimeFrame depende de getCurrentDate . Con la siguiente configuración, la prueba getCurrentDate funciona bien y usa la versión simulada. Por otro lado, por alguna razón, la prueba getStartEndDatesForTimeFrame no usa el getCurrentDate sino la implementación original, por lo que mi prueba falla. He probado muchas configuraciones diferentes (como Date.now = jest.fn(() => "2017-11-16T20:33:09.071Z"); pero no pude hacer que funcionara. ¿Alguna idea?

export const getCurrentDate = () => new Date();
export const getStartEndDatesForTimeFrame = (timeFrame) => {
  ...
  const todayDate = getCurrentDate();
  ...
  switch (timeframe) {
    case TimeFrames.TODAY:
      console.log(todayDate); // this always prints the real value in tests instead of the mocked one
      start = new Date(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate(), 0, 0, 0);
      end = new Date(
        todayDate.getFullYear(),
        todayDate.getMonth(),
        todayDate.getDate(), 23, 59, 59,
      );
      break;
  ...
  return { start: start.toISOString(), end: end.toISOString() }
};
function mockFunctions() {
  const original = require.requireActual('../../_helpers/date');
  return {
    ...original,
    getCurrentDate: jest.fn(() => '2017-11-16T20:33:09.071Z'),
  }
}
jest.mock('../../_helpers/date', () => mockFunctions());
const dateModule = require.requireMock('../../_helpers/date');

describe('getCurrentDate', () => {
  it('returns the mocked date', () => {
    expect(dateModule.getCurrentDate()).
      toBe('2017-11-16T20:33:09.071Z'); // this works well and returns the mocked value
  });
});

describe('getStartEndDatesForTimeFrame', () => {
  it('returns the start and end dates for today', () => {
    expect(dateModule.getStartEndDatesForTimeFrame('today')).toEqual(
      { 'start': '2017-11-15T23:00:00.000Z', 'end': '2017-11-16T22:59:59.000Z' }
    ); // this one uses the original getCurrentDate instead of the mocked one :(
  });
});

Entonces el getStartEndDatesForTimeFrame falla ya que usa la hora actual y no la simulada.

Me las arreglé para que funcione siguiendo una sugerencia de @ainesophaur : exportando todas las funciones dentro de un objeto y llamando a los métodos de estos objetos exportados en lugar de los métodos hermanos de ámbito local:

// imageModel.js
const model = {
  checkIfImageExists,
  getImageUrl,
  generateImagePreview
}
export default model

async function checkIfImageExists(...) {}
async function getImageUrl() {}
async function generateImagePreview() {
  // I am calling it as `model` object's method here, not as a locally scoped function
  return model.getImageUrl(...)
}

// imageModel.test.js
import imageModel from './imageModel'

test('should mock getImageUrl called within the same file', async () => {
  imageModel.getImageUrl = jest.fn().mockReturnValueOnce(Promise.resolve())

  await imageModel.generateImagePreview()

  expect(imageModel.getImageUrl).toBeCalled()
})

@miluoshi Esa es la única forma en que pude hacerlo también. ¿Hay alguna pérdida de rendimiento o algo así cuando usamos este método? Parece "incorrecto" cambiar el código para poder probarlo.

Realmente me gustaría una forma de simplemente escribir:
jest.mock('src/folder/file.func, () => {return 'whatever i want'})

la pieza clave aquí es el .func

@miluoshi @Rdlenke si su código consiste en exportaciones con nombre, también puede import * as model y luego sobrescribir model.generateImagePreview = jest.fn(() => Promise.resolve);

¿Cómo probarías eso con sinon? Como se mencionó anteriormente (consulte https://github.com/facebook/jest/issues/936#issuecomment-214939935), la forma en que funciona ESM hace que sea imposible burlarse de func2 dentro de func1 , por lo que No necesariamente lo llamaría básico.

Tal vez se pueda escribir un mod de babel que se lea en cualquier función "testImport"
y reescribe el código para exportar las funciones en el módulo antes de la
prueba de funcionamiento?

El lunes 18 de diciembre de 2017 a las 5:00 p.m., Jim Moody [email protected] escribió:

Tienes razón @SimenB https://github.com/simenb , había cambiado algo
en mi prueba entre cambiar a Sinon, lo que parece que pasó.
Cuando revirtí eso, todavía no funciona. Supongo que no es un problema
eso ha sido resuelto.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/facebook/jest/issues/936#issuecomment-352488400 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/AQRY9a5-s2_bjCWKNw5WiAJW-JeBf8W3ks5tBpoygaJpZM4IPGAH
.

-

Darren Cresswell
Desarrollador de contrato | Develer Limited
Correo electrónico: [email protected]
Teléfono:
Sitio web: http://www.develer.co.uk

Por favor considere el medio ambiente antes de imprimir este e-mail
ADVERTENCIA: Los virus informáticos pueden transmitirse por correo electrónico. El recipiente
debe revisar este correo electrónico y cualquier archivo adjunto para detectar la presencia de virus.
Develer Limited no acepta ninguna responsabilidad por los daños causados ​​por virus.
transmitido por este correo electrónico. No se puede garantizar que la transmisión de correo electrónico sea
seguro o libre de errores, ya que la información podría ser interceptada, corrompida, perdida,
destruidos, llegan tarde o incompletos, o contienen virus. El remitente
por lo tanto, no acepta responsabilidad por cualquier error u omisión en el
contenido de este mensaje, que surgen como resultado de la transmisión de correo electrónico.

ADVERTENCIA: Aunque Develer Limited ha tomado precauciones razonables para
asegúrese de que no haya virus presentes en este correo electrónico, la empresa no puede aceptar
responsabilidad por cualquier pérdida o daño que surja del uso de este correo electrónico o
archivos adjuntos.

Develer Limited es una sociedad limitada registrada en Inglaterra y Gales. |
Número de registro de la empresa 09817616 | Oficinas registradas: SUITE 1 SEGUNDO
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, REINO UNIDO, BH7 7DU

gracias @ainesophaur por la solución.

En caso de que alguien encuentre útil un ejemplo de trabajo no asíncrono, aquí está el mío:

//reportError.js
const functions = {};

functions._reportToServer = (error, auxData) => {
  // do some stuff
};

functions.report = (error, auxData = {}) => {
  if (shouldReportToServer()) {
    functions._reportToServer(error, auxData);
  }
};
export default functions;

// reportError.jest.js
import reportError from 'app/common/redux/lib/reportError';
reportError._reportToServer = jest.fn();

describe('test reportError', () => {
  it('reports ERROR to server as as error', () => {
   reportError.report(new Error('fml'), {});
    expect(reportError._reportToServer).toHaveBeenCalledTimes(1);
  });
});

@ jim-moody Si entendí el problema correctamente, esto debería funcionar para tu ejemplo:

const spyOnExampleFunc2 = jest.spyOn(example, 'func2');
example.func1();
expect(spyOnExampleFunc2).toBeCalled();

(esto _sólo_ funciona si las funciones se exportan como constantes, como en su ejemplo)

@dinvlad mi héroe!

Siguiendo la respuesta de @dinvlad , creo que agregar, mostrar por ejemplo o vincular los siguientes documentos relacionados con simulacros en la página del objeto de broma a la página de funciones de Mock podría ser una mejora de los documentos de broma sobre simulacro:

  • jest.isMockFunction (fn)
  • jest.genMockFromModule (nombreMódulo)
  • jest.mock (nombre del módulo, fábrica, opciones)
  • jest.unmock (nombreMódulo)
  • jest.doMock (nombre del módulo, fábrica, opciones)
  • jest.dontMock (nombreMódulo)

Mi caso de uso es que, como nuevo usuario de jest, estoy migrando un código mocha + sinon.js a jest. Ya tenía espías y expectativas, así que pensé que sería fácil. Pero después de leer este hilo y leer los documentos de broma sobre las funciones de Mock, tenía la impresión de que usar broma de esta manera podría necesitar una reescritura de mis pruebas o una comprensión detallada de ESM o Babel ... u otra confusión.

Gracias por Jest, está haciendo que mis pruebas sean más fáciles de escribir / comprender y más rápidas de ejecutar. :)

¡Es muy bienvenido que las relaciones públicas aclaren los documentos! 🙂

Para simular solo módulos específicos con la sintaxis del módulo ES, puede usar require.requireActual para restaurar los módulos originales y luego sobrescribir el que desea simular:

import { foo } from './example';

jest.mock('./example', () => (
  ...require.requireActual('./example'),
  foo: jest.fn()
));

test('foo should be a mock function', () => {
  expect(foo('mocked!')).toHaveBeenCalledWith('mocked!');
});

Se siente invertido, pero es la forma más sencilla con la que me he encontrado. Sugerencia de sombrero para @joshjg.

Estoy perdido en algún lugar de la larga discusión, solo tenía una pregunta, ¿hay alguna forma de probar si se llama a la implementación real de la función?

Por lo que entiendo, si necesito usar jest.fn() , anulará la función original, pero si no la uso, la consola me daría un error diciendo que debe ser jest.fn() function or a spy

Estoy tratando de probar un middleware donde se pasará la solicitud, por lo que si me burlo de ella, se perderá toda la lógica y los datos no se pasarán al siguiente middleware. Si no me burlo de él, al importarlo, ¿hay alguna forma de que pueda probar si se ha llamado a esta función?

¿Puede usar jest.spyOn , tal vez? Por defecto llama a la función subyacente

Gracias por la ayuda, lo intenté, pero la prueba sugiere que nunca se llamó a pesar de que se llamó porque puse el archivo console.log y se imprimió

archivo de prueba

import errorHandler from '../../controller/errorHandler'

describe('auth test', () => {
  describe('test error: ', () => {
    const test1 = jest.spyOn(errorHandler, 'handleClientError')
    test('should return 400', (done) => {
      request(app)
      .post('/auth/error')
      .then((res) => {
        expect(res.statusCode).toBe(400)
        expect(test1).toBeCalled()
        done()
      })
    })

errorHandler

module.exports = {
  handleClientError () {
    console.log('err')
  }
}

consola

console.log src/controller/errorHandler.js:10
      err

  ● auth test › test error:  ›  should return 400

    expect(jest.fn()).toBeCalled()

    Expected mock function to have been called.

      18 |         expect(res.statusCode).toBe(400)
    > 19 |         expect(test1).toBeCalled()
      20 |         done()
      21 |       })
      22 |     })

¿La función se llama handleClientError o logError ?

@WangHansen De su ejemplo, su código debería ser expect(errorHandler.handleClientError).toBeCalled() // > true

@WangHansen, ¿ podrías agregar .mockImplementation() a tu jest.spyOn() ? Como alguien que viene de Jasmine, encontré este consejo crucial para lograr la misma funcionalidad que los espías de Jasmine. P.ej

const mockModuleFunction = jest
  .spyOn(module, 'function')
  .mockImplementation(() => 'hello');
...
expect(mockModuleFunction.mock).toBeCalled();

Si _no_ usa mockImplementation() , entonces jest.spyOn() produce un objeto que _no_ es un simulacro (afaiu) y en realidad difiere de la implementación nativa. Si tiene que mantener la implementación nativa, tal vez valga la pena usarla

const moduleFunction = module.function;
jest.spyOn(module, 'function').mockImplementation(moduleFunction);
...

No estoy seguro de que sea necesario, pero estoy bastante seguro de que _debería_ funcionar.

Volviendo a la solicitud original ...

¿No podría simplemente envolver un proxy alrededor de una importación *? p.ej

importar * como prueba desde './myfile.js';

controlador constante = {
/ ** Intercepciones: obteniendo propiedades * /
get (target, propKey, receptor) {
console.log ( GET ${propKey} );
return 123;
},

/** Intercepts: checking whether properties exist */
has(target, propKey) {
    console.log(`HAS ${propKey}`);
    return true;
}};

const p = nuevo Proxy (prueba);

El martes 30 de enero de 2018 a las 4:24 p.m., Denis Loginov [email protected]
escribió:

@WangHansen https://github.com/wanghansen ¿ podrías agregar
.mockImplementation () a su jest.spyOn ()? Como alguien que viene de
Jasmine, este consejo me pareció crucial para lograr la misma funcionalidad que
Los espías de Jasmine. P.ej

const mockModuleFunction = broma
.spyOn (módulo, 'función')
.mockImplementation (() => 'hola'); ... espera (mockModuleFunction.mock) .toBeCalled ();

Si no usa mockImplementation (), entonces jest.spyOn () produce un
objeto que no es un simulacro (afaiu) y en realidad difiere al nativo
implementación. Si tiene que mantener la implementación nativa, tal vez sea
vale la pena usar

const moduleFunction = module.function; jest.spyOn (módulo, 'función'). mockImplementation (moduleFunction); ...

No estoy seguro de que sea necesario, pero estoy bastante seguro de que debería funcionar.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/facebook/jest/issues/936#issuecomment-361648414 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/AQRY9VXyHNYatwOOY6EV637WGQH9k5Plks5tP0I9gaJpZM4IPGAH
.

-

Darren Cresswell
Desarrollador de contrato | Develer Limited
Correo electrónico: [email protected]
Teléfono:
Sitio web: http://www.develer.co.uk

Por favor considere el medio ambiente antes de imprimir este e-mail
ADVERTENCIA: Los virus informáticos pueden transmitirse por correo electrónico. El recipiente
debe revisar este correo electrónico y cualquier archivo adjunto para detectar la presencia de virus.
Develer Limited no acepta ninguna responsabilidad por los daños causados ​​por virus.
transmitido por este correo electrónico. No se puede garantizar que la transmisión de correo electrónico sea
seguro o libre de errores, ya que la información podría ser interceptada, corrompida, perdida,
destruidos, llegan tarde o incompletos, o contienen virus. El remitente
por lo tanto, no acepta responsabilidad por cualquier error u omisión en el
contenido de este mensaje, que surgen como resultado de la transmisión de correo electrónico.

ADVERTENCIA: Aunque Develer Limited ha tomado precauciones razonables para
asegúrese de que no haya virus presentes en este correo electrónico, la empresa no puede aceptar
responsabilidad por cualquier pérdida o daño que surja del uso de este correo electrónico o
archivos adjuntos.

Develer Limited es una sociedad limitada registrada en Inglaterra y Gales. |
Número de registro de la empresa 09817616 | Oficinas registradas: SUITE 1 SEGUNDO
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, BOURNEMOUTH, REINO UNIDO, BH7 7DU

@dinvlad @iampeterbanjo @SimenB Gracias nuevamente por toda su ayuda, pero desafortunadamente, ninguna de las formas que sugirió funcionó. Me pregunto si es porque la función se llama en forma de next(err) . La lógica es que, cuando falla una solicitud, se pasará al errorHandler llamando a return next(err) . Seguro que se está llamando a la función porque cuando agregué console.log , está imprimiendo. Pero las pruebas sugieren que nunca se llama

@dinvlad Probé tu método, no funcionó, pero gracias por tu ayuda de todos modos. Me preguntaba por qué necesita llamar a mockImplementation , según el documento oficial en jest.spyOn , solo lo llama cuando desea anular la función original.

@WangHansen sí, tienes razón en que solo es necesario cuando uno quiere anular el método original. Solo estaba lanzando una idea para esas situaciones.

Una de las razones por las que podría haberle fallado es la asincronicidad. Si su método utiliza devoluciones de llamada y / o promesas (o async / await), debe asegurarse de que sus expectativas se ejecuten realmente antes de que finalice su método de prueba. Hay un método especial expect.assertions(N) para afirmar eso. También asegúrese de que su expectativa se ejecute solo después de que se haya llamado al código dentro de devoluciones de llamada / promesas. Estoy seguro de que lo has visto, pero solo como referencia, https://facebook.github.io/jest/docs/en/asynchronous.html

Es lamentable que no sea posible burlarse de los fns usados ​​internamente como @seibelj sin cambiar el módulo impl.

En mi opinión, las pruebas no deberían impulsar cómo se implementa la lógica empresarial 😕 (al menos no en este grado)

¿Hay planes para implementar tal comportamiento en broma?

Hola,
Un poco tarde para toda la discusión, pero leyendo toda la discusión, todavía no he logrado hacer eso.
La prometedora solución de @greypants realmente no funcionó para mí, ya que todavía llama a la función original.
¿Ha cambiado algo desde el comienzo de esta discusión? ¿Me estoy perdiendo de algo?

Adapté un poco la solución de

import Module from './module'
import { getJSON } from './helpers'

jest.mock('./helpers', () =>
  Object.assign(require.requireActual('./helpers'), {
    getJSON: jest.fn()
  })
)

test('should use the mock', async () => {
  getJSON.mockResolvedValue('foo')
  await expect(module().someAsyncThingThatUsesGetJSON()).resolves.toEqual('foo')
})

test('should use the actual module', () => {
  expect(module().someFunctionThatUsesAHelper()).toBe(true)
})

Esto todavía se siente un poco extraño y los documentos no fueron tan útiles. Estoy agradecido por Jest y el equipo detrás de él, pero parece un caso de uso común que al menos debería tener una solución "oficial".

@sarahdayan , y quien sea de su interés -
Terminé usando babel-plugin-rewire.
Me tomó un tiempo encontrar ese complemento, pero fue una solución lo suficientemente coherente como para no sentirme hack.

En la mayoría de los casos, no queremos una maqueta o más funciones de un módulo. Si usa el sistema de burla de global jest, puede lograrlo usando genMockFromModule y requireActual . Aquí está el ejemplo:

//  __mocks__/myModule.js
const moduleMock = jest.genMockFromModule('./../myModule');

// in most cases we don't want to mock `someFunction`
moduleMock.someFunction = jest.fn(
  (...args) => require.requireActual('./../myModule').someFunction(...args)
)

module.exports = moduleMock;

Esta solución permite usar simulacros para otras funciones del módulo, usando someFunction implementación original cuando se simula todo el módulo y también permite simular la función someFunction con mockImplementationOnce o mockImplementation API.

He leído todas las conversaciones anteriores, pero ninguna solución me funciona.
Si todavía está buscando una solución para este caso de prueba, la respuesta es babel-plugin-rewire , este complemento es para resolver ese caso de escenario que discutimos.
Por favor, eche un vistazo a esa biblioteca, me lo agradecerá más tarde.

Entonces, para resumir todo el hilo anterior:

  1. Suponga que tiene un módulo m con funciones f , g y h donde g y h llamar f . Nos gustaría simular f para que g y h llamen al simulacro en lugar del f real. Desafortunadamente, esto no es f través de exports como se describe en cpojer . Esto es imposible si su módulo usa la sintaxis de importación / exportación ES6 en TypeScript (y supongo que lo mismo es cierto en Babel).
  2. Sin embargo, supongamos que movemos f a otro módulo m2 . Entonces m tendrá una declaración como import {f} from 'm2' y cuando g y h llamen f , en realidad están llamando m2.f donde m2 = require('./m2') (así se verá la traducción de Babel / TypeScript). Esto hace posible burlarse de f manera confiable como lo describen los pantalones grises . En otras palabras, puede simular llamadas de manera confiable solo si cruzan el límite de un módulo . Nota: la solución de greypants ahora produce este mensaje de error: "La fábrica de módulos de jest.mock() no puede hacer referencia a ninguna variable fuera de alcance - Acceso a variable no válido: __assign". Sospecho que esto es un error en Jest; como solución alternativa, use Object.assign como se muestra a continuación.
  3. Pero si, en lugar de burlarse de una o dos funciones, quiere burlarse de todo excepto una o dos funciones, use código como el de darkowic .

Ejemplo de (2):

~~~ js
// módulo m.js
importar {f} desde './m2'
función de exportación g () {return 'f devuelto' + f (); };

// módulo m2.js
función de exportación f () {devuelve 'la f real'; }

// test.js
importar * como m desde './m'

jest.mock ('./ m2', () => Object.assign (
require.requireActual ('./ m2'), {
f: jest.fn (). mockReturnValue ('MOCK')
}));

prueba ('burlado', () => {
esperar (mg ()). toEqual ('f devolvió MOCK');
});
~~~

Mientras probaba esto, me encontré con # 2649: llamar a jest.mock dentro de una prueba no tiene ningún efecto, y si lo llama en el alcance global, entonces no puede unmock antes de otras pruebas. Muy molesto.

¡¡Gracias!! @sarahdayan
He estado buscando esto por un tiempo

Si faltan los documentos, los RP siempre son bienvenidos para aclararlos 🙂

¡Hola a todos!

Jugué un poco y tuve la siguiente idea para resolver ese problema:

  • simula un módulo, pero el módulo simulado tiene el módulo original en la cadena de prototipos.
  • proporcionar un método para agregar propiedades al módulo simulado (que anulará las propiedades del prototipo)
  • también proporciona un método para eliminar las propiedades del módulo simulado (para usar el del prototipo nuevamente).
// m1.js
export const f = () => "original f"

// __mocks__/m1.js
const originalM1 = require.requireActual("../m1");
// set the original as a prototype
let mockModule: any = Object.create(originalM1);
const __setMock = (name, value) => {
  mockModule[name] = value;
};
const __removeMock = (name) => {
  Reflect.deleteProperty(mockModule, name);
};
// enhance the mocked module to allow overriding original exports
module.exports = Object.assign(mockModule, { __setMock, __removeMock });


// m1.test.js
import { f } from "./m1";

jest.mock("./m1");

test("mocking stuff", () => {
  // here nothing is mocked - the original module is used
  expect(f()).toBe("original f");

  // override the export f of the module
  require("./m1").__setMock("f", () => "mocked f");
  expect(f()).toBe("mocked f");

  // set it back to the original
  require("./m1").__removeMock("f");
  expect(f()).toBe("original f");

  //override with another value
  require("./m1").__setMock("f", () => "another mocked f");
  expect(f()).toBe("another mocked f");
});

Mis 2 centavos:

Probé muchas soluciones (si no todas) y la única que funcionó para mí es esta (broma 23):

// importedModule.js
export const funcB = () => {
  return "not mocked";
};
export const funcA = () => {
  return mockProxy.funcB(); // this works but it's soooo hacky
};

export const mockProxy = {
  funcB
};

// moduleToTest.js
import { funcA } from "./moduleImported.js";

export default function() {
  return funcA();
}

// test
let moduleImported = require("./moduleImported.js");
moduleImported.mockProxy.funcB = jest.fn(() => "mocked");
const funcToTest = require("./moduleToTest.js").default; // or import

it("should return mocked", function() {
  expect(funcToTest()).toBe("mocked");
});

Llegué a la conclusión de que no es una buena idea intentar hacer esto porque:

  • test.js sabe demasiado sobre los detalles de implementación de importedModule.js
  • la solución es demasiado frágil y nadie entenderá el propósito de mockProxy mirando importedModule.js

¿Alguien ha encontrado una solución para esto que funcione?

Estoy usando:

"jest": "^21.2.1",
"jest-cli": "^21.2.1",

@jamesone, ¿has leído esto https://github.com/facebook/jest/issues/936#issuecomment -410080252?

Esto funcionó para mí, según la respuesta de @thomaskempel :

En mi caso, quería simular una dependencia en node_modules, llamémoslo 'componentes compartidos'. Exporta varios componentes con nombre. Quería burlarme de solo un par de estas exportaciones con nombre y dejar el resto como algo real.

Entonces en __mocks__/shared-components.js tengo:

const original = require.requireActual('shared-components');

module.exports = {
...original,
moduleNameToOverride: jest.fn().mockImplementation(() => {
      return 'whatever';
    }),
}

En mi caso, estaba eliminando las implementaciones. Ojalá esto ayude a alguien en el futuro.

Me enfrenté al mismo problema recientemente, la conversación en este hilo me ayudó a comprender mejor y resumí mis hallazgos aquí https://medium.com/@DavideRama/mock -spy-exported-functions-within-a-single- módulo-en-broma-cdf2b61af642

Inspirado en la solución de @qwertie

mockGet = jest.fn()
jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  get: mockGet
}))

@MajorBreakfast que funciona con React.lazy ?

const mockLazy = jest.fn();

jest.mock('React', () => ({
    ...jest.requireActual('React'),
    lazy: mockLazy
}));

Todavía obtengo ReferenceError: React is not defined .

Para simular un módulo de función de exportación independiente:
Exporte todas las funciones individuales que forman parte de la exportación predeterminada del archivo.

Ejemplo:
dataDao.js

función getData ()
función setData ()
función deleteData ()
exportar {getData, setData, deleteData}

Ahora puede importar todas las funciones del archivo a su prueba de broma por nombre predeterminado;

dataDao.spec.js

importar * como dataDao desde '../dataDao';
// Espiar los módulos que hacen referencia al nombre predeterminado asignado en la importación
jest.spyOn (dataDao, 'getData')
jest.spyOn (dataDao, 'setData')
jest.spyOn (dataDao, 'deleteData')

@vchinthakunta , eso puede funcionar, pero parece una violación de un propósito principal de la sintaxis de exportación / importación: otros módulos ya no podrán importar métodos específicos o campos de datos a través de

import { justThisThing } from 'someModule';

¿Me estoy perdiendo algo ahí?

@MajorBreakfast que funciona con React.lazy ?

const mockLazy = jest.fn();

jest.mock('React', () => ({
    ...jest.requireActual('React'),
    lazy: mockLazy
}));

Todavía obtengo ReferenceError: React is not defined .

Creo que 'React' debería estar en minúsculas aquí ya que hace referencia a la importación.

jest.mock('react'...)

Hice funcionar mi código usando lo siguiente, que es más simple que otras soluciones que he visto aquí. No requiere que use require o configure default exportaciones.

helpers / navigation.js

export const initHeader = () => {
    // initialise header
    ...
}

...

export const anotherHelperFunction = () => {
    // do stuff
    ...
}

componente que usa navigation.js

import { initHeader } from '../helpers/navigation';

jest.mock('../helpers/navigation');

...

describe('Test component', () => {

    it('should reinitialise header', () => {
        const mockHeaderInit = jest.fn();
        initHeader.mockImplementation(mockHeaderInit);

        const component = mountComponent(mockProps);
        component.simulate('click');

        expect(mockHeaderInit).toBeCalled();
    }
}
mockGet = jest.fn()
jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  get: mockGet
}))
ReferenceError: mockGet is not defined

       4 | const mockGet = jest.fn();
       5 | jest.mock('./browserStorage', () => ({
       6 |   ...jest.requireActual('./browserStorage'),
    >  7 |   get: mockGet,
         |        ^
       8 | }));

jest.mock está izado, use doMock o

jest.mock('./browserStorage', () => ({
  ...jest.requireActual('./browserStorage'),
  get: jest.fn(),
}));

const {get: mockGet} = require('./browserStorage');

Esto parece un problema bastante común. ¿Hay algo sobre esto en los documentos de broma?

Entonces, ¿existe una solución para burlarse de una función dentro del mismo módulo ?

El siguiente método funcionó para mí, el truco es restablecer la función simulada al final de la prueba.
Este ejemplo simula la función de verificación del módulo jsonwebtoken.

  test('perform my test', async () => {
    // save the real jwt.verify function
    const verify = jwt.verify
    // mock it.
    jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
    // do the test
    ...
    // set the real function back.
    jwt.verify = verify
  })

El siguiente método funcionó para mí, el truco es restablecer la función simulada al final de la prueba.
Este ejemplo simula la función de verificación del módulo jsonwebtoken.

  test('perform my test', async () => {
    // save the real jwt.verify function
    const verify = jwt.verify
    // mock it.
    jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
    // do the test
    ...
    // set the real function back.
    jwt.verify = verify
  })

¿Dónde estás usando la const verify ? de todos modos, esto solo funcionará si su función simulada no es una función const exportada

Entonces, ¿hay una solución para burlarse de una función _ dentro del mismo módulo_?

Después de mucha búsqueda, la solución para este problema es almacenar sus exportaciones en un solo objeto al que su función y simulacros puedan hacer referencia. Todos los artículos a continuación llegaron al mismo consenso.

https://github.com/facebook/jest/issues/936#issuecomment -438975674
https://medium.com/@qjli/how -to-mock-specific-module-function-in-jest-715e39a391f4
https://luetkemj.github.io/170421/mocking-modules-in-jest

Me sorprende que nadie haya mencionado una solución diferente que, si es viable para su código, elimina por completo todos estos problemas y hace que probar el código deseado sea muy, muy simple:

_Mueva la función única que desea simular en su propio módulo._

Seriamente. Si su módulo está escrito de tal manera que necesita probar las partes internas de él por separado de otras partes internas, entonces es casi seguro que su clase está violando el Principio de Responsabilidad Única (sí, no es una clase real, pero el módulo está funcionando como una clase, módulos que son un contenedor unitario de código). Divide ese tonto y boom, puedes burlarte de los requisitos.

Si la función simulada se basa en un grupo de estados privados, esta no es una buena razón para no dividir su módulo de alguna manera. El mero hecho de que se base en un montón de estados internos implica, para mí, que las preocupaciones del módulo no están claramente pensadas. Quizás haya incluso un tercer módulo para dividir, que representa algún tipo de clase de datos o DTO, que se puede pasar como argumento.

Además, ¿está exportando funciones solo para pruebas que de otro modo serían privadas? ¿Por qué el código externo llamaría directamente a la función simulada, pero también llamaría a las otras funciones que necesitan llamarla? Apuesto a que hay una especie de desconexión aquí. Tal vez la función simulada debe permanecer, pero todas las funciones deben dividirse por la mitad y la mitad se debe quitar y pasar a otro módulo. Entiendes la idea.

Cuando las pruebas se vuelven muy difíciles, casi siempre es una señal de que se necesita una refactorización ...

No se requieren tonterías de prueba:

const tomfoolery = require('tomfoolery'); // no longer required

Después de leer este hilo y probar las soluciones propuestas, todavía no puedo hacer que esto funcione. Por lo que he leído, algunas personas hicieron que esto funcionara, pero no puedo entender cómo.

¿Alguien podría decirme el código que necesito agregar al siguiente ejemplo para que las pruebas pasen?

// a.js
export const foo = () => 'foo-' + bar()
export const bar = () => 'bar'
// a.test.js
import {
  foo,
  bar
} from './a'

describe('foo', () => {
  it('should return foo-MOCKED_BAR', () => {
    expect(foo()).toBe('foo-MOCKED_BAR')
  })

  it('should have the mocked implementation of bar', () => {
    expect(bar()).toBe('MOCKED_BAR')
  })
})

describe('bar', () => {
  it('should have the original implementation of bar', () => {
    expect(bar()).toBe('bar')
  })
})

describe('foo and bar together', () => {
  it('should have the original implementation of both', () => {
    expect(foo()).toBe('foo-bar')
  })
})

¡Gracias!

Solo quería burlarme de un único método lodash como lodash.random y pude hacerlo fácilmente con:

module.js

const lodash = require('lodash');

module.exports = function() {
  return lodash.random();
}

test.js

const lodash = require('lodash');
const module = require('./module.js);

it('mocks lodash', () => {
    jest.spyOn(lodash, 'random').mockImplementationOnce(() => {
      return 2;
    });

    expect(module()).toEqual(2)
});

Espero que ayude :)

Algo que funcionó para nuestro equipo que trabaja con mecanografiado fue crear un const que exportamos en lugar de exportar la función directamente.
No funciona:
export function doSomething(a, b) {}
Laboral:
export const doSomething = function (a, b) {}

Cambié la exportación como hace @arbielsk , ¡funciona! Pero no sé cuál es la diferencia entre dos tipos de exportación ...

@dgrcode ¿Alguna vez encontró una solución para su ejemplo? Por lo que puedo decir, lo que estás tratando de hacer no es compatible con burlarte a través de Jest. Específicamente, creo que burlarse es básicamente recablear las importaciones para que las vistas externas del módulo vean los métodos burlados. Sin embargo, en su ejemplo, foo y bar están en el mismo módulo, por lo que la vista de foo de bar no se puede burlar.

Creo que tus opciones son:
1) Reorganice su código para que foo importe un módulo que incluya bar
2) Utilice babel-plugin-rewire

¡Por favor, corrígeme si no entiendo bien!

Tenía un requisito ligeramente diferente: quería simular un módulo completo _excepto_ para una función. Usando la solución de @MajorBreakfast como punto de partida, se me ocurrió lo siguiente:

jest.mock('my-module', () => ({
  ...jest.genMockFromModule('my-module'),
  myFunction: jest.requireActual('my-module').myFunction
}))

@dgrcode ¿Alguna vez encontró una solución para su ejemplo? Por lo que puedo decir, lo que estás tratando de hacer no es compatible con burlarte a través de Jest. Específicamente, creo que la burla es básicamente recablear las importaciones para que las vistas externas del módulo vean los métodos simulados. Sin embargo, en su ejemplo, foo y bar están en el mismo módulo, por lo que la vista de foo de bar no se puede burlar.

Creo que tus opciones son:

  1. Reorganice su código para que foo importe un módulo que incluya bar
  2. Utilice babel-plugin-rewire

¡Por favor, corrígeme si no entiendo bien!

Ese fue en realidad mi caso
Mi comprensión de cómo se podían simular los módulos estaba en mal estado 🤦‍♂

Básicamente
Si 2 funciones están en el mismo módulo y se llaman entre sí

Si foo está llamando bar

function bar() {
  return 'some-result'
}

function foo(){
  return bar()  // <-- the function I want to mock 
}

Pon bar en un archivo nuevo (para burlarse)

Moví el método bar en un nuevo archivo, y ahora podría usar muchos de los ejemplos anteriores
Muchas gracias a @ yoni-abtech por hacerme entender eso 🤣

De la forma en que lo veo después de leer todo este hilo y probar una y otra vez, hay 3 opciones ...

Opción 1 - declarar todas las funciones usando const

Esto requiere que exija el uso de expresiones de declaraciones . Afortunadamente, la regla func-style eslint te respalda.

El uso de export const permite spyOn funciones que son utilizadas por otras funciones dentro del módulo _same_ .

// hello.js
export const message = () => {
  return 'Hello world';
}

export const foo = () => {
  return message();
}
// hello.test.js
import * as testModule from './hello.js';

describe('test spyon with function expressions', function () {
  afterAll(() => {
    jest.restoreAllMocks();
  });
  it('should NOT mock message in foo', function () {
    const actual = testModule.foo();

    expect(actual).toBe('Hello world');
  });

  it('should mock message in foo', function () {
    jest.spyOn(testModule, 'message').mockReturnValue('my message');

    const actual = testModule.foo();

    expect(actual).toBe('my message');
    expect(testModule.message).toHaveBeenCalledTimes(1);
  });
});

Opción 2: use el complemento rewire babel

Si no desea imponer el uso de expresiones de función (es decir, usar const ), este podría ser un buen enfoque.

Esto le permite _rewire_ (también conocido como simulacro) funciones desde el mismo módulo. Podría imaginar que el código se vería así a continuación, pero no lo he probado. Además, según sus documentos , parece que puede volver a cablear funciones en el mismo módulo que ni siquiera se exportan desde el módulo 👍, imagine el siguiente ejemplo sin exportar la función message .

Ejemplo:

// hello.js
export function message() {
  return 'Hello world';
}

export function foo() {
  return message();
}
// hello.test.js
import * as testModule from './hello.js';

describe('test rewire api', function() {
  it('should NOT mock message in foo', function () {
    const actual = testModule.foo();

    expect(actual).toBe('Hello world');
  });

  it('should mock message in foo', function () {
    testModule.__RewireAPI__.__Rewire__('message', jest.fn().mockReturnValue('my message'));

    const actual = testModule.foo();

    expect(actual).toBe('my message');
    expect(testModule.message).toHaveBeenCalledTimes(1);
    testModule.__RewireAPI__.__ResetDependency__('message');
  });
});

Consulte los documentos para ver ejemplos.

Nota: Requiere transpilación babel

Opción 3: separar todas las funciones en módulos / archivos separados

Esta opción es la menos favorable, pero claramente funcionaría bien con la funcionalidad típica mock .


PD: Aunque esta pisada fue muy esclarecedora y, a menudo, entretenida, espero que esta sinopsis mitigue la necesidad de que otros lean todo el hilo. ✌️

Gracias @nickofthyme , acabas de terminar un par de días golpeándome la cabeza contra esto.

@nickofthyme tu option 1 falla tanto en mi aplicación como en create react app :

 FAIL  src/hello.test.js
  test spyon with function expressions
    ✓ should NOT mock message in foo (3ms)
    ✕ should mock message in foo (6ms)

  ● test spyon with function expressions › should mock message in foo

    expect(received).toBe(expected) // Object.is equality

    Expected: "my message"
    Received: "Hello world"

      17 |     const actual = testModule.foo();
      18 |
    > 19 |     expect(actual).toBe("my message");
         |                    ^
      20 |     expect(testModule.message).toHaveBeenCalledTimes(1);
      21 |   });
      22 | });

      at Object.toBe (src/hello.test.js:19:20)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        1.848s
Ran all test suites related to changed files.

@danielhusar Pareces tener razón. Lo siento, debería haber probado esto con CRA.

Obtuve la burla descrita en la opción 1 para que funcione aquí . Pruébelo usando el script yarn test:hello .

Resultado

> yarn test:hello
yarn run v1.16.0
$ jest --config=jest.config.js -t=hello --verbose
 PASS  src/hello/hello.test.ts
  test hello
    ✓ should NOT mock message in foo (3ms)
    ✓ should mock message in foo (1ms)

Test Suites: 1 skipped, 1 passed, 1 of 2 total
Tests:       1 skipped, 2 passed, 3 total
Snapshots:   0 total
Time:        1.392s
Ran all test suites with tests matching "hello".
✨  Done in 2.36s.

Requiere usar un archivo jest.config.js usando ts-jest y llamar a jest --config=./jest.config.js directamente, no a través de react-scripts . No estoy seguro de cómo está configurado jest en react-scripts pero creo que puede haber una forma de actualizar la configuración de alguna manera.

Esta corrección elimina las transformaciones de los archivos *.css y *.svg , así que ignore los errores App.tsx .

¿Hay algo en particular que deba hacerse para que funcione?
Yo diría que tengo una configuración bastante estándar (sin ts) y no funciona de inmediato.

Lo miraré un poco más esta noche y veré si es posible.

@danielhusar Miré anoche y no pude encontrar una solución lista para transformer que CRA le permite anular en package.json#jest . Los archivos js y ts se transpilan usando babel-jest pero react-scripts impide usar un archivo de configuración .babelrc y configurar el test env que configuraron en react-scripts test aquí .

Ojalá pudiera profundizar más, pero no tengo tiempo ahora.

Hmm todavía estoy luchando un poco para que funcione (en mi configuración personalizada, no en el cra).
(última versión de jest y babel-jest)

Esta es mi configuración de broma:

module.exports = {
  name: 'my-app',
  testURL: 'http://localhost/',
  setupFiles: ['<rootDir>/setup-jest.js'],
  setupFilesAfterEnv: ['<rootDir>/setup-test.js'],
  testMatch: ['**/__tests__/**/*.test.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
  testEnvironment: 'jest-environment-jsdom-fifteen',
  snapshotSerializers: ['enzyme-to-json/serializer'],
  globals: {
    ENV: 'test',
  },
  transform: {
    '^.+\\.[t|j]sx?$': 'babel-jest',
  },
};

Y mi babelrc:

{
  "presets": [
    "@babel/react",
    ["@babel/env", {
      "corejs": "3",
      "useBuiltIns": "entry",
      "loose": true,
      "exclude": [
        "es.string.split"
      ]
    }],
    "@babel/flow"
  ],
  "plugins": [
    "array-includes",
    "lodash",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-class-properties",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-object-rest-spread",
    "@babel/plugin-proposal-optional-chaining"
  ],
  "env": {
    "test": {
      "plugins": ["dynamic-import-node"]
    }
  }
}

Para cualquiera que tenga problemas con la opción 1 , es importante usar la función () => { return expression } lugar de () => (expression) .

Tengo la opción 1 para trabajar modificándola a:

import * as test from './test';

export const message = () => {
    return 'Hello world';
  }

  export const foo = () => {
    return test.message();
  }

No es bonito, pero debería funcionar.

@nickofthyme , la opción 1 es correcta tal como la tiene. Pero si cambia el código a:

const foo = () => {}
export { foo }

Entonces se rompe. Presumiblemente porque crea un nuevo objeto literal y lo exporta.

Interesante observación. Gracias @maletor

Jest tiene un ejemplo muy simple y claro en su documentación de cómo simular parcialmente un módulo. Esto funciona tanto con la importación de ES como con las declaraciones de requerimiento de nodo.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

@johncmunson Ese es un buen punto. Sin embargo, este ejemplo que mostró de burlarse de un módulo solo funciona si solo necesita ejecutar jest.mock _una vez_ y ninguno de los métodos simulados usa otra exportación de ese módulo.

Tome el ejemplo anterior ... He agregado bar para mostrar cómo quiero simular el módulo de manera diferente entre foo y bar .

export const message = (): string => {
  return 'Hello world';
}

export const foo = (): string => {
  return message();
}

export const bar = (): (() => string) => {
  return foo;
}

Usando jest.mock con jest.requireActual creo que sería algo como esto.

import * as mockTestModule from './hello';

jest.mock('./hello');
const actualTestModule = jest.requireActual('./hello');

describe('test hello', function () {
  afterAll(() => {
    jest.restoreAllMocks();
  });

  // first test doesn't depend on any other method of the module so no mocks
  it('should NOT mock message in foo', function () {
    const actual = actualTestModule.foo();

    expect(actual).toBe('Hello world');
  });

  // the second I want to mock message in foo
  it('should mock message in foo', function () {
    jest.spyOn(mockTestModule, 'message').mockReturnValue('my message');
    const actual = actualTestModule.foo();

    expect(actual).toBe('my message'); // fails
    expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
  });

  it('should mock foo in bar', function () {
    jest.spyOn(mockTestModule, 'foo').mockReturnValue('my message');
    const actual = actualTestModule.bar();

    expect(actual()).toBe('my message'); // fails
    expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
  });
});

Incluso intenté burlarme de ellos por separado con jest.doMock y aún así obtuve el mismo resultado.


Haga clic para ver el código

ts
importar * como testModule desde './hello';

describe ('prueba hola', función () {
afterAll (() => {
jest.restoreAllMocks ();
});

it('should NOT mock message in foo', function () {
  const actual = testModule.foo();

  expect(actual).toBe('Hello world');
});

it('should mock message in foo', function () {
  jest.doMock('./hello', () => {
    // Require the original module to not be mocked...
    const originalModule = jest.requireActual('./hello');

    return {
      ...originalModule,
      message: jest.fn().mockReturnValue('my message'),
    };
  });
  const actual = testModule.foo();

  expect(actual).toBe('my message'); // fails
  expect(testModule.message).toHaveBeenCalledTimes(1); // never called
});

it('should mock foo in bar', function () {
  jest.doMock('./hello', () => {
    // Require the original module to not be mocked...
    const originalModule = jest.requireActual('./hello');

    return {
      ...originalModule,
      foo: jest.fn().mockReturnValue('my message'),
    };
  });
  const actual = testModule.bar()();

  expect(actual).toBe('my message'); // fails
  expect(testModule.foo).toHaveBeenCalledTimes(1); // never called
});

});
''

El problema con este enfoque es que al requerir el módulo real, luego decir llamar a foo , aún se llama a la función message real y no al simulacro.

Desearía que fuera así de simple, pero por lo que veo, esto no ayuda para los ejemplos de este hilo. Si me falta algo aquí, hágamelo saber. Con mucho gusto admitiré la culpa.

Para cualquiera que se encuentre con esto en busca de una solución, lo siguiente parece funcionar para mí al exportar muchas const / functions en un archivo y al importarlas en un archivo que estoy probando

function mockFunctions() {
  const original = require.requireActual('../myModule');
  return {
    ...original, //Pass down all the exported objects
    test: jest.fn(() => {console.log('I didnt call the original')}),
    someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
  }
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
`

Lo siguiente funciona y es un poco más corto:

const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');

En TypeScript, solo import lugar de require :

import * as module from './module';

Esto tiene la ventaja de facilitar la restauración de las funciones originales y las simulaciones claras.

Para cualquiera que se encuentre con esto en busca de una solución, lo siguiente parece funcionar para mí al exportar muchas const / functions en un archivo y al importarlas en un archivo que estoy probando

function mockFunctions() {
  const original = require.requireActual('../myModule');
  return {
    ...original, //Pass down all the exported objects
    test: jest.fn(() => {console.log('I didnt call the original')}),
    someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
  }
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
`

Lo siguiente funciona y es un poco más corto:

const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');

En TypeScript, solo import lugar de require :

import * as module from './module';

Esto tiene la ventaja de facilitar la restauración de las funciones originales y las simulaciones claras.

Oh, sí, también este método no funciona si su objeto solo tiene getter definido. El mensaje de error podría ser el siguiente:

Test suite failed to run

    TypeError: Cannot set property useContent of #<Object> which has only a getter

Probablemente necesite usar jest.mock(..) para este caso. : bowing_man:

Mis simulacros funcionan usando lo siguiente:

import { unsubscribe } from "../lib/my-lib"
import { MyComponent } from "./index"

test("unsubscribe gets called", () => {
    const module = require("../lib/my-lib")
    jest.spyOn(
        module,
        "unsubscribe"
    ).mockImplementation(() => jest.fn())

    const { getByTestId } = render(() => <MyComponent  />)

    let button = getByTestId("trigger.some.action.button")

    fireEvent.press(button)

    expect(unsubscribe).toHaveBeenCalled()
})

No parece tan elegante y no se escala tan fácilmente, aunque funciona muy bien y se adapta a mis casos por ahora ... ¡pero si alguien puede sugerir cualquier otra sintaxis, sería genial! Esta es la única sintaxis que parece funcionar.

código del módulo es6:

export const funcA = () => {};
export const funcB = () => {
  funcA();
};

Después de transpilar a CommonJS:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.funcB = exports.funcA = void 0;

var funcA = function funcA() {};

exports.funcA = funcA; // You can mock or add a spy  on this `funcA`

var funcB = function funcB() {
  funcA();  // This is still original `funcA`
};

exports.funcB = funcB;

Hay muchas formas de solucionar esta situación.

  1. Necesita cambiar el código de esta manera, para que pueda usar el funcA burlado / espiado
function funcA() {}
exports.funcA = funcA;

function funcB() {
  exports.funcA(); // Now, this `exports.funcA` is added a spy or mocked. Keep the same reference to `funcA`
}
exports.funcB = funcB;

O,

export let funcA = () => {};
export const funcB = () => {
  exports.funcA();
};

resultados de la prueba unitaria:

 PASS  myModule.test.ts (9.225s)
  funcB
    ✓ should call funcA (3ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |     100 |      100 |     100 |     100 |                   
 myModule.ts |     100 |      100 |     100 |     100 |                   
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.967s
  1. use el paquete rewire para simular funcA
    ...

Además, debe echar un vistazo a estos documentos: https://nodejs.org/api/modules.html#modules_exports_shortcut , para ver qué hace exactamente require

La solución en esta publicación de stackoverflow funcionó para mí
https://stackoverflow.com/a/53402206/1217998

Básicamente, primero convierte todas las funciones que desea convertir a jest.fn

jest.mock('../../utils', () => {
  const actualUtils = jest.requireActual('../../utils');
  const originalImplementation = actualUtils.someFun;

  return {
    ...actualUtils,
    someFun: jest.fn()
      .mockImplementation(originalImplementation),
  };
});
const utils = require('../../utils');

Entonces puedes usarlo como de costumbre si quieres o burlarte de él así

jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);

Puede usar esto para obtener la implementación original

beforeEach(() => {
    jest.clearAllMocks();
  });

La solución en esta publicación de stackoverflow funcionó para mí
https://stackoverflow.com/a/53402206/1217998

Básicamente, primero convierte todas las funciones que desea convertir a jest.fn

jest.mock('../../utils', () => {
  const actualUtils = jest.requireActual('../../utils');
  const originalImplementation = actualUtils.someFun;

  return {
    ...actualUtils,
    someFun: jest.fn()
      .mockImplementation(originalImplementation),
  };
});
const utils = require('../../utils');

Entonces puedes usarlo como de costumbre si quieres o burlarte de él así

jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);

Puede usar esto para obtener la implementación original

beforeEach(() => {
    jest.clearAllMocks();
  });

¡Gracias!

Jest tiene un ejemplo muy simple y claro en su documentación de cómo simular parcialmente un módulo. Esto funciona tanto con la importación de ES como con las declaraciones de requerimiento de nodo.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename

No funciona cuando se llama a la función simulada desde dentro del módulo.

Además, descubrí que a veces puede ser útil simular la función de la forma en que no cambia la función original, sino que llama a la función con algunas variables personalizadas (adicionales):

jest.mock('./someModule', () => {
  const moduleMock = require.requireActual('./someModule');
  return {
    ...moduleMock,
    // will mock this function 
    someFunction: (args) =>
      moduleMock.someFunction({
        ...args,
        customArgument,
      }),
  };
});

En mi caso, necesitaba pasar la configuración para que funcione sin la cual usaría la predeterminada.

Esta es la única forma que encontré para hacer esto, así que si se te ocurren algunas ideas mejores o más fáciles, estaré encantado de escuchar :)

Esto no responde a la pregunta / problema de los OP, pero es una solución con algo de refactorización involucrada. Descubrí que separar mis funciones en diferentes archivos y luego burlarme de esas importaciones es lo más fácil de hacer.

// package.json
...
"scripts": {
    "test": "jest",

...
"devDependencies": {
    "@babel/preset-env": "^7.11.5",
    "jest": "^24.9.0",
...

`` js
// babel.config.js

module.exports = {
Preajustes: [
[
'@ babel / preset-env',
{
objetivos: {
nodo: 'actual',
},
},
],
],
};

```js
// module-utils.js

export const isBabaYaga = () => {
  return false
}

// module.js

import { isBabaYaga } from './module-utils'

export const isJohnWickBabaYaga = () => {
  return isBabaYaga()
}
// modules.test.js

import { isJohnWickBabaYaga } from './module';

jest.mock('./module-utils', () => ({
    isBabaYaga: jest.fn(() => true)
}))

test('John Wick is the Baba Yaga', () => {

    // when
    const isBabaYaga = isJohnWickBabaYaga()

    // then
    expect(isBabaYaga).toBeTruthy()
});
PASS  src/module.test.js
✓ John Wick is the Baba Yaga (4ms)

Recientemente me encontré con este problema. Ninguna de las soluciones propuestas me funciona porque no puedo cambiar el código. El babel-plugin-rewire tampoco me funciona. ¿Existe alguna otra solución para probar que una función fue llamada por otra función dentro del mismo módulo? Honestamente, esto parece que debería funcionar o que debería haber un complemento de babel que lo haga. ¡Cualquier ayuda será muy apreciada!

Recientemente me encontré con este problema. Ninguna de las soluciones propuestas me funciona porque no puedo cambiar el código. El babel-plugin-rewire tampoco me funciona. ¿Existe alguna otra solución para probar que una función fue llamada por otra función dentro del mismo módulo? Honestamente, esto parece que debería funcionar o que debería haber un complemento de babel que lo haga. ¡Cualquier ayuda será muy apreciada!

¿Ha revisado https://github.com/facebook/jest/issues/936#issuecomment -659597840? Hay una reproducción mínima que simula llamadas de funciones en el mismo archivo.

¿Fue útil esta página
0 / 5 - 0 calificaciones