Angulajs: The correct way to simulate a Promise received from $http

Seeking guidance after struggling with unit testing an angular service, specifically the failed part of a promise.

(function () {
angular.module('testable')
    .factory('myService', ["$http", "$q", function ($http, $q) {
        return {
            createThing: function(thing) {
                return $http.post("//...", thing)
                        .then(function (response) {
                            return response.data;
                        }, function (response) {
                            return $q.reject(response.statusText);
                        });
            }
        };
    }]);
}());

Despite reviewing multiple resources on Stack Overflow, I am unable to successfully test a rejected promise. I have attempted various approaches but none have produced the desired outcome. Here is the current setup:

    beforeEach(inject(function ($injector, myService) {
        sut = myService;
        $httpBackend = $injector.get('$httpBackend');
        $q = $injector.get('$q');
        $rootScope = $injector.get('$rootScope');
        dataToSend = "send me!";
        deferred = $q.defer();
    }));

    it("should handle error appropriately", function () {
        var result,
            expected = "FAIL!!!!!";

        deferred.reject(expected);
        $httpBackend.expectPOST(testUrl, dataToSend).respond(deferred.promise);

        sut.createThing(dataToSend).then(function (returnFromPromise) {
            result = returnFromPromise;
        });

        $rootScope.$apply();
        $httpBackend.flush();

        // Assertion goes here but result is consistently incorrect
    });

Understanding the asynchronous nature of promises, I've pieced together information from various sources. However, I welcome any advice or suggestions on how to effectively complete this unit test.

Answer №1

Utilizing the $httpBackend service eliminates the necessity of dealing with promises to mimic HTTP requests. The respond() method is structured as follows:

respond: function(status, data, headers, statusText) { 
  ...
}

To simulate an error, simply return a non-success status code (!= 200):

it('should not create a thing', function() {
    var result;

    // Arrange
    $httpBackend.expectPOST('https://domain.com/api')
      .respond(500, null, null, 'some error');

    // Act
    myService.createThing('foo').then(angular.noop, function(data) {
      result = data;
    });

    $httpBackend.flush();

    // Assert
    expect(result).toBe('some error');
  });

The comprehensive spec below covers both success and error scenarios:

describe('Testing a factory', function() {
  var myService, $httpBackend;

  beforeEach(function() {
    module('plunker');

    inject(function(_$httpBackend_, _myService_) {
      $httpBackend = _$httpBackend_;
      myService = _myService_;
    })
  });

  afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
  });

  it('should create a thing', function() {
    var result;

    // Arrange
    $httpBackend.expectPOST('https://domain.com/api')
  .respond(200, 'some data');

    // Act
    myService.createThing('foo').then(function(data) {
      result = data;
    });

    $httpBackend.flush();

    // Assert
    expect(result).toBe('some data');
  });

  it('should not create a thing', function() {
    var result;

    // Arrange
    $httpBackend.expectPOST('https://domain.com/api')
  .respond(500, null, null, 'some error');

    // Act
    myService.createThing('foo').then(angular.noop, function(data) {
      result = data;
    });

    $httpBackend.flush();

    // Assert
    expect(result).toBe('some error');
  });
});

Working Plunker

No need to execute $rootScope.$apply() as no digest cycle is required for updates.

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

Is there a way to eliminate the black seam that is visible on my floor mesh that has been separated? I am utilizing A

I recently imported a large .glb file into AFrame. The model has baked textures and the floor is divided into multiple mesh parts for improved resolution. However, I am facing an issue where black seams appear on the separated parts of the floor, only dis ...

AngularJS utilizes the trustAsHtml function to enable the inclusion of specific portions of a string

In the database, I have notification data that reads: Someone has recently interacted with your post. However, I need to display the username in bold using trustAsHtml. But what if the username is: alert('xss'); This would result in the outpu ...

Effective strategies for organizing component features in React

As I was reading through the React documentation, I came across the idea that using React effectively involves following the Single Responsibility Principle, meaning each component should have a specific purpose. I've already created a basic Gameboard ...

Is it feasible to update just one key within an exported type using setState in TypeScript?

Recently, I decided to dive into Typescript within my React App and encountered an error that has left me stumped. I'm wondering if it's possible to access a specific key in an exported type and modify its value? Here is how I've exported ...

Encountered a CastError in Mongoose when trying to cast the value "Object" to a string

I am struggling with a Mongoose CastError issue within my Node.js API. The problem arises at a specific route where data is being returned appended with some additional information. Despite finding various solutions for similar problems, my scenario seems ...

Guide on how to efficiently navigate and extract data from a (local) XML file using Prototype JS

I'm currently working on a project that already utilizes PrototypeJS and I need to develop a module for it. Here's what I have: - An XML file containing the necessary information Here's what I'm aiming for: - A basic div that showcase ...

The functionality of the delete button in Datatables is only operational on the initial page, failing to

My datatable is giving me trouble. The delete button in the action column only works on the first page, but not on the other pages. Here is the code for my delete button: <table id="example" class="table table-striped table-bordered" cellspacing="0" wi ...

How can one transform an array (in the form of a JSON array) into a map?

After running my script, I receive an array (JSON Array) structured like this: [{redirectCount=0, encodedBodySize=60962, unloadEventEnd=0, responseEnd=1601.699999999255, domainLookupEnd=995.7999999896856, ... Now, I want to display the key-value pairs fr ...

Add data to a DataTable without the need to manually refresh the page

I am looking to dynamically append a DataTable on my webpage. The goal is to have a form that users can fill out multiple times and each time they submit their inputs, the results will be added to the DataTable for display. <table id="table" class="di ...

Issue with Vue JS: e.preventDefault not functioning correctly when using Axios

I am facing an issue in my Laravel project where I have implemented a method in the Vue instance to validate resource availability upon form submission. The validation is done through an AJAX call using axios, and if any resources are unavailable, I receiv ...

What is the best way to incorporate Vuetify into a new application?

Recently, I developed a new app using Vue and I decided to integrate Vuetify as the framework. After executing the npm install vuetify --save command, Vuetify was added to the app successfully. However, when I tried to use it, the CSS button colors were no ...

React Hook Form: Reset function triggers changes across all controllers on the page

I encountered an issue with using the reset function to clear my form. When I invoke the reset function, both of my form selects, which are wrapped by a Controller, get cleared even though I only defined default value for one of them. Is there a way to pr ...

Learn how to automatically set the checked state of a radio button by toggling another radio button

I have two sets of radio buttons, each with four options. The first set has no default selection (unchecked) using jQuery, while the second set has the first option checked by default. What I'm looking to achieve is that when a radio button in the f ...

Updating the $_GET parameter using JQuery when a button is clicked

I'm working on implementing a feature where a series of buttons can update the $_GET['year'] variable on my website. These buttons should function across different pages within my website, such as: example.com example.com/?search=foo exa ...

My Gatsby website is being rendered in its HTML form on Netlify

The website build is located at . It appears that the javascript functionality is not working, and only the html version (usually meant for search engines) is being displayed. It seems like this issue is only affecting the home page. You can check out the ...

Switching between different elements in an array using React

I've got a collection of appointments and I need to create a React view that will show them one by one. Users should be able to navigate through the appointments using arrow buttons. Here's an example of what the data looks like: const arr = [ ...

VUE JS - My methods are triggering without any explicit invocation from my side

I've encountered a frustrating problem with Vue JS >.<. My methods are being triggered unexpectedly. I have a button that is supposed to execute a specific method, but this method gets executed along with other methods, causing annoyance... Her ...

Utilizing AngularJS and ADAL.JS to Define Resource ID (Audience)

Is there a way to utilize adal.js within AngularJS to obtain a bearer token for the audience https://management.azure.com through JavaScript? I have created a client application in Azure AD and configured its permissions to allow access to the "Windows Az ...

"Server request with ajax did not yield a response in JSON format

http://jsfiddle.net/0cp2v9od/ Can anyone help me figure out what's wrong with my code? I'm unable to see my data in console.log, even though the network tab in Chrome shows that my data has been successfully retrieved. Here is my code snippet: ...

Is it possible to validate an email domain using node.js? For example, checking if gmail.com is a valid email domain, and ensuring that users do not enter incorrect variations such as egmail.com

I've been working with React.js and Express.js/Node.js, utilizing nodemailer for sending emails. However, I've noticed that a lot of emails are coming in with incorrect domains, such as [email protected], rather than the correct ones like [e ...