2

Updated : In this controller I am trying to test the user login process and it calls loginRequest method in WebService to send out user credentials as a login message. A loginreply message is received by the controller.If login process is successful a URL redirection should happen.I have used jasmine spy objects to mock the WebService functionality.

There were some questions along similar lines and they did help me somewhat to narrow down the problem scope. But I am still facing some problems in getting this unit test function properly.so I decided to post an Update to my initial question.

Useful Stack Overflow Questions

Useful external resources

But I am currently having a problem evaluating the received message status and evaluating URL redirection process.How do I test that the scope is populated after the broadcast ($scope.msg)? This works perfectly in the app itself, just don't know how to test it.

I have a controller with an event listener in my Angular app, defined as follows.

AngularJS code

Apps.controller('loginCtrl',['$scope','$location','WebService','$timeout','$log','$http',
    function($scope,$location,WebService,$timeout,$log,$http)
    {
        $scope.login = function()
        {
            var loginMsg = {};
            loginMsg.UserName = $scope.username;
            loginMsg.Password = $scope.password;
            WebService.loginRequest($location.host(),$location.port(),"Check","'?'",loginMsg);
        }

      //Error attempting to access msg in Unit test
        $scope.$on('LSuccess_msg',function(e,msg)
       {      
      //These variables can be accessed in the Unit test    
        $scope.myEventCalled = true;
        $scope.response = msg;    

        if(angular.equals(msg.Status,"LOGIN_SUCCESS"))
       {
         $timeout(function ()
        {
       //Need to test this
         window.location.href = "http://"+<--custom redirection URL is added here-->;}, 5);
            }
        });
    }]);

Unit test used to test controller

    "use strict";

    describe("Controller : Login controller", function() {
    var $scope, ctrl, $location, $timeout, WebService, httpBackend, msgdata;

    beforeEach(module('Apps'));
    beforeEach(inject(function($rootScope, $controller, _$timeout_, _$location_, $httpBackend){
          $scope    =  $rootScope.$new();
          $timeout  = _$timeout_;
          $location = _$location_;
          httpBackend = $httpBackend;

          var loginMsg={};//populate message

              msgdata={EMail:"v@v.com",FullName:"Viranga Indira",SessionId:"1114335860", Status:"LOGIN_SUCCESS", 
          UserID:"c7232d47",_msgType:"com.ust.sharedmsgs.LoginReply_msg", 
                  };

          WebService = jasmine.createSpyObj(WebService, ["loginRequest"]);
                  WebService.loginRequest($location.host(),$location.port(),"Check","'?'",loginMsg);

          ctrl = $controller('loginCtrl',{
          $scope: $scope,
          $location: $location,
          WebService: WebService,
          $timeout: $timeout
          });
          $scope.$digest();
    }));

    describe("Controller : Login controller", function(){

        it("creates spies for the requested Service", function() {
            expect(WebService.loginRequest).toBeDefined();
        });

        it("tracks that the spies were called", function() {
            expect(WebService.loginRequest).toHaveBeenCalled();
        });

        it('should call loginRequest WebServiceMock', function ()
        {
        $scope.username = 'v@v.com';
        $scope.password = 'viranga123';
        $scope.$digest();

        $scope.login();
        expect($scope.username).toEqual('v@v.com');
        expect($scope.password).toEqual('viranga123');
        expect(WebService.loginRequest).toHaveBeenCalled();
        });

      var $rootScope;
      beforeEach(inject(function($injector) {
      $rootScope = $injector.get('$rootScope');
      $rootScope.$broadcast('LSuccess_msg', msgdata);
      }));

      describe("Boradcast response", function() {
               it("should broadcast Response message", function() {
               expect($scope.myEventCalled).toBe(true);
               expect($scope.msg).not.toBe(null);

             //$scope.msg.Status check fails
             //expect($scope.msg.Status).toEqual('LOGIN_SUCCESS'); 

             //$scope.response related checks can be evaluated
               expect($scope.response).not.toBe(null);
               expect($scope.response.EMail).toEqual('v@v.com');
               expect($scope.response.Status).toEqual('LOGIN_SUCCESS');
               expect($scope.response.FullName).toEqual('Viranga Indira');
         });
       });
    });
 });

Error Message thrown if expection is uncommented

Chrome 36.0.1985 (Windows 7) Controller : Login controller Controller : Login controller 
Boradcast response should broadcast Response message FAILED
TypeError: Cannot read property 'Status' of undefined at null.<anonymous>

Any leads on how I can test the received message status (e.g. $scope.msg.Status) and how to test URL redirection process would be much appreciated. Why is $scope.msg.Status inaccessible in this test ?

Can Spy Objects be used further to improve these test especially to test $scope.$on in AngularJS?

Community
  • 1
  • 1
Gehan
  • 380
  • 4
  • 17
  • @Ben Lesh - Any leads on how I can test the received message in $scope.$on block(e.g. $scope.$on('LSuccess_msg',function(e,msg){...}).How can I refer to the msg parameter in my unit test ? expect($scope.msg.Status).toEqual('LOGIN_SUCCESS'); //$scope.msg.Status fails – Gehan Sep 03 '14 at 12:49

1 Answers1

2

It sounds like your question is:

How do I test window.location?

One answer here is to create a service that wraps window location. Something very simple like:

app.factory('windowLocation', function($window) {
  return $window.location;
});

Then wherever you need it, you inject it:

app.controller('MyCtrl', function($scope, windowLocation) {
  $scope.foo = function(url){
    windowLocation.href = url;
  });
});

Testing it, therefor is as simple as mocking it and providing it to your controller (or service).

$controller('MyCtrl', {
  $scope: someScope,
  windowLocation: mockWindowLocation,
});

and then assert that href was changed on your mock:

describe('$scope.foo', function() {
  it('should update windowLocation.href', function(){
    $scope.foo('whatever');
    expect(mockWindowLocation.href).toBe('whatever');
  });
});

Outside of that, the only other thing I see that's potentially complicating your code is the $timeout you've wrapped the location change with. If that's not necessary, remove it. If it is necessary, you'll just need to make your jasmine test asynchronous, which has been documented fairly well throughout the web, I think.

Good luck, and I hope this answer helps you.

Ben Lesh
  • 107,825
  • 47
  • 247
  • 232