Entendiendo jest.spyOn() con console.log
Tags: jest, testing, spyon, spanish
Published: 2023-12-04 11:10:07



Explicación básica de como funciona el método spyOn de Jest.

Cuando se usa `jest.spyOn` para espiar la función `console.log`, Jest crea un espía que rastrea las llamadas a esa función en lugar de ejecutar realmente el código dentro de ella. Esto permite realizar verificaciones sobre cómo se llamó a `console.log` sin imprimir nada en la consola real durante las pruebas.

Vamos a usar de ejemplo una función que llama al método `log` del objeto `console`  (el famoso console.log de toda la vida) de JavaScript.

```js
function sayHello() {
  console.log('HELLO WORLD');
}

describe('Pruebas para la función sayHello', () => {

  it('debería imprimir HELLO WORLD en la consola', () => {
    const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});

    sayHello();

    expect(consoleSpy).toHaveBeenCalledWith('HELLO WORLD');
    expect(consoleSpy).toHaveBeenCalledTimes(1);

    consoleSpy.mockRestore();
  });
});
```

1. **Definir la función a testear:** Se define la función sayHello que a su vez llama a la función console.log con el argumento 'HELLO WORLD'.
	```js
	function sayHello() {
	  console.log('HELLO WORLD');  
	 }
	```
	
2. **Creación del espía (spyOn):** Al utilizar jest.spyOn se está creando un espía para la función que se está rastreando, en este caso, console.log. Aquí se le está asignando ese espía a la variable consoleSpy.
	```js
	const consoleSpy = jest.spyOn(console,  'log').mockImplementation(() => {});
	```
	La función `jest.spyOn` configura un espía para rastrear/observar llamadas a una función especificada, en este caso console.log() (del objeto `console` el método `log`). Hasta que dicha función se ejecute, el espía no registra ninguna llamada. Una vez que la función es ejecutada, el espía "observa" esa llamada y permite realizar verificaciones sobre cómo se llamó a la función.
Para evitar que se imprima en la consola durante las pruebas, se puede usar jest.spyOn junto con mockImplementation().
En este caso, mockImplementation(() => {}) reemplaza la implementación de console.log con una función vacía, evitando así la impresión en la consola durante la prueba.
	
3. **Llamada a la función a espiar (`sayHello`):** Cuando se llama a `sayHello`, se ejecuta el código dentro de `sayHello`, incluida la llamada a `console.log`. En este momento, el espía `consoleSpy` registra esta llamada sin imprimir realmente en la consola real.
	```js
	sayHello();
	```

4. **Verificación de llamada (`expect`):** Después de llamar a `sayHello`, se verifica si la función original `console.log` fue llamada con los argumentos esperados. En este caso, estás verificando si se llamó con `'HELLO WORLD'`.
	 ```js
	 expect(consoleSpy).toHaveBeenCalledWith('HELLO WORLD');
	 ```
Mas información sobre los matchers [jestjs.io](https://jestjs.io/es-ES/docs/expect#tohavebeencalledwitharg1-arg2-)
	 
5. **Verificación de numero de llamadas:** El espía también guarda información de cuantas veces ha sido llamada la función a espiar.
	```js
	expect(consoleSpy).toHaveBeenCalledTimes(1);
	```

6. **Restauración del estado original (`mockRestore`):** Después de realizar la verificación, es una buena práctica restaurar la función original al estado que tenía antes de la prueba.
	```js
	consoleSpy.mockRestore();
	```
La función consoleSpy.mockRestore(); se utiliza para restaurar la función original que fue espiada por jest.spyOn. Cuando se espía una función con jest.spyOn(console, 'log'), Jest reemplaza la función original con el espía.

Al ejecutar consoleSpy.mockRestore();, se está deshaciendo esa sustitución y devolviendo la función original al estado anterior al espionaje. Después de llamar a mockRestore, la función console.log vuelve a ser la función original de console, y cualquier futura llamada a console.log se comportará como lo haría normalmente.

Este paso es especialmente importante al finalizar una prueba porque asegura que el comportamiento de la aplicación vuelve a ser el original y evita cualquier impacto no deseado en otras partes del código que podrían depender del comportamiento real de conosle.log.

## Otro ejemplo
Espionaje del método sum del objecto calculator:
``` js
// Objeto 'calculator' con método 'sum'
const calculator = {
  sum: (a, b) => {
    return a + b;
  }
};

// Test utilizando jest.spyOn para espiar el método 'sum' en el objeto
test('el método sum debería llamarse con los argumentos correctos', () => {
  // Espiando el método 'sum'
  const sumSpy = jest.spyOn(calculator, 'sum');

  // Llamando a una función que utiliza 'sum'
  const result = calculator.sum(3, 4);

  // Verificando que 'sum' fue llamado con los argumentos correctos
  expect(sumSpy).toHaveBeenCalledWith(3, 4);

  // Verificando que la función devolvió el resultado esperado
  expect(result).toBe(7);

  // Restaurando la función original
  sumSpy.mockRestore();
});

```

## Informacion adicional
  
El objeto espía creado con `jest.spyOn` y tiene varias propiedades útiles. Algunas de las más comunes y útiles incluyen:

1.  **`calls`**: Un array que contiene información sobre cada llamada a la función espiada. Cada elemento del array es un objeto que representa una llamada y puede contener información sobre los argumentos, el contexto (`this`), entre otros detalles.
    
2.  **`callCount`**: Un número que representa cuántas veces se ha llamado la función.
    
3.  **`toHaveBeenCalledWith`**: Método que te permite verificar si la función fue llamada con argumentos específicos.
    
4.  **`lastCalledWith`**: Método que te permite verificar los argumentos de la última llamada a la función.
    
5.  **`mockRestore`**: Método que restaura la función original al estado anterior al espionaje.