Angular's HTTP testing backend lets a test control requests without sending real network traffic. You can assert the requested URL and method, then provide a success body or a deliberate failure. This proves client behavior at a mocked transport boundary.

These Angular interview questions use a small public list request. The tests do not claim that a deployed API, authentication system or real browser network is working.

How can a test control the response?

import { TestBed } from '@angular/core/testing';
import { HttpClient, provideHttpClient } from '@angular/common/http';
import {
  HttpTestingController, provideHttpClientTesting
} from '@angular/common/http/testing';

describe('article request', () => {
  let http: HttpClient;
  let backend: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [provideHttpClient(), provideHttpClientTesting()]
    });
    http = TestBed.inject(HttpClient);
    backend = TestBed.inject(HttpTestingController);
  });

  afterEach(() => backend.verify());

  it('reads a successful list', () => {
    let titles: string[] = [];
    http.get<string[]>('/api/articles').subscribe(value => titles = value);
    const request = backend.expectOne('/api/articles');
    expect(request.request.method).toBe('GET');
    request.flush(['Arrays']);
    expect(titles).toEqual(['Arrays']);
  });

  it('observes a server failure', () => {
    let status = 0;
    http.get('/api/articles').subscribe({
      error: error => status = error.status
    });
    backend.expectOne('/api/articles').flush(
      { message: 'Unavailable' }, { status: 503, statusText: 'Unavailable' });
    expect(status).toBe(503);
  });
});

This uses the familiar describe/it/expect testing interface. Use the project's configured test runner and compatible Angular packages. Providing the testing backend after the normal HTTP provider ensures the test overrides the transport in this explicit setup.

Why does subscription matter?

HttpClient observables initiate work when subscribed. Creating an observable alone is not the same as issuing the request. If expectOne finds no request, check whether the application actually subscribed and whether another branch prevented the call.

Multiple subscriptions can also create multiple requests. Decide whether the real service should share a result or make independent requests, then test that behavior. Do not assume an observable is automatically a cache.

What does verify add?

It catches unexpected outstanding requests after the test. Without that check, a test can pass its main assertion while leaving another unhandled request. Use expectNone when a particular action must not trigger a request at all.

Is a 503 the same as a network error?

No. A server error has an HTTP response status. A transport failure has a different failure path. Test each path the application handles, using the testing backend's supported error API for network failures.

Likewise, malformed success data is not necessarily an HTTP failure. TypeScript's generic type argument does not validate incoming JSON at runtime. Add runtime validation if the application's trust boundary requires it.

What should a component test add?

  • A loading indicator appears while the request is pending.
  • Success displays the expected public data.
  • Failure shows a useful recovery action.
  • Retry issues the intended new request.
  • Old or canceled requests do not overwrite newer state.

Do not use a real timeout just to wait for a mocked response. Flush it at the point the test needs it. Finish by explaining that a separate integration check is needed for the actual server and deployment.

References and next practice

angular.dev — ng http

angular question collection · Browse interview topics · Practise a question in Interview Challenge · How to Answer Technical Interview Questions Out Loud