Подтвердить что ты не робот

Mocking window.location.href в Javascript

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

 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");

Ответ 3

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

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

Ответ 4

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

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

Затем в вашем тесте, прежде чем включать вашу библиотеку, вы можете переопределить себя глобально, и библиотека будет использовать mock self в качестве представления.

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

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

self || window || global

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

Ответ 5

Ниже приведен подход, который я принял, чтобы высмеять 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, но он не установлен на объект фальшивого окна, который вы вложили. Теперь, после того как вы его заменили, код получит ваш поддельный объект окна и его поддельный href, который вы его положили.

Ответ 6

IMO, это решение является небольшим улучшением cburgmer, поскольку оно позволяет вам заменить window.location.href на $window.location.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; }

Теперь я могу установить location.href в коде, введя windowInjectable() как $window, как это:

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

и высмеивая его в unit test, он выглядит так:

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

Ответ 7

Вам нужно подделать 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 no such route