Unit testing is crucial to ensure that your Angular application is robust, maintainable, and behaves as expected. Angular provides powerful tools to facilitate testing, including Jasmine (for writing tests), Karma (for running tests), and TestBed (for configuring test environments).
Here’s
how you can approach unit testing Angular applications.
🚀 Setting Up Unit Testing in Angular
When you generate a
new Angular project using ng new, Angular comes pre-configured with Jasmine
for testing and Karma as the test runner.
- Jasmine: A
behavior-driven testing framework for JavaScript.
- Karma: A test runner
that runs Jasmine tests in the browser.
- TestBed: Angular’s
primary API for configuring and initializing Angular components and
services in tests.
🧪 1. Unit Testing Services
Example: Testing a
Service
task.service.ts
import { Injectable }
from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TaskService {
constructor() {}
getTasks() {
return ['Task 1', 'Task 2', 'Task
3'];
}
}
task.service.spec.ts (Test File)
import { TestBed }
from '@angular/core/testing';
import { TaskService } from './task.service';
describe('TaskService',
() => {
let service: TaskService;
beforeEach(() => {
TestBed.configureTestingModule({});
service =
TestBed.inject(TaskService);
});
it('should be
created', () => {
expect(service).toBeTruthy();
});
it('should return
tasks', () => {
const tasks = service.getTasks();
expect(tasks.length).toBe(3);
expect(tasks).toEqual(['Task 1',
'Task 2', 'Task 3']);
});
});
Breakdown:
- TestBed:
Angular's testing environment to configure and initialize services.
- beforeEach:
Setup function to initialize the service before each test.
- expect:
Jasmine’s assertion library used to test expectations.
🧪 2. Unit Testing Components
Example: Testing a
Component
task-list.component.ts
import { Component }
from '@angular/core';
import { TaskService } from './task.service';
@Component({
selector: 'app-task-list',
template: '<ul><li
*ngFor="let task of tasks">{{task}}</li></ul>'
})
export class TaskListComponent {
tasks: string[] = [];
constructor(private
taskService: TaskService) {}
ngOnInit() {
this.tasks =
this.taskService.getTasks();
}
}
task-list.component.spec.ts (Test
File)
import {
ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskListComponent } from './task-list.component';
import { TaskService } from './task.service';
import { of } from 'rxjs';
class MockTaskService
{
getTasks() {
return ['Task 1', 'Task 2', 'Task
3'];
}
}
describe('TaskListComponent',
() => {
let component: TaskListComponent;
let fixture:
ComponentFixture<TaskListComponent>;
let mockTaskService: MockTaskService;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TaskListComponent],
providers: [{ provide: TaskService,
useClass: MockTaskService }]
});
fixture =
TestBed.createComponent(TaskListComponent);
component =
fixture.componentInstance;
mockTaskService =
TestBed.inject(TaskService);
});
it('should create',
() => {
expect(component).toBeTruthy();
});
it('should load tasks
on init', () => {
fixture.detectChanges();
expect(component.tasks.length).toBe(3);
expect(component.tasks).toEqual(['Task 1', 'Task 2', 'Task 3']);
});
});
Key Concepts:
- ComponentFixture: Used to test
and interact with the component’s DOM.
- Mocking
services:
Using a mock service to isolate the component's behavior.
- detectChanges: Tells Angular
to run change detection so that the component reflects its updated state.
🧪 3. Testing Forms
Example: Testing a
Form Component
user-form.component.ts
import { Component }
from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-user-form',
template: `<form
[formGroup]="userForm" (ngSubmit)="onSubmit()">
<input
formControlName="name" />
<button
type="submit"
[disabled]="userForm.invalid">Submit</button>
</form>`
})
export class UserFormComponent {
userForm: FormGroup;
constructor(private
fb: FormBuilder) {
this.userForm = this.fb.group({
name: ['', Validators.required]
});
}
onSubmit() {
console.log(this.userForm.value);
}
}
user-form.component.spec.ts (Test
File)
import {
ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { UserFormComponent } from './user-form.component';
describe('UserFormComponent',
() => {
let component: UserFormComponent;
let fixture:
ComponentFixture<UserFormComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule],
declarations: [UserFormComponent]
});
fixture =
TestBed.createComponent(UserFormComponent);
component =
fixture.componentInstance;
});
it('should create the
form with one control', () => {
expect(component.userForm.contains('name')).toBeTrue();
});
it('should make the
form invalid if the name is empty', () => {
const nameControl =
component.userForm.get('name');
nameControl?.setValue('');
expect(component.userForm.invalid).toBeTrue();
});
it('should submit the
form', () => {
const nameControl =
component.userForm.get('name');
nameControl?.setValue('John Doe');
fixture.detectChanges();
component.onSubmit();
expect(component.userForm.valid).toBeTrue();
});
});
Key Concepts:
- ReactiveFormsModule: Importing this
module to use reactive forms.
- Form
validation:
Checking if the form is valid or invalid.
- Form
control value:
Testing form control values and their validations.
🧪 4. Testing HTTP Requests
To test HTTP
requests, you can use HttpClientTestingModule and HttpTestingController.
Example: Testing HTTP Service
import { TestBed }
from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from
'@angular/common/http/testing';
import { MyService } from './my-service';
describe('MyService',
() => {
let service: MyService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [MyService]
});
service = TestBed.inject(MyService);
httpMock =
TestBed.inject(HttpTestingController);
});
it('should fetch
data', () => {
const mockData = [{ id: 1, name:
'Item 1' }];
service.getData().subscribe((data)
=> {
expect(data).toEqual(mockData);
});
const req =
httpMock.expectOne('https://api.example.com/data');
expect(req.request.method).toBe('GET');
req.flush(mockData);
});
afterEach(() => {
httpMock.verify();
});
});
🧠 Summary
- Unit
Testing Services:
Use TestBed to configure and inject services, test their methods.
- Unit
Testing Components: Test components with ComponentFixture, mock
dependencies, and test DOM and behaviors.
- Form
Testing:
Use ReactiveFormsModule for reactive form testing and validate form
controls.
No comments:
Post a Comment