насмешливый window.location.href в Javascript - PullRequest
61 голосов
/ 25 января 2011

У меня есть несколько модульных тестов для функции, которая использует window.location.href - не идеально, я бы предпочел пропустить это, но это невозможно в реализации. Мне просто интересно, возможно ли смоделировать это значение, фактически не заставляя мою страницу выполнения теста перейти на URL.

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

Я использую жасмин для моей среды модульного тестирования.

Ответы [ 7 ]

49 голосов
/ 13 августа 2011

Лучший способ сделать это - создать вспомогательную функцию где-нибудь и затем смоделировать это:

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

Теперь вместо непосредственного использования window.location.href в вашем коде просто используйте это вместо этого.Затем вы можете заменить этот метод всякий раз, когда вам нужно вернуть имитированное значение:

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

Если вам нужна определенная часть местоположения окна, такая как параметр строки запроса, тогда создайте вспомогательные методы для этого и сохранитеРазбор вашего основного кода.В некоторых средах, таких как жасмин, есть шпионы-тестеры, которые могут не только высмеивать функцию, возвращающую нужные значения, но и проверять, что она была вызвана:

spyOn(mynamespace.util, 'getQueryStringParameterByName').andReturn("desc");
//...
expect(mynamespace.util.getQueryStringParameterByName).toHaveBeenCalledWith("sort");
18 голосов
/ 13 января 2012

Я бы предложил два решения, о которых уже упоминалось в предыдущих постах:

  • Создайте функцию для доступа, используйте ее в своем рабочем коде и добавьте 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");
    
8 голосов
/ 25 января 2011

Вам необходимо смоделировать локальный контекст и создать собственную версию объектов 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!) Будут в первую очередьсмотреть из объекта контекста, а если нет, то из фактического контекста.

5 голосов
/ 15 октября 2011

Иногда у вас может быть библиотека, которая изменяет window.location, и вы хотите, чтобы она работала нормально, но также была протестирована. Если это так, вы можете использовать закрытие, чтобы передать желаемую ссылку в вашу библиотеку, такую ​​как эта.

/* 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 уже является ссылкой на окно по умолчанию. На платформах, которые реализуют Worker API, внутри Worker self есть ссылка на глобальную область. В node.js и self, и window не определены, поэтому, если вы хотите, вы также можете сделать это:

self || window || global

Это может измениться, если node.js действительно реализует Worker API.

3 голосов
/ 23 июля 2015

Ниже приведен подход, который я использую, чтобы смоделировать window.location.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;

Наконец установка завершена, и вы можете протестировать свой код, заменив объект окна поддельным объектом, который вы хотите вместо этого заменить.

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

Это изменит переменную win в модуле окна. Первоначально для него был задан глобальный объект window, но он не установлен для ложного оконного объекта, который вы вставили. Так что теперь после того, как вы его заменили, код получит ваш ложный оконный объект и его ложную ссылку, которую вы поместили.

0 голосов
/ 31 августа 2016

Вам нужно подделать 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
0 голосов
/ 12 августа 2016

IMO, это решение является небольшим улучшением cburgmer в том смысле, что оно позволяет заменить window.location.href на $ window.location.href в исходном коде. Конечно, я использую Карму, а не Жасмин, но я верю, что этот подход будет работать с любым из них. И я добавил зависимость от sinon.

Первая услуга / синглтон:

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; }

Теперь я могу установить location.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

...