Jasmine - verifying variable invocation in test function

As part of my testing process, I am exploring a scenario where a service method is called using a local variable within an angular controller.

In this particular case, when the items array is empty, a modal for creating a new item will be triggered via the modal service.

Controller:

(function() {
    'use strict';

    angular
        .module('app')
        .controller('Item', Item);

    //items is resolved through the ui-router resolve
    //items contains a array of item objects. Will be an empty array if there are no items for that user

    Item.$inject = ['items', 'modalService'];

    function Item(items, modalService) {
        var vm = this;
        vm.items = items;
        vm.newItemModal = modalService.newItemModal;

        if (vm.items !== undefined) {
            if (vm.items.length === 0) {
                vm.newItemModal();
            }
        }
    }

})();

vm.newItemModal() triggers the display of the new item modal. But how can I test this scenario using Jasmine?

Current Test Implementation:

describe('Controller: Item', function(){
    var scope, 
    ctrl, 
    items, 
    modalService,
    mockItems = [{ name: 'item1', desc:'desc1'}, { name: 'item2', desc:'desc2'}];

    //mocking the modalService
    beforeEach(function(){
        module(function($provide){
            modalService = {
                newItemModal: function(){
                    return;
                }
            };
            $provide.value('modalService', modalService);
        });
    });

    beforeEach(inject(function(_$rootScope_, $controller) {
        $rootScope = _$rootScope_;
        scope = $rootScope.$new();
        ctrl = $controller('Item as item', {
            $scope: scope,
            items: mockItems
        });
    }));

    it('should verify the vm object', function(){
        expect(scope.item.newItemModal).toBeDefined();
        expect(scope.item.items).toEqual(mockItems);
    });

    //Separate test-suite as items is initialised with an empty array
    describe('new item modal', function(){
        beforeEach(inject(function(_$rootScope_, $controller) {
        $rootScope = _$rootScope_;
        scope = $rootScope.$new();
        ctrl = $controller('Item as item', {
            $scope: scope,
            items: []
        });

        it('should open a new item modal', function(){
            //returns 0
            console.log('Items length', scope.items.length);
            spyOn(scope.item, 'newItemModal').and.callThrough();
            //testing this assertion fails
            expect(scope.item.newItemModal).toHaveBeenCalled();
        });
    }));
});
});

Answer №1

The issue arises when the line below is executed:

spyOn(scope.item, 'newItemModal').and.callThrough();

By this point, the controller has already been created and its code has been run.

To resolve this, you must set up your spy before creating the controller.

For instance:

var createController;

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

  createController = function() {
    $controller('Item as item', {
      $scope: scope,
      items: []
    });
  };
}));

it('should trigger a new item modal', function() {
  spyOn(modalService, 'newItemModal').and.callThrough();
  createController();
  expect(scope.item.newItemModal).toHaveBeenCalled();
});

Keep in mind that you cannot spy on scope.item since it is not created until the controller is instantiated; therefore, you need to spy on the modalService instead.

See Demo: http://plnkr.co/edit/y0vzfaqDSuuCuPVwdybq?p=preview

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

Tips for preventing the inner surface from appearing transparent in WebGL

I am working with the code snippet provided below. The issue I am currently facing is that one side of the partial sphere is non-transparent, while the other side remains transparent. How should I modify the code to make both sides non-transparent? Thank y ...

There was an issue with Angular 2.0 at :0:0, which was caused by a response with status: 0 from the URL: null

I am a beginner in Angular 2.0 and I am currently working on creating a sample application using @angular\cli. To serve the application on my local machine, I use the command ng serve --open, which opens it at localhost:4200. Now, I have developed a ...

Utilizing Various Styles within a single Google Sheets Cell

In Cell A1, I have a challenge where I need to merge 4 different arrays of names, separated by commas. Each name should have its own styling based on the array it belongs to - red for array1, green for array2, orange for array3, and gray with a strike-th ...

Is it possible to trigger a bootstrap modal-dialog without specifying an ID or class using JQuery or JavaScript?

Is there a way to work with Bootstrap modal-dialog without setting an id or class, perhaps using JQuery or JavaScript instead? <html> <head> <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstr ...

Tips for running a post-installation script when deploying an Angular 4 application to an Azure website using KuduScript

I recently faced an issue with my angular4 app deployed on azure web app (website) using Deployment Options through the Portal. To customize the deployment script, I utilized kuduscript [kuduscript -y –node] and modified deploy.cmd to specify how Azure s ...

Using the power of HTML5, store data locally on

I am curious about the possibility of using local storage on a different page. I want to track clicks made on one page and display it on another, but I'm not sure how to go about it or if it's even feasible. Any help you can provide would be grea ...

Creating a dropdown navigation menu using jQuery

I have been experimenting with creating a customized Drop Down menu using ul li and Jquery, following this helpful Tutorial. Here is the sample HTML Code for testing: <div id="dd" class="wrapper-dropdown-3" tabindex="1"> <span>OS</span> ...

I am having trouble with updating an array in React useState. Every time I try to make changes, it keeps reverting back to the initial state. What could

My current task involves managing a state array called monthlyIncidents, which contains 12 numbers that need to be updated under certain conditions. I attempted to update the elements by copying the array, modifying the specific element, and then updating ...

Tips for aligning the camera to ensure the object maintains a consistent pixel width and height on the screen

I am grappling with a challenge that I'm unsure how to approach, hoping someone can offer me a clue on the solution. My goal is to position the camera at a specific z index so that a cube appears on the screen with consistent pixel dimensions regardl ...

Employ the $scope.go() method within a function written in JavaScript

I am working with some HTML code that looks like this <body ng-app="app" ng-controller="IndexCtrl" id="indexBody"> <h1>test</h1> </body> Next, in a JavaScript function, I retrieve the scope like so function myFx() { ...

Modify a single parameter of an element in a Map

Imagine I have a map data type exampleMap: Map<string, any> The key in the map is always a string, and the corresponding value is an object. This object might look like this: { name: 'sampleName', age: 30} Now, let's say the user se ...

Tips for dynamically passing a string into a Javascript function

Here is a JavaScript function that I have: function doIt(link) { alert(link); } I am using this function in the following JavaScript code snippet. Here, I am dynamically creating an anchor tag and appending it to my HTML page: jQuery.each(data, func ...

Utilize jQuery's animate method to scroll to the top after toggling a class in Vue

I am in the process of developing a FAQ page using vue.js Here is what I have implemented so far: <li v-for="i in items | searchFor searchString" v-on:click="toggleCollapse(i)" :class="{ collapsed: i.collapse, expanded: !i.collapse }" > <p> ...

The Ajax request is failing to execute properly on newly appended elements

My comment system allows users to post comments which are then appended to the existing ones. Whenever a user hovers over a comment, a 'delete' button appears. Clicking on the delete button prompts a confirm dialog and upon confirmation, an ajax ...

Troubleshooting the error "The 'listener' argument must be a function" in Node.js HTTP applications

I'm facing an issue resolving this error in my code. It works perfectly fine on my local environment, but once it reaches the 'http.get' call, it keeps throwing the error: "listener argument must be a function." Both Nodejs versions are iden ...

"Troubrella: Attempting to inject one controller into another resulted in an error

I have a controller named A that I am trying to inject into all my other controllers. Controller A communicates with a factory that performs authentication services and interacts with the database. The factory, named myFactoryServices.js, is included in m ...

What to do when VueJs computed method throws an error: "Cannot access property 'words' of undefined"?

Here is some pseudo code that I've written: var vm = new Vue({ el: '#test', data: { words: [{name:'1'}, {name:'2'}, {name:'3'},{name:'4'},{name:'5'},{name:'6'}], } ...

Click the link to capture the ID and store it in a variable

Currently working on a project involving HTML and JavaScript. Two HTML pages ("people.html" and "profile.html") are being used along with one JavaScript file ("people.js") that contains an object with a list of names, each assigned a unique ID: var person ...

Cordova launching a webpage in mobile browser

Hey, I'm encountering a problem. I have customized my CordovaWebView.java class so that when I press the device back button, the application prompts me to close it. public boolean backHistory() { // Check webview first to see if there is a h ...

Problem with resizing in CSS and jQuery

I need help creating a chatbox that can be resized. I've almost got it, but the bottom part of the chatbox detaches when I resize it. Also, I'm having trouble making the userList a fixed size that won't be affected by resizing. Check out th ...