2011-01-25 2 views
53

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

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

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

+1

я с той же проблемой, когда я хочу, чтобы вызвать функцию внутри с. Как вы его решаете? – wizztjh

ответ

6

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

+1

будет ли это решение работать в angular2 с машинописным текстом –

+5

'' с инструкциями '' не допускаются в строгом режиме.' – isherwood

+0

Не выглядит так, как вы можете писать с ним читаемые тесты. Я думаю, что решение, предложенное @ Kurt Harriger, намного лучше. -1 –

42

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

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

Иногда вы можете иметь библиотеку, которая модифицирует 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} 
}()); 

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

self || window || global 

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

17

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

  • Создать функцию вокруг доступа, использовать это в вашем рабочем коде, и незавершенное это с Жасмин в тестах:

    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"); 
    
  • Используйте dependency injection и впрыснуть подделку в тесте:

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

В Jasmine 2.x синтаксис, используемый в первом примере, кажется, немного изменился. '.andReturn()' следует заменить на '.and.returnValue()'. –

3

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

0

IMO, это решение - небольшое усовершенствование cburgmer's, поскольку оно позволяет вам заменить 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() в $ окна, как это:

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

Поглотитель/сеттер синтаксис восходит к IE 9, а в противном случае широко поддерживается в соответствии с https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set

-1

Вам необходимо подделать 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 
Смежные вопросы