Dalai Lama
Dalai Lama

Reputation: 399

How to avoid location.reload() in angular tests via jasmine, and test it

In one of my controllers I have a location.reload()

(For those who wants to know why, My login page does not load the entire app before the user logs in to reduce threshold on the server)

When I test the controller (using jasmine html for the ui), it actually reloads the test in an infinite loop.

My questions is this:

How do I avoid the location.reload() from loading the test itself, and how do I even check whether it has been called? can I mock it somehow?

Here's the code for the controller:

(function() {
    'use strict';

    angular
    .module('app.ctrls')
    .controller('LoginController', LoginController);

    LoginController.$inject = [ '$scope', 'AuthService' ];

    function LoginController($scope, AuthService)
    {
        console.log('LoginController');
        $scope.credantials = {
                email:      '',
                password:   '',
                remember:   false
        }
        $scope.login = function(credantials)
        {
            AuthService.login(credantials.email, credantials.password, credantials.remember || false).success(function(response) {
                console.log(response);
                if (response.email !== null)
                {
                    $scope.logged = true;
                    console.log('login successful');
                    location.reload();
                }
                else
                {
                    $scope.logged = false;
                    $scope.error = true;
                }
            }).error(function() {
                console.log('failed to login');
            });
        };
    }
})();

Here's the spec:

describe("successfully logged in", function() {
    it("should reload the page", function() {
        this.scope.credantials = { email: '[email protected]', password: '123', remember: false };
        this.$httpBackend.expectPOST('/auth/login', this.scope.credantials).respond({first_name: "Bakayaru", email: "[email protected]"});
        this.$httpBackend.whenGET('/tpls/home.html').respond(200);
        this.scope.login(this.scope.credantials);
        this.$httpBackend.flush();
        expect(this.scope).toBeDefined();
        expect(this.scope.logged).toBeTruthy();
        /* -------> TEST that location.reload() has been called HERE <--------- */
    });
});

Upvotes: 4

Views: 10455

Answers (3)

Sam Vloeberghs
Sam Vloeberghs

Reputation: 912

You can mock $window in your test like this:

describe('service', function () {

  var $window;

  beforeEach(module('core', function ($provide) {
    $window = {
      location:{
        reload: jasmine.createSpy()
      }
    };
    $provide.value('$window', $window);
  }));

});

This way it will "override" the default behaviour of $window.

Upvotes: 2

Dalai Lama
Dalai Lama

Reputation: 399

I realized that just like Unit Tests in PHP for example, when the code has file_get_content for example, you have to mock it yourself, and not test it.

The location.reload is a browser component and therefor must be mocked for my code to become testable.

Therefore I have created a BrowserService:

(function() {
    'use strict';

    angular
    .module('rushcore.services')
    .factory('BrowserService', BrowserService);

    function BrowserService()
    {
        console.log('BrowserService');
        return {
            reload: function() {
                location.reload();
            }
        };
    }
})();

And in the test I mocked it and injected it.

var BrowserServiceMock = { reload: function(){ console.log("reloaded"); } };

I then spied on it:

var reload = spyOn(BrowserServiceMock, 'reload');

And finally made sure it was called:

expect(reload).toHaveBeenCalled();

Upvotes: 1

Antti Latva-aho
Antti Latva-aho

Reputation: 111

I am not sure what you mean by "not loading itself" but anyways it is possible to mock the .reload() in tests. I did it in my App by first of all using Angular service $window.location instead of native javascript location in my App like this after injection $window as dependency to my controller:

$window.location.reload();

Then in the spec file I have the following (with one example test case using the mocked reload):

describe('Controller: MyCtrl', function () {

  // load the controller's module
  beforeEach(module('myApp'));

  var MyCtrl,
    scope,
    mockBackend,
    window;

  beforeEach(function(){
    inject(function(_$httpBackend_, $controller, $rootScope, $window) {
        scope = $rootScope.$new();
        mockBackend = _$httpBackend_;
        window = $window;
        spyOn(window.location, 'reload');
        myCtrl = $controller('MyCtrl', {
            $scope: scope,
            $window: window
        });
    });
  });

it('logout should fail if $window.reload() is called on unsuccessful logout call', function(){
    scope.logout();
    mockBackend.expectGET('/rest/session/logout').respond(404);

    mockBackend.flush();
    expect(window.location.reload.calls.count()).toEqual(0);

    mockBackend.verifyNoOutstandingExpectation();
    mockBackend.verifyNoOutstandingRequest();
  });
});

This is a bit differently structured to your spec file, but I hope this example clarifies how to mock $window object and use spies on it.

Upvotes: 4

Related Questions