Exploring AngularJS unit testing: integrating async and await with Jasmine

I'm currently facing a challenge with unit testing my Angular service using the async/await keywords in the respective (Jasmine) unit tests below. The test for the built-in Promise is functioning correctly, however, I am encountering difficulties in getting the Angular $q counterpart to work.

  • Angular: 1.6.5
  • Jasmine: 2.7.0
  • (Headless) Chrome on MacOS: 60.x

angular
  .module('asyncAwaitTest', [])
  .factory('xService', xServiceFactory);

function xServiceFactory(
  $q,
  $timeout
) {
  return {
    getXAfter1Sec() {
      return new Promise(resolve => setTimeout(() => resolve(43), 1000));
    },
    getXAfter1SecWithAngular$Q() {
      const deferred = $q.defer();

      $timeout(() => deferred.resolve(43), 1000);

      return deferred.promise;
    }
  };
}

jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;

describe('asyncAwaitTest: x service', () => {
  let $timeout;
  let xService;

  beforeEach(() => {
    module('asyncAwaitTest');

    inject(
      (
        _$timeout_,
        _xService_
      ) => {
        $timeout = _$timeout_;
        xService = _xService_;
      }
    );
  });

  it('should work', async (done) => {
    const x = await xService.getXAfter1Sec();

    expect(x).toEqual(43);

    done();
  });

  it('should work, as well. Why isn't it working?', async (done) => {
    const xPromise = xService.getXAfter1SecWithAngular$Q();

    $timeout.flush();

    const x = await xPromise;

    expect(x).toEqual(43);

    done();
  });
});

Fiddle link provided here: https://jsfiddle.net/glenn/gaoh6bvc/

I've attempted to search on Google for solutions, but unfortunately, I haven't found a clear resolution yet 😞

Answer â„–1

If you want to simplify your test, consider creating a helper function that converts promises from $q to native promises. Take a look at an example here.

it('Why not try this approach as well?', async (done) => {
  const xPromise = toNativePromise(xService.getXAfter1SecWithAngular$Q());

  $timeout.flush();

  const x = await xPromise;

  expect(x).toEqual(43);

  done();
});

function toNativePromise(promise) {
  return new Promise((resolve, reject) => {
    promise.then(val => {
      resolve(val);
    }, err => {
      reject(err);
    });
  });
}

Answer â„–2

async operations rely on native promises, whereas AngularJS utilizes $q promises. The await keyword serves as a shorthand for linking a promise with then. In tests, $q promise chains are executed during the digest cycle.

Attempting to address this issue using

await xPromise;
$rootScope.$digest();

is ineffective because $rootScope.$digest() doesn't get evaluated until after its execution, leading to a lingering promise.

It is not recommended to test AngularJS using async..await methodology since Angular was primarily designed for synchronous testing.

An alternative approach could be

it('...', () => {
  ...
  xPromise.then(x => {
    expect(x).toEqual(43);
  });
  $rootScope.$digest();
});

Alternatively, promises can be streamlined with jasmine-promise-matchers:

it('...', () => {
  ...
  expect(xPromise).toBeResolvedWith(43);
  $rootScope.$digest();
});

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

How to Shift Focus to a Component in Vue.js

Within a container, I have a form section enclosed by a component that remains hidden due to the use of v-if. Upon clicking a button, the boolean value is toggled, revealing the previously concealed component. At this point, I aim to shift focus to the ini ...

Take away the attention from the span element

I have been experimenting with the following jsfiddle and attempted various methods suggested in this Stack Overflow thread, but I am struggling to remove the focus from the "feedback" span after clicking on the cancel button in jQuery confirm dialog. Her ...

React and Material-UI issue: Unable to remove component as reference from (...)

My React components are built using Material-UI: Everything is running smoothly MainView.js import React, { Component } from 'react'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import { List, ListItem } from ...

The specified variable will not be visible in the window object

I recently created a JavaScript code snippet that looks like this: let var1 = 1; window.var2 = 2; After running the code in the Chrome console, I entered window to inspect the global window object. Surprisingly, only the second variable appeared and the ...

The count of bits is not producing the anticipated result

Attempting to tackle the challenge of Counting Bits using JavaScript, which involves determining the number of set bits for all numbers from 0 to N, storing them in an array, and returning the result Let me provide an explanation Input: n = 5 ...

What causes the off-canvas menu to displace my fixed div when it is opened?

Using the Pushy off-canvas menu from GitHub for my website has been great, but it's causing some trouble with my fixed header. When I scroll down the page, the header sticks perfectly to the top, but once I open the off-canvas menu, the header disappe ...

Array in Javascript reset to null after its second iteration

''' const users = [] const addUser = ({ id, username, room }) => { // Clean the data username = username.trim().toLowerCase() room = room.trim().toLowerCase() // Validate the data if (!username || !room) { r ...

Leveraging Google Cloud Functions with Next.js (Client-Side Rendering)

Having trouble incorporating firebase cloud functions into my Next.js project, encountering an unfamiliar error. firebase-config.js const firebaseConfig = { apiKey: '~~~', authDomain: '~~', projectId: '~~~', storageBu ...

There are zero assumptions to be made in Spec - Jasmine analyzing the callback function

I've encountered a challenge with a method that is triggered by a d3 timer. Each time the method runs, it emits an object containing several values. One of these values is meant to increase gradually over time. My goal is to create a test to verify wh ...

Splitting jQuery - discover and distribute all price categories

I am faced with a challenge on my page where I have a list of items each displaying prices in GBP. The price is enclosed within a span element with a class of "price". My goal is to change the value of all the prices by dividing them by 1.2. Here's an ...

Automatically refreshing controller functionality in CodeIgniter

Greetings everyone, I'm looking for a way to automatically refresh a controller function every 5 seconds. Currently, I am using header('Refresh: 10.2'); within the controller function like this: public function delete() { heade ...

Encountering an internal/modules/cjs/loader.js:892 error when attempting to install the newest version of node.js on Windows 10

After recently updating my node.js to the latest version using chocolatey, I encountered a problem with my command prompt displaying the following error: internal/modules/cjs/loader.js:892 throw err; ^ Error: Cannot find module 'C:\Users&bso ...

Choose an element at random

I am trying to figure out how to select all div elements with the same class of "dot" under a parent div in JavaScript. Here is an example structure: <div id="dots"> <div class="dot"> . </div> <div class="dot"> . </div> ...

Tips for creating a backup static file for angular-translate

When utilizing the translateUrlLoader to retrieve the resource file from the server, what steps should be taken if this process fails? Is there a way to seamlessly switch to using a local file instead? ...

Update the color scheme of text labels and the title on a 3D bar graph created with amcharts

After creating a 3D stacked bar chart, I have successfully generated the graph using the provided code. However, I am looking to customize the appearance by changing the font color of all labels and the title to a different color. While I was able to mod ...

The customized Vaadin component with a tag is missing from the MainView layout

I am attempting to integrate my custom vis component into the MainView VerticalLayout. However, it is appearing above the layout and the layout itself contains an empty component tag. Custom component code In this section, I have labeled my component as " ...

Bring in dynamically

I am interested in dynamically importing a module only when it is needed. To achieve this, I have created a small mixin: import {extend} from "vee-validate"; export const rules = { methods: { addRule (name) { let requi ...

Steps to include a Target property in a freshly created MouseEvent

Trying to dispatch a contextMenu event, I've noticed that in the MouseEvent interface for TypeScript, the target property is missing, even though it is documented in the contextMenu documentation. Here's my TypeScript snippet: const emulatedMou ...

A guide on setting option values using ng:Options

It seems that AngularJS has trouble constructing a proper list of <option> elements using a simple template in Internet Explorer. As a workaround, the ng:Options directive is provided (refer to https://github.com/angular/angular.js/issues/235). Upon ...

Error message 800A03EA in Windows Script Host encountered while running Express.js script

I'm currently diving into the world of JavaScript development, following along with the guidance provided in the book called "JavaScript Everywhere." The book instructs me to execute the following code: const express = require('express' ...