Los spies (espías) en Jasmine permiten observar y modificar el comportamiento de funciones en pruebas sin necesidad de modificar el código original. Esto es útil para pruebas en las que quieres simular el comportamiento de ciertas dependencias o funciones.
Ejemplo avanzado de un Spy en TypeScript:
typescript class Calculadora { sumar(a: number, b: number): number { return a + b; } }
describe('Pruebas avanzadas con spies', () => { let calculadora: Calculadora;
beforeEach(() => { calculadora = new Calculadora(); });
// Espiamos el método 'sumar' de la clase Calculadora it('debería espiar el método sumar y modificar su comportamiento', () => { spyOn(calculadora, 'sumar').and.returnValue(10); // Simulamos que siempre devuelve 10
const resultado = calculadora.sumar(3, 4);
expect(resultado).toBe(10); // Verificamos que el resultado sea 10, no importa la entrada
expect(calculadora.sumar).toHaveBeenCalledWith(3, 4); // Verificamos que f
ue llamado con los argumentos correctos
});
});
🛑 5.2. Manejo de errores y excepciones en pruebas
Jasmine proporciona el matcher toThrowError() para comprobar que una función lanza una excepción. Puedes usarlo para asegurarte de que tu código maneje correctamente las excepciones esperadas.
Ejemplo de manejo de errores:
class Calculadora { dividir(a: number, b: number): number { if (b === 0) { throw new Error('División por cero'); } return a / b; } }
describe('Pruebas de manejo de excepciones', () => { let calculadora: Calculadora;
beforeEach(() => { calculadora = new Calculadora(); });
// Verificamos que se lanza una excepción al dividir por cero it('debería lanzar un error al dividir por cero', () => { expect(() => calculadora.dividir(4, 0)).toThrowError('División por cero'); }); });
🧩 5.3. Pruebas parametrizadas con jasmine-data-provider
Las pruebas parametrizadas permiten ejecutar el mismo conjunto de pruebas con diferentes entradas, lo que es útil para reducir código repetitivo.
Ejemplo de pruebas parametrizadas:
function dataProvider() {
return [
{ a: 1, b: 2, resultado: 3 },
{ a: 5, b: 5, resultado: 10 },
{ a: 10, b: 20, resultado: 30 }
];
}
describe('Pruebas parametrizadas', () => {
dataProvider().forEach(caso => {
it(`debería sumar ${caso.a} y ${caso.b} correctamente`, () => {
const resultado = caso.a + caso.b;
expect(resultado).toBe(caso.resultado); // Verificamos que la suma es correcta para cada caso
});
});
});
⏳ 5.4. Pruebas asíncronas en Jasmine
Para manejar pruebas asíncronas en Jasmine, puedes usar async() y fakeAsync() con tick(). Estas herramientas permiten simular el paso del tiempo y controlar la ejecución de promesas o temporizadores.
Ejemplo de pruebas con fakeAsync() y tick():
import { fakeAsync, tick } from '@angular/core/testing';
describe('Pruebas asíncronas', () =>
{
it('debería manejar correctamente el temporizador con fakeAsync', fakeAsync(() => {
let valor = false;
setTimeout(() => {
valor = true;
}, 1000);
tick(1000); // Simulamos el paso de 1 segundo
expect(valor).toBe(true); // Verificamos que el valor cambió a true
}));
});
🕒 5.5. Faking timers en Jasmine con jasmine.clock()
Jasmine también permite controlar los temporizadores (como setTimeout o setInterval) utilizando jasmine.clock(). Esto es útil para simular el paso del tiempo sin tener que esperar realmente.
Ejemplo usando jasmine.clock() para simular temporizadores:
describe('Pruebas con temporizadores', () => {
beforeEach(() => {
jasmine.clock().install(); // Instalamos el reloj falso
});
afterEach(() => {
jasmine.clock().uninstall(); // Desinstalamos el reloj falso después de cada prueba
});
it('debería manejar correctamente setTimeout', () => {
let valor = false;
setTimeout(() => {
valor = true;
}, 1000);
jasmine.clock().tick(1000); // Simulamos el paso de 1 segundo
expect(valor).toBe(true); // Verificamos que el valor cambió a true
});
});
📝 5.6. Uso de jasmine.createSpyObj() para objetos simulados
jasmine.createSpyObj() permite crear objetos simulados con múltiples métodos espías de manera sencilla. Esto es útil para probar servicios o componentes que dependen de múltiples métodos en un objeto o servicio. Ejemplo de createSpyObj:
class Calculadora { sumar(a: number, b: number): number { return a + b; } restar(a: number, b: number): number { return a - b; } }
describe('Uso de createSpyObj en Jasmine', () => { let calculadoraSpy: jasmine.SpyObj<Calculadora>;
beforeEach(() => { // Creamos un objeto espía con los métodos 'sumar' y 'restar' calculadoraSpy = jasmine.createSpyObj('Calculadora', ['sumar', 'restar']); calculadoraSpy.sumar.and.returnValue(10); // Simulamos el valor retornado por 'sumar' calculadoraSpy.restar.and.returnValue(5); // Simulamos el valor retornado por 'restar' });
it('debería usar el spy para el método sumar', () => { const resultado = calculadoraSpy.sumar(3, 4); expect(resultado).toBe(10); // Verificamos que devuelve el valor simulado expect(calculadoraSpy.sumar).toHaveBeenCalledWith(3, 4); // Verificamos que fue llamado con los argumentos correctos });
it('debería usar el spy para el método restar', () => { const resultado = calculadoraSpy.restar(10, 5); expect(resultado).toBe(5); // Verificamos que devuelve el valor simulado expect(calculadoraSpy.restar).toHaveBeenCalledWith(10, 5); // Verificamos que fue llamado con los argumentos correctos }); }); ``
🔍 5.7. Uso de .withContext() para mejorar los mensajes de error
El método .withContext() en Jasmine permite agregar un contexto adicional a los errores en las expectativas, proporcionando mensajes más claros cuando las pruebas fallan. Esto es especialmente útil en pruebas con múltiples expectativas o cuando es difícil entender qué parte falló.
Ejemplo con .withContext():
describe('Pruebas con .withContext()', () => {
it('debería sumar correctamente dos números con contexto adicional', () => {
const resultado = 2 + 3;
// Agregamos un mensaje adicional si la prueba falla
expect(resultado).withContext('Suma de 2 + 3 debería ser 5').toBe(5);
});
it('debería fallar con un contexto adicional', () => {
const resultado = 2 + 2;
// Aquí el contexto ayuda a entender por qué falló la prueba
expect(resultado).withContext('Suma de 2 + 2 debería ser 5').toBe(5);
});
});