Perfect choice 🙌 — Angular testing can feel intimidating if presented only in theory. A practical, hands-on testing chapter will give your readers confidence by showing them how to actually write and run tests for components, services, and full applications.
Here’s a book-style draft for Chapter 18: Testing Angular Applications (Practical Guide).
Chapter 18: Testing Angular Applications
Writing tests is one of the most valuable investments in software development. Angular provides a rich testing ecosystem out of the box, using Jasmine (test framework) and Karma (test runner). Many teams also use Jest and Cypress for modern workflows.
In this chapter, we’ll focus on practical setups:
- Unit testing components and services.
- Testing HTTP requests.
- Writing end-to-end (E2E) tests.
- Tips for faster, maintainable testing.
18.1 Angular Testing Setup
When you generate a project with the Angular CLI, testing support is already included:
- Jasmine for writing specs.
- Karma for running tests in a browser.
- Test files use the
.spec.tssuffix.
To run tests:
ng test
18.2 Unit Testing a Service
Let’s start with a simple service.
Service:
@Injectable({ providedIn: 'root' })
export class CalculatorService {
add(a: number, b: number) { return a + b; }
subtract(a: number, b: number) { return a - b; }
}
Test:
import { TestBed } from '@angular/core/testing';
import { CalculatorService } from './calculator.service';
describe('CalculatorService', () => {
let service: CalculatorService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CalculatorService);
});
it('should add numbers', () => {
expect(service.add(2, 3)).toBe(5);
});
it('should subtract numbers', () => {
expect(service.subtract(5, 2)).toBe(3);
});
});
18.3 Unit Testing a Component
Component:
@Component({
selector: 'app-greeter',
standalone: true,
template: `<p>Hello {{ name }}</p>`
})
export class GreeterComponent {
@Input() name = 'Angular';
}
Test:
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GreeterComponent } from './greeter.component';
describe('GreeterComponent', () => {
let fixture: ComponentFixture<GreeterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [GreeterComponent]
}).compileComponents();
fixture = TestBed.createComponent(GreeterComponent);
});
it('should render default name', () => {
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('Hello Angular');
});
it('should render input name', () => {
fixture.componentInstance.name = 'World';
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('Hello World');
});
});
18.4 Testing Services with HTTP
Angular provides the HttpTestingController to mock HTTP requests.
Service:
@Injectable({ providedIn: 'root' })
export class UserService {
constructor(private http: HttpClient) {}
getUsers() { return this.http.get<any[]>('/api/users'); }
}
Test:
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule]
});
service = TestBed.inject(UserService);
httpMock = TestBed.inject(HttpTestingController);
});
it('should fetch users', () => {
const mockUsers = [{ name: 'Alice' }, { name: 'Bob' }];
service.getUsers().subscribe(users => {
expect(users).toEqual(mockUsers);
});
const req = httpMock.expectOne('/api/users');
expect(req.request.method).toBe('GET');
req.flush(mockUsers);
httpMock.verify();
});
});
18.5 End-to-End Testing with Cypress
Angular CLI used to ship with Protractor, but most teams now use Cypress for E2E.
Install Cypress:
npm install cypress --save-dev
Add a test (cypress/e2e/app.cy.ts):
describe('My First Test', () => {
it('visits the app root', () => {
cy.visit('http://localhost:4200');
cy.contains('Hello Angular');
});
});
Run Cypress:
npx cypress open
This opens the Cypress test runner with a browser simulation.
18.6 Tips for Better Angular Tests
- Use
TestBedfor components and services to get DI and Angular environment. - Keep unit tests small — one assertion per test is best.
- Mock external APIs (use
HttpTestingController, or libraries likejest-mock). - Use
async/awaitin tests for better readability with asynchronous code. - Prefer Jest over Karma if you want faster, modern testing (drop-in replacement for Jasmine).
- Test behavior, not implementation details.
18.7 Summary
- Angular testing is built-in with Jasmine + Karma.
- Use
TestBedfor unit testing components and services. - Mock HTTP with
HttpTestingController. - For end-to-end tests, prefer Cypress over Protractor.
- Keep tests small, focused, and meaningful.