Mocking window.location.href in Javascript

I have some unit tests for a function that uses window.location.href - not perfect, I would have passed it much more quickly, but it was not possible in the implementation. I'm just wondering if this value can make fun of it without actually causing my test runner page to actually go to the URL.

window.location.href = "http://www.website.com?varName=foo"; expect(actions.paramToVar(test_Data)).toEqual("bar"); 

I use jasmine for my unit testing system.

+50
javascript mocking jasmine
Jan 25 '11 at 10:19
source share
7 answers

You need to simulate the local context and create your own version of the window and window.location objects

 var localContext = { "window":{ location:{ href: "http://www.website.com?varName=foo" } } } // simulated context with(localContext){ console.log(window.location.href); // http://www.website.com?varName=foo } //actual context console.log(window.location.href); // http://www.actual.page.url/... 

If you use with , then all variables (including window !) Will first be viewed from the context object, and if not, then from the actual context.

+6
Jan 25 2018-11-11T00:
source share

The best way to do this is to create a helper function somewhere and then mock it:

  var mynamespace = mynamespace || {}; mynamespace.util = (function() { function getWindowLocationHRef() { return window.location.href; } return { getWindowLocationHRef: getWindowLocationHRef } })(); 

Now instead of using window.location.href directly in your code, just use this instead. Then you replace this method whenever you need to return the mocking value:

 mynamespace.util.getWindowLocationHRef = function() { return "http://mockhost/mockingpath" }; 

If you want a specific part of the location of the window, such as a query string parameter, then create helper methods for this and save the parsing of your main code. Some structures, such as jasmine, have test spies that can not only mock a function to return the desired values, but can also verify that it was called:

 spyOn(mynamespace.util, 'getQueryStringParameterByName').andReturn("desc"); //... expect(mynamespace.util.getQueryStringParameterByName).toHaveBeenCalledWith("sort"); 
+39
Aug 13 2018-11-11T00:
source share

I would suggest two solutions that were already mentioned in previous posts:

  • Create a function around access, use it in your production code, and leave it with Jasmine in your tests:

     var actions = { getCurrentURL: function () { return window.location.href; }, paramToVar: function (testData) { ... var url = getCurrentURL(); ... } }; // Test var urlSpy = spyOn(actions, "getCurrentURL").andReturn("http://my/fake?param"); expect(actions.paramToVar(test_Data)).toEqual("bar"); 
  • Use dependency injection and inject fake in your test:

     var _actions = function (window) { return { paramToVar: function (testData) { ... var url = window.location.href; ... } }; }; var actions = _actions(window); // Test var fakeWindow = { location: { href: "http://my/fake?param" } }; var fakeActions = _actions(fakeWindow); expect(fakeActions.paramToVar(test_Data)).toEqual("bar"); 
+17
Jan 13 2018-12-01T00:
source share

Sometimes you may have a library that modifies window.location, and you want it to function normally, but also be tested. If so, you can use closure to pass the desired link to your library, for example.

 /* in mylib.js */ (function(view){ view.location.href = "foo"; }(self || window)); 

Then in your test, before you include your library, you can redefine yourself globally and the library will use mock self as a view.

 var self = { location: { href: location.href } }; 

In your library, you can also do something like the following so that you can redefine yourself at any time during the test:

 /* in mylib.js */ var mylib = (function(href) { function go ( href ) { var view = self || window; view.location.href = href; } return {go: go} }()); 

In most, if not all modern browsers, I already refer to the window by default. On platforms that implement the Worker API, inside the Worker itself. It is a reference to the global area. In node.js, both I and the window are not defined, so if you want, you can also do this:

 self || window || global 

This may change if node.js really implements the Worker API.

+5
Oct. 15 '11 at 7:14
source share

Below is the approach I took to make fun of window.location.href and / or anything else that might be on a global object.

First, instead of directly accessing it, encapsulate it in a module in which the object is stored using a getter and setter. Below is my example. I use require, but this is not needed here.

 define(["exports"], function(exports){ var win = window; exports.getWindow = function(){ return win; }; exports.setWindow = function(x){ win = x; } }); 

Now that you usually did something like window.location.href in your code, now you would do something like:

 var window = global_window.getWindow(); var hrefString = window.location.href; 

Finally, the installation is complete, and you can test your code by replacing the window object with the fake object that you want instead in its place.

 fakeWindow = { location: { href: "http://google.com?x=y" } } w = require("helpers/global_window"); w.setWindow(fakeWindow); 

This will change the win variable in the module window. It was originally set to the global window object, but it is not set to the fake window object that you nested. Now, after you replace it, the code will get your fake window object and its fake href that you put it on.

+3
Jul 23 '15 at 16:05
source share

IMO, this solution is a slight improvement to cburgmer as it allows you to replace window.location.href with $ window.location.href in the source. Of course, I use Karma, not Jasmine, but I believe that this approach will work with. And I added dependency on sinon.

First service / singleton:

 function setHref(theHref) { window.location.href = theHref; } function getHref(theHref) { return window.location.href; } var $$window = { location: { setHref: setHref, getHref: getHref, get href() { return this.getHref(); }, set href(v) { this.setHref(v); } } }; function windowInjectable() { return $$window; } 

Now I can set location.href in the code by entering windowInjectable () as $ window, like this:

 function($window) { $window.location.href = "http://www.website.com?varName=foo"; } 

and making fun of it in unit test, it looks like this:

 sinon.stub($window.location, 'setHref'); // this prevents the true window.location.href from being hit. expect($window.location.setHref.args[0][0]).to.contain('varName=foo'); $window.location.setHref.restore(); 

The getter / setter syntax is returned in IE 9 and is otherwise widely supported according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set

0
Aug 12 '16 at 1:56 on
source share

You need to fake window.location.href , being on the same page. In my case, this cut off worked fine:

 $window.history.push(null, null, 'http://server/#/YOUR_ROUTE'); $location.$$absUrl = $window.location.href; $location.replace(); // now, $location.path() will return YOUR_ROUTE even if there no such route 
-one
Aug 30 '16 at 21:09
source share



All Articles