DEV Community

Fran Saoco
Fran Saoco

Posted on • Edited on

3 1 1

Testing Angular HTTP Services using Jest

The tests check if the HTTP service works correctly by:

  1. Making fake API calls (GET/POST)
  2. Returning test data
  3. Handling errors properly
  4. All without using real internet connections

This ensures the service works as expected before using it in real apps.

data-http-service.ts

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

const API_URL = 'https://api.example.com/data';

@Injectable({
  providedIn: 'root'
})
export class DataHttpService {
  http = inject(HttpClient);

  getData(): Observable<unknown> {
    return this.http.get<unknown>(API_URL);
  }

  postData(payload: unknown): Observable<unknown> {
    return this.http.post<unknown>(API_URL, payload);
  }

}
Enter fullscreen mode Exit fullscreen mode

Production tip: Store API URLs in environment variables (never in code) and mock them for testing, complete walkthrough: Using and Mocking Angular Environments with Jest

data-http-service.spec.ts

import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { DataHttpService } from './data-http.service';

describe('DataHttpService', () => {
  let service: DataHttpService;
  let httpMock: HttpTestingController;
  const API_URL = 'https://api.example.com/data';
  const mockData = { id: 1, name: 'Test Data' };

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [DataHttpService]
    });

    service = TestBed.inject(DataHttpService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    httpMock.verify();  // Verify no outstanding requests
  });


  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  describe('.getData', () => {
    it('should make GET request and return data', () => {
      service.getData().subscribe(data => {
        expect(data).toEqual(mockData);
      });

      const req = httpMock.expectOne(API_URL);
      expect(req.request.method).toEqual('GET');
      req.flush(mockData);
    });

    it('should handle GET errors', () => {
      const mockError = { status: 404, statusText: 'Not Found' };

      service.getData().subscribe({
        next: () => fail('Should have failed'),
        error: (error) => {
          expect(error.status).toEqual(mockError.status);
        }
      });

      const req = httpMock.expectOne(API_URL);
      req.flush(null, mockError);
    });
  });

  describe('.postData', () => {
    it('should make POST request with payload', () => {
      const testPayload = { data: 'test' };

      service.postData(testPayload).subscribe(data => {
        expect(data).toEqual(mockData);
      });

      const req = httpMock.expectOne(API_URL);
      expect(req.request.method).toEqual('POST');
      expect(req.request.body).toEqual(testPayload);
      req.flush(mockData);
    });

    it('should handle POST errors', () => {
      const testPayload = { data: 'test' };
      const mockError = { status: 500, statusText: 'Server Error' };

      service.postData(testPayload).subscribe({
        next: () => fail('Should have failed'),
        error: (error) => {
          expect(error.status).toEqual(mockError.status);
        }
      });

      const req = httpMock.expectOne(API_URL);
      req.flush(null, mockError);
    });
  });

});
Enter fullscreen mode Exit fullscreen mode

Create and Manage Features in Your AI Agent

Create and Manage Features in Your AI Agent

With DevCycle's MCP server, your AI agent can create, manage and evaluate feature flags - and you can stay in code and in context. Use it to create/QA features end-to-end with a prompt, or to investigate incidents right from your AI-enabled IDE.

Learn More

Top comments (2)

Collapse
 
damiansiredev profile image
Damian Sire (Dev)

I followed the series of articles and it's great! Just one comment, because many people come and copy things literally without thinking much xD (or without adapting them to their specific case).

When you copy this, the API_URL constant in this case is defined both in the service and in the test file. This works, but if the URL changes, you must remember to update it in both places.

So, when you apply this, you could export the constant from the service file (export const API_URL = ...;) and import it into the test file. This ensures you always use the same URL.

Also, keeping it separate in the tests can also be useful if you need to test different endpoints in the future within the same spec file (although in that case you could import more than one)

Remember to evaluate and think critically according to your case (You can even have a file with the API URLs).

To the author of the post, thank you for taking the time to make them and share :)

Collapse
 
fransaoco profile image
Fran Saoco • Edited

Thanks @damiansiredev !
You inspired a deep dive into Angular environment variables covering secure configuration, CI/CD integration, and proper mocking for unit tests:

Using and Mocking Angular Environments with Jest

Tiger Data image

🐯 🚀 Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

[Workshop] Building and Monitoring AI Agents and MCP servers

Building with AI is different. Sentry has built some tools to help. In this workshop, you’ll learn how to monitor your agents, debug your MCP servers and how to debug with Seer.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️