7

I have a function in my Angular controller that looks like the following:

$scope.myFunction = function(){
    $scope.myVariable = "something";
    $scope.myOtherVariable = "something else";
    window.location.href = "/path/to/page"; 
}

A simple Jasmine test covers the above function and looks like:

describe('my test', function(){
    it('should pass', function(){
        scope.myFunction();
        expect(scope.myVariable).toBe('something');
        expect(scope.myOtherVariable).toBe('something else');     
    });
});

The above test itself passes, but then Karma throws the following error in the console:

Some of your tests did a full page reload!

The page redirect is causing Karma to raise this warning. What's the best way to get around this?

I thought about giving both anonymous functions in the Jasmine test and Angular names and then using arguements.callee.caller.name inside the original function to determine whether the function is being called by itself or Jasmine. Unfortunately, arguments.callee.caller.name always returns undefined, which I suspect is caused by the way Angular and Jasmine are linked to each other.

Lloyd Banks
  • 35,740
  • 58
  • 156
  • 248
  • This issue didn't actually come up in my code, but it did give me the spark that ignited a fix for my own, so thanks for posting it! In case you're curious, the issue I fixed for myself due to your question is http://stackoverflow.com/questions/31947852/how-to-test-angular-app-with-john-papa-style-karma-and-jasmine-with-data-servi. Thanks, C§ – CSS Aug 11 '15 at 20:50

2 Answers2

22

A long time since this question was asked, but the following approach should be cleaner:

By changing the original function to use Angular's $window service instead of the browser window object, it is possible to write a test that does not require modifying production code or take any runtime parameters into account. This of course requires that $window is a dependency for the controller where the function is found.

Using angular-mocks/ngMock, something like:

var fakeWindow = {
  location: {
    href: ''
  }
}
// instantiate controller with mock window
beforeEach(inject(function($controller) {
  $controller('YourCtrlName', {
    $window: fakeWindow
  });
}));

should allow the test to pass, assuming that the controller doesn't need $window for anything else.

orbitbot
  • 728
  • 6
  • 18
  • most perfect answer i think – Mak Dec 12 '14 at 14:48
  • In most browsers the `window.location` is a non-writable property, so supplying your own fake object is the best way to mock these non-writable properties. – user2943490 Dec 16 '14 at 05:35
  • This should be the accepted answer. You should not have to change the target code to get the tests to run in the way the currently accepted answer does. – Red3 Jul 28 '15 at 16:46
4

You could just create a function that does the navigation such as

$scope.Navigate = function() {
    window.location.href = "/path/to/page";
};

or even better in some service

app.factory('navigator', function() {
    return {
        Navigate: function(path) {
            window.location.href = path;
        }
    };
});

Then in your test you can use a spy on Navigate, to be sure that the navigation was called, but don't actually call the navigation.

hassassin
  • 5,024
  • 1
  • 29
  • 38