可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have some unit tests for a function that makes use of the window.location.href -- not ideal I would far rather have passed this in but its not possible in the implementation. I'm just wondering if its possible to mock this value 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'm using jasmine for my unit testing framework.
回答1:
You need to simulate local context and create your own version of 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 firstly be looked from the context object and if not present then from the actual context.
回答2:
The best way to do this is to create a helper function somewhere and then mock that:
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 simply use this instead. Then you replacing this method whenever you need to return a mocked value:
mynamespace.util.getWindowLocationHRef = function() { return "http://mockhost/mockingpath" };
If you want a specific part of the window location such as a query string parameter then create helper methods for that too and keep the parsing out of your main code. Some frameworks such as jasmine have test spies that can not only mock the function to return desired values, but can also verified it was called:
spyOn(mynamespace.util, 'getQueryStringParameterByName').andReturn("desc"); //... expect(mynamespace.util.getQueryStringParameterByName).toHaveBeenCalledWith("sort");
回答3:
I would propose two solutions which have already been hinted at in previous posts here:
Create a function around the access, use that in your production code, and stub this 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 a dependency injection and inject a 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");
回答4:
Sometimes you may have a library that modifies window.location and you want to allow for it to function normally but also be tested. If this is the case, you can use a closure to pass your desired reference to your library such as this.
/* in mylib.js */ (function(view){ view.location.href = "foo"; }(self || window));
Then in your test, before including your library, you can redefine self globally, and the library will use the mock self as the view.
var self = { location: