Jasmine examination fails to progress to the subsequent segment of the promise

I want to test a specific function:

    function initializeView() {
        var deferred = $q.defer();
        if(this.momentArray) {
            core.listMoments(constants.BEST_MOMENT_PREFIX, '').then(function(moments) {
                //Ommitted
                deferred.resolve(moments);      
            }, function(error) {
                console.log("ERROR");
                deferred.reject(error);
            });
        }
        else {
            deferred.resolve();
        }
        return deferred.promise;    
    };

There is a function call within this one:

    function listMoments(prefix, startAfter) {
        // var deferred = $q.defer();
        var promises = [];
        return awsServices.getMoments(prefix, startAfter).then(function(moments) { //Mocked
            console.log("getMoments Returned"); //This does not appear in the console
            for(var i = 0; i < moments.length; i++) {
                // moments[i].Key = constants.IMAGE_URL + moments[i].Key;
                promises.push(getMomentMetaData(moments[i]));
            }
        return $q.all(promises);
        });
    };

This is the corresponding test function:

it('Should correctly initialize the view', function(done) {
    spyOn(awsServices, 'getMoments').and.callFake(function() {
        console.log("getMoments Has been mocked"); //This appears in the console
        return $q.resolve(mock_moment);
    });
    service.initializeView().then(function() {
        done();
    })
});

The issue lies with the mock of 'getMoments' function in awsServices. It seems that although the function is being mocked successfully, the "then" part of the promise is not executing.

Hence, based on the console logs, it prints the 'getMoments Has been mocked' log but fails to print 'getMoments Returned'. Consequently, the function gets stuck in an infinite loop and my test eventually times out.

Answer №1

In order to ensure the proper functioning of the .then() section in a promise within a test, it is necessary to utilize the $rootScope.$apply() method. This applies irrespective of whether the promise lies within your test code or in a referenced library being tested. It can be thought of as similar to the flush() function utilized for $http or $timeout calls.

The Angular documentation's $q page provides an example of how to use this:

it('should imitate promise', inject(function($q, $rootScope) {
  var deferred = $q.defer();
  var promise = deferred.promise;
  var resolvedValue;

  promise.then(function(value) { resolvedValue = value; });
  expect(resolvedValue).toBeUndefined();

  // Imitate resolution of promise
  deferred.resolve(123);
  // Take note that the 'then' function does not get called synchronously.
  // The reason for this is that we want the promise API to always behave asynchronously,
  // regardless of whether it was called synchronously or asynchronously.
  expect(resolvedValue).toBeUndefined();

  // Propagate promise resolution to 'then' functions using $apply().
  $rootScope.$apply();
  expect(resolvedValue).toEqual(123);
}));

Please observe that they have injected $rootScope.

Answer №2

$q guarantees both synchronous and asynchronous promises depending on the resolution timing and digest cycles.

In Angular tests, it is generally advised not to use an asynchronous done callback.

Angular tests should ideally be synchronous, just like $q promises. To ensure this, a manual trigger of digest is required when chaining existing promises (such as those returned from getMoments and initializeView) using then. If a done callback is placed within then but no digest is triggered, it may result in specification timeout.

spyOn(awsServices, 'getMoments').and.callFake(function() {
    console.log("getMoments Has been mocked"); //This gets logged
    return $q.resolve(mock_moment);
});
service.initializeView();
$rootScope.$digest();

An area that can be enhanced here is isolation. Currently, multiple units (methods) are involved in a single test, which can make troubleshooting more cumbersome if any of them fail.

Typically, unit testing involves testing one unit at a time, while the others are either mocked or stubbed. In this example, one test calls service.listMoments and mocks awsServices.getMoments, while another test calls service.initializeView and mocks service.listMoments.

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

The issue with Jquery .post function not functioning properly within a popup div

After spending countless hours on this issue, I feel like I'm at a loss... The problem lies in a div that pops up with a button, where the button fills data into different sections of the HTML... Everything works fine except for when I use ajax to c ...

What is the best way to keep an image centered in the nav-bar as it decreases in size?

As I work on building a website using a template (https://github.com/learning-zone/web-templates/tree/master/victory-school-free-html5-bootstrap-template), I encountered an issue with the navigation bar. The original design appears like this: Before shrin ...

What is the speed difference between calling functions from require's cache in Node.js and functions in the global scope?

Let's imagine a scenario where we have two files: external.js and main.js. // external.js //create a print function that is accessible globally module.exports.print = function(text) { console.log(text) } Now let's take a look at main.js: ...

When attempting to access http://localhost:3000/traceur in Angular 2 with the angular-in-memory-web-api, a 404 (Not Found) error

Hello, I'm encountering an issue with angular-in-memory-web-api. I have attempted to use angular2-in-memory-web-api in SystemJS and other solutions, but without success. I am currently using the official quickstart template. Thank you for any assistan ...

Issue with AngularJS where the value in a dropdown list does not bind to ng-model if there are no changes made

Please check out my plunkr for reference: https://plnkr.co/edit/QRQQmxf3ZDyh6o0CqtrD?p=preview I have a form with a dropdown list that is dynamically populated as shown below: <form name="frmAccount" role="form" ng-submit="submit()"> <div ...

Tips for concealing an input IP Address in React

Looking for suggestions on an IP Address mask input solution. The format might vary between 999.99.999.99 and 99.9.99.9, but react-input-mask does not support varying lengths. Any recommendations? ...

Is there a way to utilize the 'interval' Rxjs function without triggering the Change Detection routine?

My goal is to display the live server time in my application. To achieve this, I created a component that utilizes the RXJS 'interval' function to update the time every second. However, this approach triggers the Change Detection routine every se ...

Unable to display label in form for Angular 2/4 FormControl within a FormGroup

I'm having trouble understanding how to: Use console.log to display a specific value Show a value in a label on an HTML page Display a value in an input text field Below is my TypeScript component with a new FormGroup and FormControls. this.tracke ...

How to access the dynamic route's path in Next.js when using Server Components?

Obtaining the dynamic route path in a Next JS server component poses a challenge. This task is simple when working with client components. If you are working on src/app/[id]/page.tsx "use client"; import { usePathname } from "next/navigatio ...

Display the number of rows per page on the pagination system

Looking for a way to add a show per page dropdown button to my existing pagination code from Mui. Here's the snippet: <Pagination style={{ marginLeft: "auto", marginTop: 20, display: "inline-b ...

Creating a carousel of cards using JavaScript, CSS, and HTML

Here is a visual reference of what I'm attempting to achieve: https://i.stack.imgur.com/EoQYV.png I've been working on creating a carousel with cards, but I'm struggling to synchronize the button indicators with card advancement when clicke ...

Exploring React and finding the way to a specific item

I have a list of users displayed in a table. When a user clicks on a row, I want to navigate to a different component to show the details. <tbody> {this.state.clients.map(client => ( <tr className="tableRow" onClick={() => this. ...

Vite terminal not executing Npm commands

Here is what I entered: npm run dev Error: npm ERR! Missing script: "dev" npm ERR! npm ERR! To see a list of scripts, run: npm ERR! npm run npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\andre\AppData&bs ...

Using the .each method in AJAX JQUERY to iterate through callback JSON data and applying an if statement with Regular Expression to identify matches

Implementing a live search feature using ajax and jQuery involves running a PHP script that returns an array of database rows, encoded with JSON, based on the textfield input. This array is then iterated through in JavaScript after the callback function. ...

Choose the camera when utilizing the navigate.getUserMedia() function

I am currently utilizing the navigate.getUserMedia() method to record video on my mobile device and perform additional processing on it. However, at the moment, it is only capturing video using the front camera. How can I make it switch to the rear facing ...

Error: doc.data().updatedAt?.toMillis function is not defined in this context (NextJs)

I encountered an error message while trying to access Firestore documents using the useCollection hook. TypeError: doc.data(...)?.updatedAt?.toMillis is not a function Here is my implementation of the useCollection Hook: export const useCollection = (c, q ...

Embracing Error Handling with ES6 Promises

I am seeking clarification on how errors travel through a series of then continuations to a catch continuation. Consider the following code: Promise.reject(new Error("some error")) .then(v => v + 5) .then(v => v + 15) .catch(er ...

Optimal method for defining controllers in AngularJS

As a newcomer to AngularJS, I find myself faced with the dilemma of determining the best approach for creating a controller within the ng-app="mainApp". In traditional programming languages, it is common practice to keep related data together. However, in ...

The TypeScript factory class anticipates an intersection

In my code, I have a class factory called pickSomething that generates a specific type based on a key provided from a ClassMap: class A { keya = "a" as const; } class B { keyb = "b" as const; } type ClassMap = { a: A b: B } c ...

The findByIdAndUpdate() function lacks the ability to modify the collection

I'm encountering an issue when trying to update a product using mongodb and redux. It seems that the database is not reflecting the changes after I attempt to update the product. Can someone please assist me with this problem? Here is my product.js f ...