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

Error encountered in Angular: Trying to assign a value to an empty string array results in the error message "Type (string | undefined)[] is not assignable to

Struggling with an issue in my Angular App - trying to assign a value to an empty array Current environment: node 12.18.4 npm 6.14.8 Here's my typescript.ts code snippet: import { Injectable } from "@angular/core"; import { Product } from ...

What is the process for loading a model using tf.loadModel from firebase storage?

I'm currently working on an app using the ionic3 framework that recognizes hand-drawn characters. However, I am encountering difficulties with importing the model into my project. The model was initially imported from Keras and converted using tensorf ...

Encountered an issue while trying to set up mocks with $route in vue-test-utils

I am currently practicing test referencing by using a mock router. Here is the code I am working with: NestedRoute.vue <template> <div> <div>Nested Route</div> <div class="username"> {{ $route.params.username ...

Encasing the Angular 2 component template in a <div> tag

Currently, I have a parent component managing multiple child components. My goal is to enclose each child component's template with a *ngIf directive for conditional rendering. The number of children in the parent component can vary. Here is an examp ...

What is the best way to track if a checkbox has been selected or not?

Working on a sports list for a mobile app using Ionic. There is a view where the user can select which sports they want to see on the list. <input type="checkbox" ng-model="sport.checked" ng-init="sport.checked=true"> After selecting ...

Is it possible to use an ngClick function in one directive to toggle data in another?

Currently, I am in the process of developing a weather application using Angular 1.5.8 where users should have the option to switch between imperial and metric units for temperature and wind speed. The toggle feature along with all the weather data fetche ...

Verifying whether the user is requesting their own page using ASP.NET MVC and AngularJS

My current project involves using asp.net mvc alongside angularjs. How can I include additional data (such as an isOwner variable) along with the user object being returned? var isOwner = false; if(user.Alias == User.Identity.Name) isOwner = true;) In my ...

"A collection of elements in Typescript that is uniform in type, denoted by

Is it possible to declare an array of type any[] where all elements are of the same type? For example: // Allowed const array1: any[] = [1, 2, 3]; const array2: any[] = ['a', 'b', 'c']; // Not allowed because it contains bot ...

Prevent HTTP using AsyncValidator when the value is empty

I have developed a custom AsyncValidator to verify the uniqueness of a userName. Inspired by this tutorial, I have implemented a delay of 500ms. However, I am facing a challenge in preventing the HTTP service call if the input value does not meet a speci ...

When you invoke a function within Angular 1.5 as a fresh component emerges

I have a component where clicking on a button triggers the invocation of another component. model.alert = function () { modalInstance = $uibModal.open({ template: '<abc-xyz return-value="model" on-cancel="Cancel()">& ...

The issue with ng-if not functioning within ng-repeat is that the ng-if directive

My issue is with using ng-if inside an ng-repeat in AngularJS. Despite updating to the latest version on 09/27/2014, I am still unable to make it work properly. The code functions perfectly outside of ng-repeat, and also works fine inside ng-repeat when us ...

Error accessing Amazon S3 through ng-file-upload due to authorization problems

After spending a considerable amount of time on this task, I seem to be stuck in a loop receiving the same error message from Amazon no matter what approach I take. <Error> <Code>InvalidArgument</Code> <Message>Unsupported Auth ...

Display a picture retrieved from a POST request using Angular and a Spring Boot backend

Recently, I've been delving into the world of HTTP requests. My latest task involves uploading an image from the client and sending it to the server using a POST request. When testing this process in Postman, everything works smoothly as I receive th ...

Using TypeScript to consolidate numerous interfaces into a single interface

I am seeking to streamline multiple interfaces into one cohesive interface called Member: interface Person { name?: { firstName?: string; lastName?: string; }; age: number; birthdate?: Date; } interface User { username: string; emai ...

Error: Code cannot be executed because the variable "sel" has not been defined in the HTML element

Every time I try to click on the div, I encounter an error message stating 'Uncaught ReferenceError: sel is not defined at HTMLDivElement.onclick' I am currently developing with Angular 8 and this error keeps popping up. I have read through simil ...

Is it possible to execute a unit test from within another unit test?

Currently, I am in the process of developing Python x Selenium unit tests specifically for testing the login feature of a particular website. One challenge I encountered is writing a test case for the "Remember Me" functionality after successfully creati ...

Is there a way to determine completion of page loading in an AngularJS application using JavaScript?

I am currently in the process of crafting Robot Framework tests to address specific use cases for an external AngularJS application. One of my requirements is the utilization of Python 3.5+ and SeleniumLibrary instead of the outdated Selenium2Library. In ...

The Best Approach for Angular Google Maps Integration

I'm diving into Angular for the first time while working on a project that requires advanced mapping functionality like clustering, routing, road routing, paths, directions, polygons, events, drawing on maps, info windows, markers, etc. After some re ...

Exploring the functionalities of the 'Angular Rails Templates' gem

Recently, I've been exploring the use of the templates folder in AngularJS instead of the traditional 'views' folders found in Rails. Everything runs smoothly when I write my code in HTML, but what if I want to utilize slim for a more conci ...

How can you annotate and inherit a class method that returns an array of itself?

In the following example, I present a simplistic representation of code that may not align with standard HTML or front-end conventions. Please excuse any confusion this may cause. TL, DR I am facing challenges in specifying a return type for a method tha ...