0

I'm trying to test a controller than requires me to mock a service that I'm using to get data. Currently I'm getting an error saying the function is undefined on this line:

dataServiceMock = jasmine.createSpyObj('dataService', ['getFunctionStuff']);

According to other examples and tutorials this should be working fine.

Here's my code including the test file, the service and the controller.

Controller:

var app = angular.module('myApp', []);

app.controller('MainCtrl', function($scope, dataService) {

    dataService.getFunctionStuff($scope.foo)
      .then(function(data) {
        $scope.test = data;
      });

});

Service:

app.factory('dataService', function ($timeout, $q){

  function getFunctionStuff(formData) {
            return $http.post('../randomAPICall', formData).then(function(data) {
                return data;
            });
        };

});

Tests:

describe('Testing a controller', function() {
  var $scope, ctrl, $timeout;

  var dataServiceMock;

  beforeEach(function (){

    dataServiceMock = jasmine.createSpyObj('dataService', ['getFunctionStuff']);

    module('myApp');

    inject(function($rootScope, $controller, $q, _$timeout_) {

      $scope = $rootScope.$new();

      dataServiceMock.getFunctionStuff.and.ReturnValue($q.when('test'));

      $timeout = _$timeout_;

      ctrl = $controller('MainCtrl', {
        $scope: $scope,
        dataService: dataServiceMock
      });
    });
  });

  it('should update test', function (){
    expect($scope.test).toEqual('test');    
  });
});

Here's a plunker of it: http://plnkr.co/edit/tBSl88RRhj56h3Oiny6S?p=preview

1
  • I have posted 2 ways to do the unit test. One is your way to create a spy, another is to use $httpBackend which is a common way. Commented Jan 21, 2015 at 6:17

2 Answers 2

1

As you are using jasmine 2.1, the API is .and.returnValue. And in your test spec, do $scope.$apply() before then

describe('Testing a controller', function () {
    var $scope, ctrl, $timeout;

    var dataServiceMock;

    beforeEach(function () {

        dataServiceMock = jasmine.createSpyObj('dataService', ['getFunctionStuff']);

        module('myApp');

        inject(function ($rootScope, $controller, $q, _$timeout_) {

            $scope = $rootScope.$new();

            dataServiceMock.getFunctionStuff.and.returnValue($q.when('test'));

            $timeout = _$timeout_;

            ctrl = $controller('MainCtrl', {
                $scope: $scope,
                dataService: dataServiceMock
            });
        });
    });

    it('should update test', function () {
        $scope.$apply();
        expect($scope.test).toEqual('test');
    });
});
Sign up to request clarification or add additional context in comments.

2 Comments

Can you explain why I need to use $scope.$apply()? I've tried it and its working great, I'd just like to understand.
@Blake - you need to propagate promise resolution to 'then' functions using $apply(). refer to angular $q document
0

Here is another common way to test $http by $httpBackend:

app.js

var app = angular.module('myApp', []);

app.controller('MainCtrl', function($scope, dataService) {

    dataService.getFunctionStuff($scope.foo)
      .then(function(data) {
        $scope.test = data.data;
      });

});

dataService.js

app.factory('dataService', function($http) {

  function getFunctionStuff(formData) {
    return $http.post('../randomAPICall', formData).then(function(data) {
      return data;
    });
  }

  return {
    getFunctionStuff: getFunctionStuff
  };

});

specs.js

describe('Testing a controller', function() {
  var $scope, ctrl, $controller, $httpBackend;

  beforeEach(function (){

    module('myApp');

    inject(function($injector) {

      $httpBackend = $injector.get('$httpBackend');
      $scope = $injector.get('$rootScope').$new();
      $controller = $injector.get('$controller');

      $scope.foo = 'foo';
      $httpBackend.expectPOST('../randomAPICall', 'foo').respond(201, 'test');
      ctrl = $controller('MainCtrl', {$scope: $scope});
    });

  });

  it('should update test', function (){
    expect($scope.test).not.toBeDefined();
    $httpBackend.flush();
    expect($scope.test).toEqual('test');    
  });
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.