Testing a recursive function that invokes a function which returns a promise

In the code snippet I am testing, there is a simple queue that gets periodically emptied. Each number in the queue triggers an HTTP POST request to send it to a fictional API address. If the POST is successful, the number is removed from the queue and the next number is processed.

class Queue {
    queue: Array<number>;
    processingQueue: boolean;

    constructor(public $http: angular.IHttpService) {
        this.queue = new Array<number>();
        this.processingQueue = false;
    }

    startQueueProcessor() {
        this.processingQueue = false;

        setInterval(() => {
            if (!this.processingQueue)
                this.processQueue();
        }, 1000);
    }

    postNumber(number: number): angular.IHttpPromise<{}> {
        return this.$http.post('http://mydummyurl.com/givemeanumber', number);
    }

    processQueue(): void {
        this.processingQueue = true;

        if (this.queue.length !== 0) {
            const item = this.queue[0];

            this.postNumber(item)
                .then(
                () => { 
                    this.queue.shift(); 
                    this.processQueue(); 
                },
                () => { 
                    this.processingQueue = false; 
                }
                );
        } else {
            this.processingQueue = false; 
        }
    }

    enqueue(number: number): void {
        this.queue.push(number);
    }
}

The test I aim to conduct will confirm that after adding three items to the queue, calling processQueue() once will empty it.

An example using Jasmine:

describe('queueing', () => {
    var queueService;

    beforeEach(inject((_$httpBackend_, $injector) => {
        httpBackend = _$httpBackend_;
        httpBackend.whenPOST().respond(200);

        queueService = $injector.get('queue');
    }));

    it('should clear queue completely when processQueue() is called once', () => {

        queueService.enqueue(1);
        queueService.enqueue(2);
        queueService.enqueue(3);

        expect(queueService.queue.length).toBe(3);

        queueService.processQueue();

        // include waiting logic for processQueue() completion

        expect(queueService.queue.length).toBe(0);
    });
});

However, the second expect() fails with the message Expected 3 to be 0. This is likely due to the asynchronous nature of the promises returned by postNumber(), causing the queue not to be empty when the second expectation is evaluated.

How can I ensure that processQueue() completes before checking whether the queue has been emptied?

Answer №1

To enhance processQueue functionality, consider modifying it to return a Promise. This can be achieved by declaring the method as async and using await within it:

async processQueue(): Promise<void> 
{
    this.processingQueue = true;

    if (this.queue.length !== 0) 
    {
        const item = this.queue[0];

        try
        {
            await this.postNumber(item);
        
            this.queue.shift();
            this.processQueue(); 
        }
        catch
        {
                this.processingQueue = false;
        }
    } 
    else 
    {
        this.processingQueue = false;
    }
}

In your test suite, ensure that the queue is fully cleared after calling processQueue() once:

it('should clear queue completely when processQueue() is called once', async (done) => {

    queueService.enqueue(1);
    queueService.enqueue(2);
    queueService.enqueue(3);

    expect(queueService.queue.length).toBe(3);

    await queueService.processQueue();

    // Add logic to wait for completion of processQueue()

    expect(queueService.queue.length).toBe(0);

    done();
});    

This approach should provide better control over queue processing.

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

Create a function that can accept either no parameters or one parameter depending on the input parameter provided

Do you think there is a more efficient way to write this code? The function Z can be called with either one parameter or with no parameters. If the parameter Y is passed in, then the function z(y) is returned, otherwise just run the function z() async x ...

Adjust the body class dynamically based on the URL in AngularJS

Here is the link to my website To Log In - http://localhost/ang/#/login To Access Dashboard - http://localhost/ang/#/dashboard See below for the HTML code for the body tag If the current URL is http://localhost/ang/#/login, then the body should include ...

Having trouble typing a RequestHandler in Express JS using TypeScript?

I am trying to create a RequestHandler that types the req.params and req.body with these interfaces interface UpdateNoteParams { noteId: string, } interface UpdateNoteBody { title?: string, text?: string, } This is what I have tried so far: e ...

Using TypeScript to work with JSON fields that include the '@' symbol

While working on an Angular project with TypeScript, I am facing a challenge of displaying certain JSON fields obtained from a POST request to the user. One of the fields begins with the '@' symbol, which is a reserved word in Angular causing th ...

Determine the sum of all rows in ng-repeat by using a different controller

I am working with rows that require a total calculation. Within my ng-repeat loop, I use a controller called RowCtrl to calculate the total. app.controller('RowCtrl', function ($scope) { $scope.unit_price = 0; $scope.quantity = 0; $sc ...

disabled button feature not being activated

Although I have browsed through several similar questions, none of them seem to have the solution I require. Here's an angular component code snippet: @Component({ providers: [ FlowsheetService, SubscriptionService ], moduleId: module.id, sele ...

How do I submit my form data as a JSON object using AngularJS?

I'm currently in the process of developing my first Angular app and trying to incorporate form submission into JSON data. Below is the form I have created: <span ng-show="show"> <form name="inputForm" class="form-group" ng-controller="For ...

Ensuring consistency between TypeScript .d.ts and .js files

When working with these definitions: https://github.com/borisyankov/DefinitelyTyped If I am using angularJS 1.3.14, how can I be certain that there is a correct definition for that specific version of Angular? How can I ensure that the DefinitelyTyped *. ...

Issue with Select Box Value not updating in HTML Document Object Model

The issue I'm facing is that the select box DOM value does not update after the value has been changed in the component.ts file. Could you please review the code provided below: <form class="form-horizontal aformgroups" [formGroup]="Purchase ...

Is there a way to utilize namespace import without bringing in all the other imports as well

Within our angular application built with typescript, we make use of lodash. Our current approach to importing lodash is demonstrated below: import * as _ from 'lodash'; //.. code which utilizes _.pluck() However, in order to optimize for tree ...

Is it possible for transclusion to display content from external sources using *ngIf and <ng-content>?

In my Angular4 Project, I have come across this snippet of code: <div class="divider"></div> <ng-content select=".nav-toggle"></ng-content> Now, I am trying to figure out a way to display the divider only when there is content pr ...

Angular model does not bind properly to Bootstrap 3 DateTimePicker after 'dp.change' event

I am currently implementing the Bootstrap 3 DateTimePicker plugin by eonasdan. While everything seems to be functioning correctly, I have encountered an issue with binding the selected date within the input field to Angular's ng-model. Whenever I make ...

The issue with AngularJS ng-model not functioning properly with dynamically generated input fields

I have set up a dynamic templateUrl for form fields and I am attempting to use ng-model within ng-repeat. The parent directives and form field directive are functioning correctly and being generated, but when I try to apply ng-model it does not seem to wor ...

Spartacus storefront is having trouble locating the .d.ts file when using Angular/webpack

Seeking help from the SAP Spartacus team. Encountering errors while developing a Spartacus component, specifically with certain Spartacus .d.ts definition files that cannot be resolved. The issue is reproducible in this Github repository/branch: This pr ...

Exploring the variances among Angular promise objects

Can you explain the distinction between promise 1 and promise 2? The first one only has a $$state property, while the second one also includes error and success callbacks. What makes them different? <div ng-app="app"> <div ng-controller="TodoCtr ...

Using TypeScript in Angular 2, obtain the name of the current location through geolocation

My current project involves retrieving my exact location (City Name) using Typescript. I am working on creating a service that can accomplish this task. Below is the code snippet I have implemented: import { Injectable} from '@angular/core'; imp ...

Transformation of Ionic 2 ScreenOrientation Plugin

Can someone assist me with this issue? A while back, my Ionic 2 app was functioning correctly using the ScreenOrientation Cordova plugin and the following code: window.addEventListener('orientationchange', ()=>{ console.info('DEVICE OR ...

ability to reach the sub-element dictionaries in typescript

class ProvinciaComponent extends CatalogoGenerico implements OnInit, AfterViewInit { page: Page = new Page({sort: {field: 'description', dir: 'asc'}}); dataSource: ProvinciaDataSource; columns = ['codprovi ...

Dynamic Image Dropdown in Bootstrap NavBar

I need some help with implementing an image dropdown menu in the NavBar using UI Bootstrap 14.3. Currently, I have tried the code below, but the menu appears at the top of the page instead of aligning under the image. Any suggestions on how to fix and en ...

Failure to deliver Server Side Events (SSE) to the client side

I'm currently utilizing the MEAN stack and facing challenges with receiving events from the server side. Despite using EventSource, I am unable to make it work as intended. Although the connection appears to be open, no messages are being received fr ...