
Cómo las Pruebas Unitarias Transforman tus Aplicaciones Angular
Tabla de Contenidos
Tabla de Contenidos
Las pruebas unitarias son una parte esencial del desarrollo de software, junto con las Pruebas End-to-End (E2E) y las Pruebas de Integración. Juega un papel clave en garantizar la calidad de nuestra aplicación. Para los desarrolladores de software, este puede ser uno de los aspectos más desafiantes de Angular. Implica simular profundamente cómo opera Angular, comprender el funcionamiento de la Inyección de Dependencias, los Ciclos de Vida de Angular y cómo simular (mockear) servicios. Inicialmente, entender y crear una buena prueba unitaria puede ser bastante difícil.
Pirámide de Pruebas

El objetivo de la pirámide de pruebas es optimizar la cantidad de pruebas rápidas y económicas (pruebas unitarias) mientras se reduce la frecuencia de pruebas más lentas y costosas (pruebas E2E). Este enfoque no solo mejora la eficiencia de las pruebas, sino que también facilita la identificación rápida de problemas. En esencia, la pirámide de pruebas sirve como un marco estratégico para asignar efectiva y eficientemente los recursos de prueba, asegurando así la calidad del software de manera escalable.
Pero, ¿Por qué son importantes las Pruebas Unitarias?
Encontrar errores temprano: Las pruebas unitarias te permiten detectar errores en tu código mientras lo creas, por lo que es más fácil corregirlos antes de que los usuarios se vean afectados.
Hacer mejor código: Escribir pruebas unitarias te hace pensar sobre cómo funciona tu código y ayuda a asegurar que esté bien hecho.
Refactorizar de forma segura: Las pruebas unitarias te dan confianza de que puedes cambiar tu código sin causar problemas.
Aumentar la cobertura de código: La cobertura de código es el porcentaje de código que ejecutan las pruebas unitarias. Una alta cobertura de código muestra que tu código está bien probado (Y ahí es donde radica la importancia de hacer una buena Prueba Unitaria).
¿Qué herramientas tienes para hacer pruebas unitarias en Angular?
Angular tiene herramientas integradas para pruebas unitarias. Las principales son:
Karma: Un ejecutor de pruebas que ejecuta pruebas unitarias en el navegador.
Jasmine: Un framework para escribir pruebas unitarias.
TestBed: Un módulo que te ayuda a configurar un entorno de pruebas para tu componente.
Jest: Un framework para escribir pruebas en el navegador y puede usarse en reemplazo de Karma.
¿Qué hace que una prueba sea una buena Prueba Unitaria?
Algunas prácticas recomendadas para realizar una Prueba Unitaria incluyen:
Arrange, Act, Assert (AAA)
Arrange: Esta es la configuración para que nuestra prueba tenga todo lo que necesita para funcionar.
Act: Ejecutar la acción
Assert: Verificar el resultado del Act.
Aislar el componente a probar
Para componentes que dependen de servicios para su comportamiento, usar "Test Doubles" (mocks, stubs, doubles).
Usar spies para verificar el comportamiento de métodos o funciones.
Siempre usar datos Mock. Esto hace que las pruebas sean predecibles.
Usar un buen nombre para la Prueba Unitaria.
Probar las interacciones esperadas: Haz clic en nuestros botones, no llames a los métodos directamente
it('should emit form value when the user clicks the button and form is valid', async () => {
const expectedEmittedQueryValue = 'test';
component.form.setValue({ query: expectedEmittedQueryValue });
component.form.markAsTouched();
// Spies
const submitSpy = jest.spyOn(component, 'submitForm');
const outputSpy = jest.spyOn(component.query, 'emit');
// Find the button
const button = de.nativeElement.querySelector(
'[data-testId="submit-button"]'
);
// Always check the null
expect(button).not.toBeNull();
// Simulate the click of the user at the button
button.click();
expect(component.form.valid).toBe(true);
expect(submitSpy).toHaveBeenCalled();
expect(outputSpy).toHaveBeenCalledWith(expectedEmittedQueryValue);
});
- Encuentra elementos con data-testId, no con clases CSS.
<span
data-testId="reset-icon"
nz-icon
class="ant-input-clear-icon"
nzTheme="fill"
nzType="close-circle"
(click)="resetForm()">
</span>
it('form should be reset when click clear icon', async () => {
// Add something to the form
component.form.setValue({ query: 'test' });
fixture.detectChanges();
await fixture.whenStable();
// Use the data-testId not the class name of the element
const clearIcon = de.nativeElement.querySelector(
'[data-testId="reset-icon"]'
);
// Check the null
expect(clearIcon).not.toBeNull();
// Act
jest.spyOn(component, 'resetForm'); // Spi the method
clearIcon.click(); // Simulate the click the element
// Assert
expect(component.resetForm).toHaveBeenCalled();
expect(component.form.controls['query'].value).toBeNull();
});
- Probar todos los caminos de código posibles (tanto verdadero como falso)
it('should show loading button when input is loading `true`', () => {
// arrange
const button = fixture.debugElement.query(
By.directive(NzButtonComponent);
expect(button).not.toBeNull();
// checking the false
fixture.componentRef.setInput('isLoading', false);
fixture.detectChanges();
// assert
expect(button.injector.get(NzButtonComponent).nzLoading).toBe(false);
// checking the true
fixture.componentRef.setInput('isLoading', true);
fixture.detectChanges();
// assert
expect(button.injector.get(NzButtonComponent).nzLoading).toBe(true);
});
- Mockear un servicio usado en un componente para verificar el mensaje mostrado en el componente
describe('UserComponent', () => {
let component: UserComponent;
let fixture: ComponentFixture<UserComponent>;
let userServiceMock: Partial<UserService>;
beforeEach(async () => {
// create a mock of the service
userServiceMock = {
isLoggedIn: jest.fn(),
};
await TestBed.configureTestingModule({
declarations: [UserComponent],
// Replace the UserService with the mock
providers: [{ provide: UserService, useValue: userServiceMock }],
}).compileComponents();
fixture = TestBed.createComponent(UserComponent);
component = fixture.componentInstance;
});
it('should show "Logged In" if the user is logged in', () => {
// manipulate the value of isLoggedIn to return true
userServiceMock.isLoggedIn.mockReturnValue(true);
fixture.detectChanges();
expect(component.message).toBe('Logged In');
});
it('should show "Not Logged In" if the user is not logged in', () => {
// manipulate the value of isLoggedIn to return false
userServiceMock.isLoggedIn.mockReturnValue(false);
fixture.detectChanges();
expect(component.message).toBe('Not Logged In');
});
});
Qué otras herramientas uso para Pruebas Unitarias
Spectator: Simplifica las pruebas de Angular eliminando tareas repetitivas, haciendo que las pruebas unitarias sean claras y eficientes. Permite probar componentes, directivas, servicios y más sin necesitar un conocimiento profundo de las APIs TestBed, ComponentFixture y DebugElement. Las características incluyen consulta simple del DOM, activación de eventos, soporte de pruebas de enrutamiento y HTTP, auto-mocking de proveedores y compatibilidad con Jest.
NgMock: Esta es una biblioteca para Angular que simplifica la creación de mocks en pruebas, permitiendo la simulación de componentes, directivas, pipes, servicios, módulos y tokens. Facilita la configuración de TestBed, reduce el código repetitivo en las pruebas y ofrece una interfaz simple para acceder a las declaraciones. Compatible con varias versiones de Angular, NgMocks soporta tanto Jasmine como Jest, ayudando a crear un entorno de pruebas más eficiente y menos tedioso.
Conclusión
En conclusión, las pruebas unitarias son cruciales para crear aplicaciones Angular robustas y confiables. Permiten a los desarrolladores detectar errores temprano, mejorar la calidad del código, asegurar una refactorización segura y lograr alta cobertura de código. Con las herramientas de Angular como Karma, Jasmine, TestBed y opcionalmente Jest, los desarrolladores pueden seguir las mejores prácticas como Arrange, Act, Assert (AAA), aislamiento de componentes, uso de test doubles y prueba de todos los caminos de código. Estas estrategias conducen a aplicaciones funcionales, mantenibles y escalables, mejorando la calidad del software y el proceso de desarrollo.

