subtractMocks en jest
- Jest.fn Nos permite simular un método o función
- jest.spyOn: Simula un método o función que le indiques y además permite restaurar la implementación original
- Jest.mock: Simula un módulo entero
jest.fn
//math.ts
export function add(a: number, b: number) {
return a + b;
}
export function subtract(a: number, b: number) {
return a - b;
}
Nuestro SUT (subject under test, lo que estamos testeando)
//Calculator.ts
import * as arithmetic from './math';
export function doAdd(a: number, b: number) {
return arithmetic.add(a, b);
}
export function doSubtract(a: number, b: number) {
return arithmetic.subtract(a, b);
}
Ahora vamos al test
import * as arithmetic from "../../src/core/math";
import * as calculator from "../../src/core/Calculator";
describe('calculator', () => {
// (1) Hacemos un MOCK del método add
(arithmetic as any).add = jest.fn();
// (2) Hacemos un MOCK del método subtract, pero manteniendo la implementación inicial
(arithmetic as any).subtract = jest.fn(math.subtract);
it("calls add", () => {
calculator.doAdd(1, 2);
expect(arithmetic.add).toHaveBeenCalledWith(1, 2);
});
it("calls subtract", () => {
calculator.doSubtract(1, 2);
expect(arithmetic.subtract).toHaveBeenCalledWith(1, 2);
expect(result).toBe(-1)
});
});
jest.mock
Al usar jest.mock(<path>)
crea un jest.fn()
para cada uno de los métodos del modulo. Esto no siempre es bueno, por que nos dificulta el acceso a la implementación original.
import * as math from "../../src/core/math";
import * as calculator from "../../src/core/calculator";
// Creamos el mock de TODO el modulo
jest.mock("../../src/core/math");
describe('calculator', () => {
it("calls add", () => {
calculator.doAdd(1, 2);
expect(arithmetic.add).toHaveBeenCalledWith(1, 2);
});
it("calls subtract", () => {
calculator.doSubtract(1, 2);
expect(arithmetic.subtract).toHaveBeenCalledWith(1, 2);
});
});
jest.spyOn
Permite crear spy y stubs
describe('calculator', () => {
it("calls add", () => {
// Creamos el spy del método que nos interesa, y mantenemos la implementación original
const mockedAdd = jest.spyOn(arithmetic, 'add');
expect(calculator.doAdd(1, 2)).toBe(3);
// Le preguntamos al Spy si el comportamiento fue el esperado
expect(addMock).toHaveBeenCalledWith(1, 2);
});
it("calls subtract", () => {
// Creamos el spy del método que nos interesa, y mantenemos la implementación original
const mockedSubtract = jest.spyOn(arithmetic, 'subtract');
// También podemo hacer un STUB del spy y "forzar" una respuesta, suplantando la implementación original
mockedSubtract.mockImplementation(() => 10);
expect(addMock).toHaveBeenCalledWith(1, 2);
});
});
No abuses de los mocks, al definir un mock, le estamos diciendo al TEST como funciona el código que esta probando, y esto hará que nuestro test este "acoplado" al código que prueba y lo ideal es que el código que se esta probando (SUT) sea una caja negra (en lo posible).
Por ejemplo, en el caso de arriba, al suplantar subtract
estamos acoplados a ese método, si este método cambia su comportamiento en el futuro quizás este test se rompa, de ahí el termino "test frágiles".