насмешливое окно.местоположение.href в Javascript
У меня есть некоторые модульные тесты для функции, которая использует окно.местоположение.href -- не идеально я бы скорее передал это, но это невозможно в реализации. Мне просто интересно, можно ли издеваться над этим значением, фактически не заставляя мою страницу тестового бегуна фактически перейти на URL.
window.location.href = "http://www.website.com?varName=foo";
expect(actions.paramToVar(test_Data)).toEqual("bar");
Я использую jasmine для своей платформы модульного тестирования.
7 ответов:
вам нужно смоделировать локальный контекст и создать свою собственную версию
windowиwindow.locationобъекты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/...если вы используете
withтогда все переменные (включаяwindow!) сначала будет выглядеть из объекта контекста, а если нет, то из фактического контекста.
лучший способ сделать это-создать вспомогательную функцию где-то, а потом глумиться, что:
var mynamespace = mynamespace || {}; mynamespace.util = (function() { function getWindowLocationHRef() { return window.location.href; } return { getWindowLocationHRef: getWindowLocationHRef } })();Теперь вместо использования окна.местоположение.href непосредственно в вашем коде просто используйте это вместо этого. Затем вы можете заменить этот метод всякий раз, когда вам нужно вернуть издевались значение:
mynamespace.util.getWindowLocationHRef = function() { return "http://mockhost/mockingpath" };Если вы хотите определенную часть местоположения окна, такую как параметр строки запроса, то создайте вспомогательные методы для этого тоже и сохраните синтаксический анализ из своего основного кода. Некоторые фреймворки, такие как jasmine, имеют тестовые шпионы, которые могут не только издеваться над функцией для возврата желаемых значений, но также могут проверить, что она была вызвана:
spyOn(mynamespace.util, 'getQueryStringParameterByName').andReturn("desc"); //... expect(mynamespace.util.getQueryStringParameterByName).toHaveBeenCalledWith("sort");
Я бы предложил два решения, которые уже намекали в предыдущих постах здесь:
создайте функцию вокруг доступа, используйте ее в своем производственном коде и заглушите это с помощью Jasmine в своих тестах:
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");использовать инъекции зависимостей и ввести подделку в тесте:
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");
иногда у вас может быть библиотека, которая изменяет окно.расположение и вы хотите, чтобы он нормально функционировал, но также был протестирован. Если это так, вы можете использовать закрытие, чтобы передать нужную ссылку на вашу библиотеку, такую как эта.
/* in mylib.js */ (function(view){ view.location.href = "foo"; }(self || window));затем в вашем тесте, прежде чем включать вашу библиотеку, вы можете переопределить себя глобально, и библиотека будет использовать макет себя в качестве представления.
var self = { location: { href: location.href } };В вашей библиотеке, вы можете также сделать что-то вроде следующего, таким образом, вы можете переопределить себя в любой момент теста:
/* in mylib.js */ var mylib = (function(href) { function go ( href ) { var view = self || window; view.location.href = href; } return {go: go} }());в большинстве, если не во всех современных браузерах, self уже является ссылкой на окно по умолчанию. В платформах, реализующих рабочий API, в пределах рабочего self-это ссылка на глобальную область. В узлах.js как self, так и window не определены, поэтому, если вы хотите, вы также можете сделать это:
self || window || globalэто может измениться, если узел.js действительно реализует рабочий API.
Ниже приведен подход, который я использую для макета окна.местоположение.href и / или что-нибудь еще, что может быть на глобальном объекте.
во-первых, вместо того, чтобы обращаться к нему напрямую, инкапсулируйте его в модуль, где объект хранится с помощью геттера и сеттера. Ниже приведен мой пример. Я использую require, но это не обязательно здесь.
define(["exports"], function(exports){ var win = window; exports.getWindow = function(){ return win; }; exports.setWindow = function(x){ win = x; } });теперь, где вы обычно делали в своем коде что-то вроде
window.location.href, Теперь вы бы сделали что-то например:var window = global_window.getWindow(); var hrefString = window.location.href;наконец настройка завершена, и вы можете проверить свой код, заменив объект window поддельным объектом, который вы хотите быть на его месте.
fakeWindow = { location: { href: "http://google.com?x=y" } } w = require("helpers/global_window"); w.setWindow(fakeWindow);это изменило бы
winпеременная в окне модуля. Первоначально он был установлен на глобальныйwindowобъект, но он не установлен на поддельный объект окна, который вы вставляете. Итак, теперь после того, как вы заменили его, код получит ваш поддельный объект окна и его поддельный href, который вы положили.
IMO, это решение является небольшим улучшением cburgmer в том, что он позволяет заменить окно.местоположение.href с $ window.местоположение.href в источник. Конечно, я использую карму, а не Жасмин, но я считаю, что этот подход будет работать с любым. И я добавил зависимость от Синон.
первый сервис / синглтон:
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; }Теперь я могу установить местоположение.href в коде путем инъекции windowInjectable() как $window следующим образом:
function($window) { $window.location.href = "http://www.website.com?varName=foo"; }- глумливым это в модульном тесте выглядит так:
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();синтаксис getter / setter возвращается к IE 9 и в противном случае широко поддерживается в соответствии с https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
вам нужно подделывать
window.location.hrefнаходясь на той же странице. В моем случае этот отрезанный работал отлично:$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's no such route
Comments