Testing is crucial for building reliable Angular applications. Learn unit, integration, and end-to-end testing.
Setup
`bash ng test # Unit tests ng e2e # E2E tests `
TestBed Setup
typescript
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [UserService]
});
service = TestBed.inject(UserService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});Component Testing
typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
describe('UserListComponent', () => {
let component: UserListComponent;
let fixture: ComponentFixture<UserListComponent>;
let mockUserService: jasmine.SpyObj<UserService>;
beforeEach(async () => {
mockUserService = jasmine.createSpyObj('UserService', ['getUsers']);
mockUserService.getUsers.and.returnValue(of([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]));
await TestBed.configureTestingModule({
declarations: [UserListComponent],
providers: [
{ provide: UserService, useValue: mockUserService }
]
}).compileComponents();
fixture = TestBed.createComponent(UserListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display users', () => {
const elements = fixture.nativeElement.querySelectorAll('.user');
expect(elements.length).toBe(2);
expect(elements[0].textContent).toContain('John');
});
it('should call getUsers on init', () => {
expect(mockUserService.getUsers).toHaveBeenCalled();
});
});Testing with Inputs and Outputs
typescript
describe('UserCardComponent', () => {
let component: UserCardComponent;
let fixture: ComponentFixture<UserCardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UserCardComponent]
}).compileComponents();
fixture = TestBed.createComponent(UserCardComponent);
component = fixture.componentInstance;
});
it('should accept user input', () => {
component.user = { id: 1, name: 'John' };
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('John');
});
it('should emit delete event', () => {
spyOn(component.delete, 'emit');
component.onDelete(1);
expect(component.delete.emit).toHaveBeenCalledWith(1);
});
});Service Testing with HttpClient
typescript
import {
HttpClientTestingModule,
HttpTestingController
} from '@angular/common/http/testing';
describe('UserService with Http', () => {
let service: UserService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [UserService]
});
service = TestBed.inject(UserService);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
it('should fetch users', () => {
const mockUsers = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
];
service.getUsers().subscribe(users => {
expect(users).toEqual(mockUsers);
});
const req = httpMock.expectOne('/api/users');
expect(req.request.method).toBe('GET');
req.flush(mockUsers);
});
it('should handle errors', () => {
service.getUsers().subscribe({
next: () => fail('Should have failed'),
error: error => expect(error.message).toContain('404')
});
const req = httpMock.expectOne('/api/users');
req.flush('Error', { status: 404, statusText: 'Not Found' });
});
});Testing Directives
typescript
describe('HighlightDirective', () => {
it('should highlight element', () => {
const fixture = TestBed.createComponent(TestComponent);
const directiveEl = fixture.nativeElement.querySelector('.highlight');
directiveEl.dispatchEvent(new Event('mouseenter'));
fixture.detectChanges();
expect(directiveEl.style.backgroundColor).toBe('yellow');
});
});Testing Pipes
typescript
describe('TruncatePipe', () => {
let pipe: TruncatePipe;
beforeEach(() => {
pipe = new TruncatePipe();
});
it('should truncate long text', () => {
const result = pipe.transform('Hello World', 5);
expect(result).toBe('Hello...');
});
it('should return short text unchanged', () => {
const result = pipe.transform('Hi', 5);
expect(result).toBe('Hi');
});
});Testing Routing
typescript
import { RouterTestingModule } from '@angular/router/testing';
describe('NavigationComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [NavigationComponent],
imports: [RouterTestingModule]
}).compileComponents();
});
it('should navigate to home', () => {
const fixture = TestBed.createComponent(NavigationComponent);
const router = TestBed.inject(Router);
spyOn(router, 'navigateByUrl');
fixture.nativeElement.querySelector('[routerLink="/"]').click();
fixture.detectChanges();
expect(router.navigateByUrl).toHaveBeenCalledWith('/');
});
});Mocking Services
typescript
class MockUserService {
getUsers() {
return of([
{ id: 1, name: 'John' }
]);
}
}
describe('With Mock', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UserListComponent],
providers: [
{ provide: UserService, useClass: MockUserService }
]
}).compileComponents();
});
});Integration Testing
typescript
describe('UserForm Integration', () => {
let component: UserFormComponent;
let fixture: ComponentFixture<UserFormComponent>;
let userService: jasmine.SpyObj<UserService>;
beforeEach(async () => {
userService = jasmine.createSpyObj('UserService', ['createUser']);
await TestBed.configureTestingModule({
declarations: [UserFormComponent],
providers: [
{ provide: UserService, useValue: userService }
]
}).compileComponents();
fixture = TestBed.createComponent(UserFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should submit form', () => {
userService.createUser.and.returnValue(of({ id: 1, name: 'John' }));
component.form.setValue({ name: 'John', email: 'john@example.com' });
component.onSubmit();
expect(userService.createUser).toHaveBeenCalledWith({
name: 'John',
email: 'john@example.com'
});
});
});Best Practices
- 1Test user behavior, not implementation
- 2Use meaningful test names
- 3Keep tests independent
- 4Mock external dependencies
- 5Test edge cases and error scenarios
Angular testing ensures your application is reliable and maintainable.