I am facing issues with testing a service in Angular that utilizes the HttpClient

Currently, I am working on an Angular 5 Project, but it's not a major project. However, I haven't been involved in the Angular2+ environment since early 2.1/2.2.

My issue revolves around a Service that makes a call to a public API. Unfortunately, my test is failing with the error message:

Error: Expected one matching request for criteria "Match URL: http://api.icndb.com/jokes/random/10", found none.

Here is the code snippet:

fact.service.spec.ts

import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing";
import {TestBed, inject} from "@angular/core/testing";
import {FactService} from "./fact.service";
import {Fact} from "../models/fact";
import {FactHttpResponse} from "../models/factHttpResponse";

describe("FactService", () => {
    let factService: FactService;
    let httpMock: HttpTestingController;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [FactService],
        });
        httpMock = TestBed.get(HttpTestingController);
        factService =  TestBed.get(FactService);
    });

    // TODO: There seems to be an issue with this test, seeking online help
    it("Should be able to retrieve Facts in subscription when calling loadFacts", (done) => {
    const factList: Fact[] = [
        {id: 1, joke: "a"},
        {id: 2, joke: "a"},
    ];

    const factResponse: FactHttpResponse = { value: factList};

    factService.subscribe(val => {
        expect(val).toEqual(factList);
        done();
    });

    const mockRequest = httpMock
        .expectOne(factService.CHUCK_NORRIS_API.replace("{count}", "10"));
    expect(mockRequest.request.method).toEqual('GET');

    factService.loadFacts();

    mockRequest.flush(factResponse);
});

afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
    httpMock.verify();
}));
});

fact.service.ts

 import { HttpClient } from "@angular/common/http";
 import { Injectable } from "@angular/core";
 import { Subject } from "rxjs/Subject";

 import {Fact} from "../models/fact";
 import {FactHttpResponse} from "../models/factHttpResponse";

@Injectable()
export class FactService {

// public for testing purposes
public readonly CHUCK_NORRIS_API = "http://api.icndb.com/jokes/random/{count}";
private factSubject = new Subject();
private facts: Fact[] = [];
private favorites: Fact[] = [];

constructor(private http: HttpClient) { }

public subscribe(callback: (value: Fact[]) => void) {
    return this.factSubject.subscribe(callback);
}

public loadFacts(count = 10) {
    this.http.get<FactHttpResponse>(this.CHUCK_NORRIS_API.replace("{count}", count.toString()))
        .subscribe(data => {
           if (data.value) {
               this.facts = data.value
                   .map(f => {
                       f.favorite = !!this.favorites.find(fav => fav.id === f.id);
                       return f;
                   });
               this.factSubject.next(this.facts);
           }
        });
}
}

The code is functioning properly, but I am encountering difficulties in testing it. Interestingly, removing the verifyOne line results in a complaint about an outstanding request to the exact same URL mentioned in the error message.

Answer №1

Since your method factService.loadFacts() is responsible for generating the HTTP request, the last three steps of your test are actually in the incorrect order. Here is the corrected sequence, along with explanatory comments:

describe(..., () => {
    it(..., () => {
        ...

        // Add the HTTP request to the mockHttp request queue
        factService.loadFacts();

        // Ensure that there is only one request queued up
        const mockRequest = httpMock
            .expectOne(factService.CHUCK_NORRIS_API.replace("{count}", "10"));
        expect(mockRequest.request.method).toEqual('GET');

        // Respond to the pending request in the mockHttp request queue
        mockRequest.flush(factResponse);
    });

    // Pro tip: You can simplify by accessing mockHttp directly in the describe() scope
    afterEach(() => {
        // Verify that there are no pending requests in the mockHttp queue
        httpMock.verify();
    });
});

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

Tips and tricks for accessing the state tree within effects in @ngrx/effects 2.x

I am currently in the process of migrating my code from version 1.x to 2.x of @ngrx/effects. Previously, in version 1.x, I was able to access the state tree directly within an effect: constructor(private updates$: StateUpdates<AppState>) {} @E ...

New techniques for integrating jQuery with Angular 4

As I delve into learning Angular 4, my goal is to apply it to various projects. While I am still grasping the basics, I have encountered noticeable differences when compared to using jQuery for DOM manipulation. The transition to using Angular has presente ...

Guide to mocking the 'git-simple' branchLocal function using jest.mock

Utilizing the simple-git package, I have implemented the following function: import simpleGit from 'simple-git'; /** * The function returns the ticket Id if present in the branch name * @returns ticket Id */ export const getTicketIdFromBranch ...

The error that has occurred is: `TypeError: The object on the right side of the 'instanceof' keyword is

Testing my function that verifies if a variable is an instance of a specific type and performs an action has been successful. I conduct the verification using the following code: myCheckingFunction = () => { if (target instanceof H.map.Marker) { ... ...

The type '{}' does not contain a property named 'map'

Recently delving into TypeScript, and I'm curious about the process of typing an axios response when storing it in a state variable. I've initiated a basic API call from App.tsx, which returns the following object: {data: {…}, status: 200, s ...

Angular 2+ Service for tracking application modifications and sending them to the server

Currently I am facing a challenge in my Angular 4 project regarding the implementation of the following functionality. The Process: Users interact with the application and it undergoes changes These modifications are stored locally using loca ...

A new feature reminiscent of grace has been introduced in the most recent release of ChartJs 2.9.3

Incorporating ng2-charts into my angular project for chart generation has been quite useful. It relies on ChartJs2.9.3. Within the project, I have utilized a shared ChartOptions for various dynamically generated charts with varying values. However, the cha ...

Looking to retrieve HTML elements based on their inner text with queryselectors?

I am looking to extract all the HTML divs that contain specific HTML elements with innerText = ' * ' and save them in an array using Typescript. If I come across a span element with the innerText= ' * ', I want to add the parent div to ...

"Take control of FileUpload in PrimeNG by manually invoking it

Is there a way to customize the file upload process using a separate button instead of the component's default Upload button? If so, how can I achieve this in my code? Here is an example of what I've attempted: <button pButton type="button" ...

Transforming an Image URL into base64 format using Angular

I'm currently facing difficulty when attempting to convert a specified image URL into base64. In my scenario, I have a string that represents the image's path. var imgUrl = `./assets/logoEmpresas/${empresa.logoUrl}` Is there a way to directly co ...

Arranging Objects by Alphabetical Order in Typescript

I am struggling with sorting a list of objects by a string property. The property values are in the format D1, D2 ... D10 ... DXX, always starting with a D followed by a number. However, when I attempt to sort the array using the following code snippet, it ...

Delivering emails with attachments using Angular 8

I've been attempting to send an email with an attachment using Angular 8. Here's the code I've tried: <a href="mailto:<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="51283e2423343c30383d11343c30383d7f323e3c"&g ...

Load Angular template dynamically within the Component decorator

I am interested in dynamically loading an angular template, and this is what I have so far: import { getHTMLTemplate } from './util'; const dynamicTemplate = getHTMLTemplate(); @Component({ selector: 'app-button', // templat ...

Experimenting with a Collection of Items - Jest

I need to conduct a test on an array of objects. During the test coverage analysis of the displayed array, I found that the last object with the key link has certain conditions that are not covered. export const relatedServicesList: IRelatedServiceItem[ ...

methods to ensure a preselected value remains in use within mat-select in the context of Angular

In my current project, I've incorporated a unique mat-select component. Here's the corresponding code: <div class="col-md-6"> <mat-form-field> <mat-select placeholder="Select Job" formControlName="job_instruc ...

What is the best way to interpret the property 'subscribe' of an undefined object?

After cloning a MEAN stack application from GitHub, I made minor modifications by changing all instances of the word 'employee' to 'sensor'. The build was successful without any issues. However, upon launching localhost:4200, I encounte ...

What is preventing my Angular form from displaying the input data correctly?

I've been diligently following a PDF tutorial step by step, but for some reason, I'm not achieving the same results. Despite thorough googling, I can't seem to figure out where I'm going wrong. Here's the content of student.compon ...

The onRowSelect and onRowClick events are not being triggered on the Primeng table component in an Angular application

I am currently struggling to navigate to another component with the data selected when a row is clicked. I have been using p-table to accomplish this task. For some reason, neither onRowClick nor onRowSelection functions are being triggered. I even added ...

Creating asynchronous JavaScript constructors using a static method called "create" presents challenges when dealing with TypeScript types

I've been diving into the world of asynchronous constructors and have successfully implemented them in JavaScript. However, I'm facing a challenge with TypeScript types. Here's how it should ideally work: const a: AnyClass = await AnyClass. ...

Incorporate matTooltip dynamically into text for targeted keywords

I'm currently tackling a challenge in my personal Angular project that utilizes Angular Material. I'm struggling to find a solution for the following issue: For instance, I have a lengthy text passage like this: When you take the Dodge action, ...