Testing a function that involves multiple HTTP requests through unit testing

I am struggling with writing a unit test for a function that indirectly triggers multiple HTTP requests.

The service I am testing has the following structure:

/* content.service.ts */
import { Injectable } from "@angular/core"
import { ApiService } from "services/api/api.service"

@Injectable()
export class ContentService {
  public languages: Array<any>
  public categories: Array<any>;

  constructor(private apiService: ApiService) {}

  public async fetchLanguages(): Promise<boolean> {
    const data: any = await this.apiService.getData("language");

    if (!data) return false;

    this.languages = data;
    return true;
  }

  public async fetchCategories(): Promise<boolean> {
    const data: any = await this.apiService.getData("category");

    if (!data) return false;

    this.categories = data;
    return true;
  }

  public async initService(): Promise<boolean> {
    const statusLanguages: boolean = await this.fetchLanguages();

    if (!statusLanguages) return false;

    const statusCategories: boolean = await this.fetchCategories();

    if (!statusCategories) return false;

    return true;
  }
}

The test file for the service is structured as follows:

/* content.service.spec.ts */
import {
  HttpClientTestingModule,
  HttpTestingController
} from "@angular/common/http/testing"
import { TestBed } from "@angular/core/testing"

import { ApiService } from "services/api/api.service"
import { ContentService } from "./content.service"

describe("ContentService:", () => {
  let contentService: ContentService;
  let httpMock: HttpTestingController;

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

    contentService = TestBed.get(ContentService);
    httpMock = TestBed.get(HttpTestingController);
  });

  afterEach(() => {
    httpMock.verify();
  });

  /* Dummy data */
  const languages = [
    { id: 1, uid: "en", active: true },
    { id: 2, uid: "es", active: false },
    { id: 3, uid: "hr", active: false }
  ];

  const categories = [
    { id: 1, uid: "default" },
    { id: 2, uid: "alternative" }
  ];

  describe("initService:", () => {
    it("successful response", (done: DoneFn) => {
      contentService.initService().then(result => {
        expect(result).toEqual(true);
        expect(contentService.languages).toEqual(languages);
        expect(contentService.categories).toEqual(categories);
        done();
      });

      const requests = httpMock.match(req => !!req.url.match(/api\/data/));

      expect(requests.length).toBe(2);

      requests[0].flush({ status: "ok", data: languages });
      requests[1].flush({ status: "ok", data: categories });
    });
  });
});

While running unit tests with ng test, an error is encountered:

TypeError: Cannot read property 'flush' of undefined

This TypeError specifically points to the line:

requests[1].flush({ status: "ok", data: categories })

From this, it appears that the first request is handled correctly, but not the second one.

Shouldn't the match method from the HttpTestingController capture all matching HTTP requests based on the provided regular expression?

Answer №1

When developing unit tests, it is important to focus on testing only one specific unit at a time. In this scenario, your main unit of focus is the ContentService. It's crucial to remember that testing its dependencies is not necessary. For example, if the ApiService is a dependency used for making Http Requests, there is no need to verify whether these requests are actually being made during testing. Instead, simply create a mock version of the ApiService using either a mock value or a jasmine spy object.


const spy = jasmine.createSpyObj('ApiService', ['getData']);

TestBed.configureTestingModule({
  providers: [
    ContentService,
    { provide: ApiService, useValue: spy }
  ]
});

Focus your testing efforts on ensuring that the variables within the ContentService are assigned the correct values and that the methods return the expected outcomes based on given inputs.

If needed, you can conduct separate testing for the ApiService. Remember, the goal of unit testing is to examine the internal workings of individual classes rather than their dependencies.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Managing the rxjs from an Array of observables when the array has no elements

Looking for a more elegant solution to handle the case where an array of observables is empty in the following TypeScript function. I want the observable to complete when subscribe() is called without the need for an initial check. I've already imple ...

Chromium browser experiencing issues with the functionality of Dropdown component

My issue involves a basic pie chart with a dropdown menu (created with the <select> tag) that allows users to change the data displayed on the chart. I have implemented this in Angular 6 and it works perfectly fine in Firefox. However, when I tested ...

What are the steps to integrate webpack with .NET 6?

Struggling to incorporate webpack into .NET 6 razor pages. The existing documentation online only adds to my confusion. Here is a snippet from my file1.ts: export function CCC(): string { return "AAAAAA"; } And here is another snippet from ...

Executing ng test and ng serve at the same time

As someone who is still learning Angular 5, I am in the process of setting up a development environment for my team to work on a new Angular 5 application. My goal is to have our team able to run linting tests and unit tests every time they make changes, ...

Ways to organize JSON information in Angular by date basis?

I am working on a project where I need to organize multiple JSON objects into an array based on their date data, with the date field serving as the key. ...

What are the steps to switch out the 'subscribe' method with 'mergeMap' or

I am trying to understand how I can replace the subscribe method with switchMap in my code. I have been searching for an example online but haven't found one yet. dialogRef.afterClosed().pipe(filter(result => !!result)).switchMap(result => { ...

Using custom types for prop passing in Next.js with TypeScript

After making a http call, I obtain an array containing JSON data. This data is then assigned to a type called Service. type Service = { id?: string; name?: string; description?: string; }; The api call is made in getServerSideProps and the Service type is ...

Is Webpack CLI causing issues when trying to run it on a .ts file without giving any error

I am facing an issue with my webpack.config.js file that has a default entrypoint specified. Here is the snippet of the configuration: module.exports = { entry: { main: path.resolve('./src/main.ts'), }, module: { rules: [ { ...

Can Typescript restrict a value to only exist within a specified set of key names within the same object?

I am completely new to Typescript and I am fascinated by the way it can check types. One thing I would like to know is if Typescript can be used to verify at compile time whether a value's domain falls within a predefined set of key names that are de ...

A guide to implementing angularjs app.service and $q in typescript

I am fairly new to TypeScript and AngularJS and I am struggling to find the correct answer for my issue. Below is the relevant code snippet: export class SidenavController { static $inject = ['$scope', '$mdSidenav']; constructor(p ...

One cannot use a type alias as the parameter type for an index signature. It is recommended to use `[key: string]:` instead

I encountered this issue in my Angular application with the following code snippet: getLocalStreams: () => { [key: Stream['key']]: Stream; }; During compilation, I received the error message: An index signature parameter typ ...

problem encountered when running "ionic cordova build android --prod --release"

A chat application has been developed using Ionic2. Upon attempting to generate a production build with ionic cordova build android --prod --release, the following error is encountered. Error: ./node_modules/rxjs/observable/BoundCallbackObservable.js ...

Unable to prolong TypeScript document

As I develop a drag and drop interface, upon dropping a file, the native File is retrieved. To enhance this interface with additional information, I decided to explore various approaches. In my initial attempt, I utilized: interface AcceptedFile extends ...

Interacting with an iframe within the same domain

I'm currently working on an application in Angular 6 that requires communication with an iframe on the same origin. I'm exploring alternative methods to communicate with the iframe without relying on the global window object. Is there a more effi ...

7 Tips for Renaming Variables in VSCode without Using the Alias `oldName as newName` StrategyWould you like to

When working in VSCode, there is a feature that allows you to modify variables called editor.action.rename, typically activated by pressing F2. However, when dealing with Typescript and Javascript, renaming an imported variable creates aliases. For exampl ...

Tips for troubleshooting Angular 4 unit testing using jasmine and karma with simulated HTTP post requests

I have a service that I need to unit test in Angular 4 using TypeScript and Jasmine. The problem is with the http where it needs to perform a post request and get an identity in return, but for some reason, no data is being sent through. My goal is to ac ...

Downloading a CSV file using Angular 8 and Django RestFramework

I am a beginner in Angular and I'm looking to implement a feature where users can download a CSV file upon clicking a download button. Django viewset @action(detail=False, methods=['get']) def download_csv(self, request): data = { ...

Error message: Cordova not found - unable to use 'ionic native' 3 for CRUD operations with SQLite database

I am attempting to manage CRUD data with SQLite in Ionic 3, but unfortunately cordova is not functioning as expected. https://i.sstatic.net/5m411.png ...

What is the best way to programmatically choose an option from a ng-select dropdown list?

My program displays a list to the user utilizing ng-select. This particular list is populated with various items: item 1 item 2 item N The user has two options when interacting with this list. They can either select an existing item or add a new one. If ...

Using TypeScript to automatically determine the argument type of a function by analyzing the return type of a different function

I am working on an interface with the following structure: interface Res<R = any> { first?(): Promise<R>; second(arg: { response: R }): void; } However, I noticed that when creating a plain object based on this interface, the response ...