Anuj K S
Anuj K S

Reputation: 1

angular js unit test using jasmine for a resource based factory in controller

I am new in writing unit test case using jasmine. I am trying to test a resource based service in a controller fetching real data from server but not getting the desired results. Please provide your inputs where I am doing wrong. Below is the unit test I have written : describe('NewStarterController', function() { beforeEach(module('myApp')); var scope, ctrl, $resource;

       var context = 'http://localhost.com:8085/AngularPrototype/home';

       beforeEach(inject(function(_$resource_, $rootScope, $controller) {
              $resource = _$resource_;

              NewStarter = {
                     getNSData : function() {
                           return $resource(context + '/selfservice/newstarter/get', {}, {
                                  get : {
                                         method : 'GET'
                                  }
                           });
                     }
              };
              scope = $rootScope.$new();

              $controller('NewStarterController', {
                     $scope : scope,
                     NewStarter : NewStarter
              });

       }));

       it('should test new starter data', function() {

              NewStarter.getNSData().get({}).$promise.then(function(response) {
                     scope.refData = response;
                     scope.data();

              });


              expect(scope.data.length).toEqual(2);

              expect(NewStarter.getNSData().get.length).toEqual(2);

              expect(NewStarter.getNSData().get()).toEqual('formTitleNode');

       });

}); 



Below is the jasmine test result ::

Jasmine 1.3.1 revision 1354556913finished in 0.183s
•   
No try/catchFailing 1 spec1 spec | 1 failing
NewStarterController
should test new starter data
NewStarterController should test new starter data.
Expected 0 to equal 2.
Expected 4 to equal 2.
Expected { $promise : { $$state : { status : 0 } }, $resolved : false } to equal 'formTitleNode'.





get.json      ->   represents the data that is returned from server(Data returned from RestController) on hitting the url : http://localhost.com:8085/AngularPrototype/home/selfservice/newstarter/get

SpecRunner.html->  is used to run the jasmine unit test. Below are the contents of this file :
<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Jasmine Demo</title>

    <script>
        libraries here
    </script>
</head>
<body>
<!-- Include Angular and Angular Mocks-->
<script>libraries here </script>
    <!-- Include your source files here... -->
<script src="services/services.js"></script>
<script src="myApp.js"></script>
<script src="controller/selfservice/NewStarterController.js"></script>

<!-- Include your spec files here... -->
<script src="jasmineunittest/NewStarterControllerTest.js"></script>


<!-- Jasmine execution script (Standard Jasmine Bolierplate)-->
<script type="text/javascript">
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.execute();

</script>

</body>
</html> 


Below is the controller code ( NewStarterController.js ) which is unit tested : 

myApp.controller("NewStarterController", ['$scope','$modal', '$state', '$sce', 'NewStarter', function($scope,$modal, $state, $sce, NewStarter) {


       $scope.data = function() {
              NewStarter.getNSData().get({}, function(response) {
                     $scope.refData = response;
                     $scope.formTitle = [];
                     $scope.displayOptions = [];
                     angular.forEach($scope.refData.formTitleNode, function(contentItem)
                     {
                           var content = -1;
                           var title = -1;
                           angular.forEach(contentItem.fieldList, function(fieldBean)
                           {
                                  if(fieldBean.fieldName == 'TITLE')
                                  {
                                         title = fieldBean.fieldValue;

                                  }
                                  if(fieldBean.fieldName == 'FORMATTEDCONTENTS')
                                  {
                                         content = fieldBean.fieldValue;

                                  }
                           })
                           $scope.formTitle.push({title:title, content:content});
                     })

                     angular.forEach($scope.refData.displayOptionsNode, function(contentItem)
                     {

                           var nsContent = -1;
                           var nsTitle = -1;
                           angular.forEach(contentItem.fieldList, function(fieldBean)
                           {
                                  if(fieldBean.fieldName == 'TITLE')
                                  {
                                         nsTitle = fieldBean.fieldValue;

                                  }
                                  if(fieldBean.fieldName == 'FORMATTEDCONTENTS')
                                  {
                                         nsContent = fieldBean.fieldValue;

                                  }
                           })
                           $scope.displayOptions.push({nsTitle:nsTitle, nsContent:nsContent});
                           $scope.selectedOption = '';
                     })

                     $scope.hasNewStarterSubmitted = $scope.refData.hasNewStarterSubmitted;
                     $scope.submittedSelectedOption = $scope.refData.submittedSelectedOption;
                     $scope.submittedStudentLoan = $scope.refData.submittedStudentLoan;
                     $scope.nsAlreadySubmittedValidationMsg = $scope.refData.nsAlreadySubmittedValidationMsg;



                     console.log('Success', response);
              }, function(error) {
                     //alert(JSON.stringfy(error))
                     $scope.contentLoadErrorMsg=error.data;
                     //console.error('ERR', error);
              });
       }
}]);





Below is the code in services.js

myApp.factory('NewStarter', function($resource, context) {
       return {
              getNSData: function() {
                     return $resource(context + '/selfservice/newstarter/get', {}, {get: {method:'GET'}});
              },
              submitNSForm: function() {
                     return $resource(context + '/selfservice/newstarter/submit', {}, {submit: {method:'POST'}});
              }

       }
});


Below is the code in myApp.js 

var myApp = angular.module('myApp', ['ngResource', 'ngSanitize', 'ui.bootstrap', 'ui.router']);

myApp.constant('context', '/AngularPrototype/home');
myApp.constant('projectName', '/AngularPrototype');
myApp.config(function($stateProvider, $urlRouterProvider, projectName) {
$urlRouterProvider.otherwise('/home');

$stateProvider .state('newStarter', {
                     url:'/newstarter',
                     templateUrl: projectName + '/newstarter',
                     controller: 'NewStarterController'
              })
});


 Thanks!  



Below i tried as per your suggestion but getting error while running the unit test:
describe('NewStarterController', function() { 
beforeEach(module('myApp')); 
var scope, postDefer,NewStarter;



       beforeEach(inject(function(_NewStarter_, $rootScope, $controller,$q) {
              NewStarter = _NewStarter_;
             postDefer = $q.defer();
             spyOn(NewStarter,'getNSData').and.returnValue(postDefer.promise);
              scope = $rootScope.$new();

              $controller('NewStarterController', {
                     $scope : scope,
                     NewStarter : NewStarter
              });

       }));

       it('should test new starter data', function() {

              NewStarter.getNSData().get({});
              postDefer.resolve();
              scope.$apply();

              expect(scope.data.length).toEqual(2);

       });

}); 


Below are the errors while running the test :

Error:[$injector:unpr] Unknown provider: NewStarterProvider <- NewStarter
typeError: Unable to get property 'getNSData' of undefined or null reference

Upvotes: 0

Views: 618

Answers (1)

sdfacre
sdfacre

Reputation: 1273

Since it is a unit test, you need to focus on testing the single unit, you should try to mock all dependencies as much as possible. Having say that, using real $resource in your unit test is not desirable.

What I think you should do is: - 1. Mock NewStarter.getNSData(), or whatever service methods get called in controller 2. Return a fake resource object 3. mock get method of the fake resource (as it is what's used in your controller) and return a promise 4. resolve the promise to test your success handler.

Check out this plunker http://plnkr.co/edit/8l3mY1?p=preview

var  mockResource = {get: function(){}}, $q;
beforeEach(inject(function($rootScope, $controller, _$q_, NewStarter) {
  $scope = $rootScope.$new();
  $q = _$q_;
  spyOn(NewStarter, 'getNSData').and.callFake(function() {
     return mockResource;
  });

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

it('should return value', function() {
  var responseObj = {id: 1, name:'test'};
  spyOn(mockResource, 'get').and.callFake(function(data, func){
      var deferred = $q.defer();
      deferred.resolve(responseObj);
      return deferred.promise.then(func);
    });

  $scope.data();

  expect(mockResource.get).toHaveBeenCalled();  
  $scope.$apply();

  expect($scope.refData).toEqual(responseObj);
});

Upvotes: 0

Related Questions