AngularJS - How to test if a function is called from within another function?

28,882

Solution 1

Your addNew method is calling fakeFunction. However, it is not calling controller.fakeFunction, which is what your expectation is.

You'll need to change your code to use your controller, rather than these independent functions.

EDIT: You also need to not spy on your addNew function. This is causing the function to be replaced with a spy. The other alternative is to change it to:

spyOn(controller, 'addNew').and.callThrough()

Solution 2

I just came across this problem myself. The previous answer by @Vadim has the right principles but I don't think everything was very clear. In my case, I am trying to call a public function on a service from within another function. Here are the relevant snippets:

Service:

angular.module('myApp').factory('myService', function() {

    function doSomething() {
      service.publicMethod();
    }

    function publicMethod(){
      // Do stuff
    }

    var service = {
      publicMethod: publicMethod
    };

    return service;
});

Test:

it('calls the public method when doing something', function(){
  spyOn(service, 'publicMethod');

  // Run stuff to trigger doSomething()

  expect(service.publicMethod).toHaveBeenCalled();
});

The key here is that the function being tested needs to be calling the same reference as the public function that is being spy'd on.

Share:
28,882
Raphael Rafatpanah
Author by

Raphael Rafatpanah

If you wish to make apple pie from scratch, you must first create the universe. -- Carl Sagan

Updated on July 09, 2022

Comments

  • Raphael Rafatpanah
    Raphael Rafatpanah almost 2 years

    I'm trying to get started with karma-jasmine and I'm wondering why this test fails:

    it("should call fakeFunction", function() {
        spyOn(controller, 'addNew');
        spyOn(controller, 'fakeFunction');
        controller.addNew();
        expect(controller.fakeFunction).toHaveBeenCalled();
    });
    

    In my controller that I've previously set up for this test I have the following:

    function addNew() {
        fakeFunction(3);
    }
    
    function fakeFunction(number) {
        return number;
    }
    

    both addNew and fakeFunction are exposed using:

    vm.addNew = addNew;
    vm.fakeFunction = fakeFunction;
    

    The test, however, fails with the following:

    Expected spy fakeFunction to have been called.

    I can make the test pass if I call the function from within my test. I was hoping, however, I could test if fakeFunction was called by another function. What is the proper way to achieve this?

    Update:

    //test.js
    
    beforeEach(function() {
    
        module("app");
    
        inject(function(_$rootScope_, $controller) {
    
            $scope = _$rootScope_.$new();
            controller = $controller("CreateInvoiceController", {$scope: $scope});
    
        });
    
    });
    

    If I test something like:

    it('should say hello', function() {
        expect(controller.message).toBe('Hello');
    });
    

    The test passes if I put the following in my controller:

    var vm = this;
    vm.message = 'Hello';
    

    I just want to know how I can test if a public function was called from another function.

  • Raphael Rafatpanah
    Raphael Rafatpanah over 9 years
    I've tried calling vm.fakeFunction and it does not work either. How exactly is it done?
  • Vadim
    Vadim over 9 years
    @RaphaelRafatpanah I don't know what vm is, but that's not what you're testing, you're testing that controller.fakeFunction was called
  • Raphael Rafatpanah
    Raphael Rafatpanah over 9 years
    Sorry for not clarifying. vm is a common variable for capturing this in Angular controllers. In effect, vm and controller should both be referring to this.
  • Vadim
    Vadim over 9 years
    @RaphaelRafatpanah maybe you can post more code, how is your controller variable in the test set up for example? What does your controller look like?
  • Charlesliam
    Charlesliam over 7 years
    vm is short name for ViewModel. usually use as var vm = this;
  • pushkin
    pushkin over 3 years
    What if you're in a NodeJS context and have a bunch of exported functions outside of a class (so there's no controller)? Using module.exports.exportedFunction works but then I lose all the typing information for the function. Do you know of a better way?