How to mock HttpClient in Angular tests
Testing components and services that make HTTP requests requires mocking to avoid actual API calls and ensure tests run fast and reliably.
With over 12 years of Angular development experience since 2014 and as the creator of CoreUI, I’ve written extensive test suites for HTTP-based services.
Angular provides HttpClientTestingModule and HttpTestingController specifically for mocking HTTP requests in tests.
This approach allows you to verify requests are made correctly and control response data for different test scenarios.
Use HttpClientTestingModule and HttpTestingController to mock HTTP requests and verify API calls.
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],
providers: [UserService]
})
service = TestBed.inject(UserService)
httpMock = TestBed.inject(HttpTestingController)
})
afterEach(() => {
httpMock.verify() // Ensure no outstanding requests
})
it('should fetch users from API', () => {
const mockUsers = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]
service.getUsers().subscribe(users => {
expect(users.length).toBe(2)
expect(users).toEqual(mockUsers)
})
const req = httpMock.expectOne('https://api.example.com/users')
expect(req.request.method).toBe('GET')
req.flush(mockUsers)
})
it('should send POST request with data', () => {
const newUser = { name: 'Bob', email: '[email protected]' }
service.createUser(newUser).subscribe(user => {
expect(user.id).toBe(3)
})
const req = httpMock.expectOne('https://api.example.com/users')
expect(req.request.method).toBe('POST')
expect(req.request.body).toEqual(newUser)
req.flush({ id: 3, ...newUser })
})
it('should handle HTTP errors', () => {
service.getUsers().subscribe(
() => fail('should have failed'),
(error) => {
expect(error.status).toBe(404)
expect(error.error).toBe('Not found')
}
)
const req = httpMock.expectOne('https://api.example.com/users')
req.flush('Not found', { status: 404, statusText: 'Not Found' })
})
it('should include authorization header', () => {
service.getUserById(1).subscribe()
const req = httpMock.expectOne('https://api.example.com/users/1')
expect(req.request.headers.get('Authorization')).toBe('Bearer token123')
req.flush({ id: 1, name: 'John' })
})
it('should handle multiple requests', () => {
service.getUsers().subscribe()
service.getUserById(1).subscribe()
const requests = httpMock.match(req => req.url.includes('users'))
expect(requests.length).toBe(2)
requests.forEach(req => req.flush([]))
})
})
Best Practice Note
Always call httpMock.verify() in afterEach to ensure no unexpected HTTP requests were made. Use expectOne() for single requests and match() for multiple requests. The flush() method simulates the server response with mock data. Test both success and error scenarios including different HTTP status codes. For services with interceptors, the interceptors will run in tests unless you explicitly skip them. This is the testing strategy we use in CoreUI for Angular—comprehensive HTTP mocking to ensure services handle all API scenarios correctly without making actual network calls.



