Evaluating the use of promise in Angular using Jasmine testing

I'm currently troubleshooting whether a method with a promise is being properly called

Below is the snippet of my controller code:

app.controller('StoresListController', function ($scope, StoresService) {

    $scope.getStores = function () {
        StoresService.getStores().then(function (data) {
            $scope.stores = data.data;
        });
    };
    $scope.getStores();

    $scope.deleteStore = function (id) {
        StoresService.deleteStore(id).then(function () {
            $scope.getStores();
        });

    };
})

And this is the testing script I have written:

beforeEach(inject(function($rootScope, $controller, $q) {
        rootScope = $rootScope;
        scope = $rootScope.$new();
        controller = $controller;

        serviceMock = {
            getStores: function(){
                // mock promise
                var deferred = $q.defer();
                deferred.resolve({data : 'foo'});
               return deferred.promise;
            },
            deleteStore : function(){
                var deferred = $q.defer();
                deferred.resolve({data : 'foo'});
                return deferred.promise;
            }
        }
        spyOn(serviceMock,'getStores').and.callThrough();
        controller("StoresListController", {$scope: scope, StoresService: serviceMock});      

    }));  

    it('should call scope.getStores', function(){
        scope.$digest();
        expect(scope.getStores).toHaveBeenCalled()
    });
    it('should call scope.getStores afeter scope.deleteStore', function(){
        scope.deleteStore(1)
        scope.$digest();
        expect(scope.getStores.call.count).toBe(2)
    });

});

I encountered an error message stating "Expected a spy, but got Function." during the first test, and the second one failed as well. Can you help identify what may be causing these issues?

Answer №1

This issue arises because the Spy is added to the serviceMock object instead of being attached to the StoresService. Additionally, you have the option to use the callFake method on the spy in order to simulate the service.

// It's important to note that the StoresService needs to be injected here
beforeEach(inject(function($rootScope, $controller, $q, StoresService) {
    rootScope = $rootScope;
    scope = $rootScope.$new();
    controller = $controller;

    serviceMock = {
        getStores: function() {
            // mocking the promise
            var deferred = $q.defer();
            deferred.resolve({ data: 'foo' });
            return deferred.promise;
        },
        deleteStore: function() {
            var deferred = $q.defer();
            deferred.resolve ({ data: 'foo' });
            return deferred.promise;
        }
    }
    // Creating a Spy for the getStores method
    spyOn(StoresService, 'getStores').and.callFake(serviceMock.getStores);
    // Creating a Spy for the deleteStore method 
    spyOn(StoresService, 'deleteStore').and.callFake(serviceMock.deleteStore);
    // Eliminated the local injection of StoresService
    controller("StoresListController", { $scope: scope });

}));

it('should invoke scope.getStores', function() {
    scope.$digest();
    expect(scope.getStores).toHaveBeenCalled()
});
it('should invoke scope.getStores after invoking scope.deleteStore', function() {
    scope.deleteStore(1)
    scope.$digest();
    expect(scope.getStores.call.count).toBe(2)
});

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 authorizing AJAX call to Gmail API

I'm just getting started with the GMail API and I'm attempting to use AJAX to fetch emails. This is my code: $.ajax({ beforeSend: function (request) { request.setRequestHeader("authorization", "Bearer xxxxxxxxxxxxxxxxx.a ...

Is there a way to generate and transmit a text file using XmlHttpRequest or $.ajax?

The server is anticipating an html or txt file to be sent from a form named "websitetopdf". The file is dynamically created on client javascript and should only function properly on Chrome. Below is the form that needs to be used for sending: <form ac ...

What is the best approach for incorporating sub-navigation within a page popup in a Next.js project?

In the midst of my Next.js project, there is a requirement to showcase a chat popup consisting of multiple sub-pages like user registration, conversation page, and more. Hence, seamless navigation inside this popup is necessary. The idea is not to alter th ...

Executing an xajax/ javascript function simultaneously

Is there a way to simultaneously execute the same function? Here is an example: function convert_points() { show_loading(); xajax_ConvertPoints(); xajax_GetRegularGamingCards(); } When xajax_ConvertPoints is called, there seems to be a mill ...

What could be the reason for the electron defaultPath failing to open the specified directory?

Hi, I'm experiencing difficulties opening the directory path (/home/userxyz/Releases/alpha) using electron. This is what I have attempted: I have a similar path on Ubuntu: /home/userxyz/Releases/alpha When trying to access this path with the fo ...

Transforming the unmanaged value state of Select into a controlled one by altering the component

I am currently working on creating an edit form to update data from a database based on its ID. Here is the code snippet I have been using: import React, {FormEvent, useEffect, useState} from "react"; import TextField from "@material ...

Divide an HTML file into separate documents upon submitting a form

Is there a way to input HTML into a text area, then upon submission, have the system read the file, identify the class selector, and separate the HTML into multiple files saved in a directory? If you have any thoughts on how this could be accomplished, pl ...

What are some solutions for resolving an infinite loop of axios requests?

I am currently in the process of developing a web app for Spotify using its API. I have encountered an issue where the handleClick function in Albums.js is being called repeatedly when trying to make a get request for specific artist album data. Could this ...

The controller in AngularJS fails to function properly after the initial page refresh

I am currently utilizing AngularJS in my hybrid Ionic application. Here is my controller: .controller('SubmitCtrl', function($scope) { console.log("It only works when the page is refreshed!"); }); The console.log function runs perfectly fine ...

What is the method to design a file upload feature without a specific form using JavaScript?

I am currently working on a form that handles file uploads using jQuery and AJAX. The goal is to upload the files individually as JSON containing base64 data. Rather than uploading all the images at once, I want each image to be treated as if it were in it ...

Permanently remove the span tag and any associated text from the HTML document

Currently, I am working on a project that involves numerous page numbers marked using the span class. Below is an example of this markup: <p>Cillacepro di to tem endelias eaquunto maximint eostrum eos dolorit et laboria estiati buscia ditiatiis il ...

A numerical input field that removes non-numeric characters without removing existing numbers?

Currently, I have implemented the following code: <input type="text" onkeyup="this.value = this.value.replace(/\D/g, '')"> This code restricts users from entering anything other than numbers into a field on my webpage. However, I hav ...

The data retrieved from the API call is outdated

I am struggling with a weather web API that is only showing old data when called in the code. However, when I enter the API URL directly into the browser, it displays the most up-to-date information for the current city. Can anyone help me troubleshoot why ...

Issue with Ref when used in a distinct HTML template

I have encountered a frustrating issue with my simple Vue project. When I separate the template and code into individual files, the ref stops working and I end up with an undefined value in the HTML template. This scenario works fine: map.component.vue ...

A guide on effectively utilizing ref forwarding in compound component typing

I am currently working on customizing the tab components in Chakra-ui. As per their documentation, it needs to be enclosed within React.forwardRef because they utilize cloneElement to internally pass state. However, TypeScript is throwing an error: [tsserv ...

Integrate incoming websocket information with state management in React

I am facing a challenge in creating a timeseries plot with data obtained from a websocket connection. The issue arises when new values overwrite the previously stored value in the state variable. const [chartData, setChartData] = React.useState(null) Cu ...

Exploring the mechanics of callbacks in jQuery's Ajax functionality

Greetings fellow developers! I've embarked on a new programming project with the firm intention of writing cleaner and more easily maintainable code than ever before. However, I have encountered my old nemesis - jQuery AJAX returns. The last time I t ...

There is an issue with the function of elem.autocomplete - it is not recognized

I'm new to Angular.JS and have been struggling for the past 6 hours. I've been working on reading data from an HTTP source and displaying it as an autocomplete feature on the view. Previously, the data was displayed in a select box, but I decide ...

I'm facing some difficulties in sourcing my header into a component and encountering errors. Can anyone advise me on how to properly outsource my header?

Looking to streamline my headers in my Ionic 4 project by creating a separate reusable component for them. Here's how I set up my dashboard page with the header component: <ion-header> <app-header title="Dashboard"></app-he ...

Using the Ionic framework to transfer data from a controller variable to a page

Hey there! I'm currently working on a hybrid app using the Ionic Framework, but I believe my error lies more within Angular. Here's the code snippet that's giving me trouble: <ion-view class="back" ng-controller="webCtrl" view-title="{{ ...