Unexpected behavior: Promise.catch() fails to catch exception in AngularJS unit test

During the process of writing Jasmine unit tests for my Typescript app and running them via Resharper, I encountered an issue with executing an action when the handler throws an exception:

describe("Q Service Test", () => {
    var q: ng.IQService;
    var rootScope: ng.IRootScopeService;

    beforeEach(inject(($q, $rootScope) => {
        q = $q;
        rootScope = $rootScope;
    }));

    it("Proper handling of caught exceptions", () => {
        var state = 'ok';
        q.when(1)
            .then(() => {
                throw new Error("test exception");
            })
            .catch(() => {
                state = 'error';
            });

        rootScope.$digest();
        expect(state).toBe('error');
    });
});

Despite my efforts, the test is unsuccessful. This leads me to question whether this issue is unique to my testing environment/tools or if I am misunderstanding how the promise mechanism should be utilized.

Answer №1

Your use of the promise mechanism is not correct, simply throwing a user-defined throw statement does not handle it as a promise rejection properly. According to the documentation on $q:

When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of reject as the throw keyword in JavaScript. This also means that if you "catch" an error via a promise error callback and you want to forward the error to the promise derived from the current promise, you have to "rethrow" the error by returning a rejection constructed via reject.

While they are similar concepts, catching user-defined throw statements requires the use of catch statement blocks. Additionally, $q promises should only be used to catch rejected promises. Therefore, returning a rejected promise is the appropriate way to handle the process instead of throwing a user-defined exception.

DEMO

JAVASCRIPT

describe('Q Service Test', function() {

  var $q,
      $rootScope;

  beforeEach(inject(function(_$q_, _$rootScope_) {
    $q = _$q_;
    $rootScope = _$rootScope_;
  }));

  it('Rejected promises are handled properly', function() {

    var state = 'ok';

    $q.when(1)
      .then(function() {
        return $q.reject('rejected');
      })
      .catch(function() {
        state = 'error';
      });

    $rootScope.$digest();
    expect(state).toBe('error');    

  });

});

UPDATE:

The reason for the behavior of your code in the browser is due to Angular's $q implementation using try/catch statement blocks in processing the promise queue. When any callbacks throw errors, it catches the error, rejects it with the exception as the reason for rejection, and uses $exceptionHandler to log the error. It is recommended to simply return a rejected promise.

In terms of unit test behavior, the implementation of angular-mocks differs from the application's actual $exceptionHandler. The former uses different modes, with the default angular-mocks implementation using the rethrow mode which throws the exception instead of logging it. To align unit tests with the application's $exceptionHandler, set the mode to 'log'.

DEMO

JAVASCRIPT

describe('Q Service Test', function() {

  var $q,
      $rootScope;

  beforeEach(module('ng', function($exceptionHandlerProvider) {
    $exceptionHandlerProvider.mode('log');
  }));

  beforeEach(inject(function(_$q_, _$rootScope_) {
    $q = _$q_;
    $rootScope = _$rootScope_;
  }));

  it('Caught exceptions are handled properly', function() {

    var state = 'ok';

    $q.when(1)
      .then(function() {
        throw new Error();
      })
      .catch(function() {
        state = 'error';
      });

    $rootScope.$digest();
    expect(state).toBe('error');    

  });

});

Answer №2

According to the insights shared in the blog post titled Leveraging and linking promises within AngularJS, it is highlighted that triggering an exception will result not only in an error being thrown, but also activating Angular's designated exception handling mechanism. It raises the question whether Jasmine relies on Angular's exceptional handling capabilities to detect errors.

Is it necessary to explicitly throw an exception or can similar results be achieved through a method like this:

return q.reject(new Error("test exception"));

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

Can I search Firebase with parameters?

Just started using Firebase and AngularFire. Here's my current data structure: friends -JzKr-mrv-O7rlxrMi3_ creator: "111181498675628551375" description: "dsa" name: "das" --JzKrahnTf47MXp8nAZx creator: "111181498675628551320" ...

Can someone explain to me the best way to utilize the variable $scope.compdata within the

I am encountering an issue where I cannot access the variable $scope.compdata outside of the controller. Can someone help me with this? Thanks in advance. var compdata; $scope.fetchCompanies = function() { var deferred = $q.defer(); Company.ge ...

The format for Dependency Injection in AngularJS

I'm brand new to angular and I'm a little confused as to why $scope and $http are placed before the function like this. app.controller('PlayerController', ['$scope', '$http', function($scope, $http) { var audio ...

Angular - Automatically filling in an empty input field upon dropdown selection

My goal is to create a DropdownBox that will automatically fill input fields based on the selected value. For example, selecting "Arnold" from the dropdown will populate another textbox with "Laptop". How can I accomplish this? { name:'Arnold', i ...

Dealing with GraphQL mutation errors without relying on the Apollo onError() function

When managing access to an API call server-side, I am throwing a 403 Forbidden error. While trying to catch the GraphQL error for a mutation, I experimented with various methods. (Method #1 successfully catches errors for useQuery()) const [m, { error }] ...

Is it possible to create an observable with RXJS that emits only when the number of items currently emitted by the source observables matches?

I am dealing with two observables, obs1 and obs2, that continuously emit items without completing. I anticipate that both of them will emit the same number of items over time, but I cannot predict which one will emit first. I am in need of an observable th ...

Angular component not transmitting any data through binding

I have a page with two components. Component A is named Dashboard, which displays a list of elements using ng-repeat. These elements, referred to as systems, are shown through Component B called SystemView. Each SystemView contains a list of log entries, a ...

Deliver transcluded data to the descendant element of a hierarchical roster

I understand that there have been similar questions asked before, but my situation is slightly different. I am currently constructing a nested list and I want to include custom HTML content in each grandchild element alongside some common HTML. The problem ...

Unable to retrieve information from service using Angular 1.6 and TypeScript

I'm having an issue retrieving data from a Service in my Controller. Here is the code for my Service file: import {IHttpService} from 'Angular'; export class MyService { public static $inject = ['$http']; constructor(private $htt ...

The concept of a generic type serving as a characteristic of an incoming argument

What is the best way to assign a type property of an argument to a generic in TypeScript? Here's the code snippet: const foo = <T = someObject.bar>(someObject: {[string]: any}): T => { return someObject.bar } How can we set the type of ...

Utilizing the Angular directive md-autocomplete alongside the Google Maps autocompleteService for seamless integration

Greetings and thank you for taking the time to assist me. I have been diligently working on setting up an autocomplete search box that interacts with the Google AutocompleteService API using Angular. Currently, my Angular AutocompleteService is functioni ...

utilizing jquery functions within angularjsorleveraging jquery functions in

I am looking to utilize jQuery functions within my Angular code. Specifically, I have three functions funcX(data), funcY(), and funcZ() enclosed in $(document).ready(function(){ ..... }) The function funcX(data) requires a parameter data obtained from a ...

How can you identify dynamically created elements within an AngularJS directive?

I have a directive where I need to target specific DOM elements, some of which are dynamically generated in an ng-repeat loop. If I try to select them directly, I only get the static elements. However, if I delay the selection by, let's say, 500ms, I ...

The contents table remains fixed in the top right corner as you scroll

I have developed an Angular app with a table-of-contents component that only displays two items. The code for the script is as follows: ts import { Component, OnInit } from '@angular/core'; import { pdfDefaultOptions } from 'ngx-extended-p ...

Experiencing issues when running `ng serve` with a basic Angular CLI project created using

Just started using angular-cli and initiated an angular 2 project with the command ng new angular2. However, upon trying to run it using ng serve, I encounter crashes accompanied by errors. Error Log: ERROR in [default] C:\Users\user\deskt ...

The Typescript hello world example encounters an issue with Karma

Recently, I encountered an issue while working on a TypeScript project with Jasmine and Karma. It seems that Karma is unable to execute the compiled unit tests due to an error showing up in Chrome: Uncaught ReferenceError: define is not defined To illust ...

Tips on safeguarding your templates while working with SailsJS and AngularJS

I am currently working on a web application using SailsJS and AngularJS. My project requires session management to track user login status. I came across this helpful tutorial and implemented an index.ejs view located in the /view folder. Within the index. ...

Retrieve the child grid's data source in Kendo using Angular

I am currently working with a master detail grid in Kendo Grid and using the Angular version of the Kendo Grid. One interesting feature I have added is a custom button for adding a new record in each row of the grid. When this button is clicked, a new rec ...

The modal template in Angular UI is not displaying properly with Bootstrap styling

I've been working on displaying a modal template when a row is selected on a table. The issue I'm facing is that upon clicking a row, a 2px thick black shadowed line appears, which seems to represent the modal but doesn't display its conten ...

Creating JSON arrays for multiple elements using the same model name

I am using Angular to generate JSON data for multiple elements. Currently, the output looks like this: {"field":{"name":{"0":"a","1":"b"},"length":{"0":10,"1":20}}} However, I would like the JSON data to be more sophisticated and in array form, like this ...