2011-01-25 51 views
53

我對使用window.location.href的函數進行了一些單元測試 - 這並不理想,我寧願將它傳入,但在實現中不可能。我只是想知道是否有可能嘲笑這個值,而實際上並沒有讓我的測試運行器頁面實際轉到URL。在Javascript中模仿window.location.href參數

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

我使用茉莉花爲我的單元測試框架。

+1

我有同樣的問題,當我想調用一個函數內。你如何解決它? – wizztjh 2011-07-28 07:55:23

回答

6

您需要模擬當地情況和創建自己的windowwindow.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中使用typescript – 2017-07-05 06:45:47

+5

`'with'語句在嚴格模式下不被允許。` – isherwood 2017-07-07 14:43:15

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

如果你想在窗口位置的特定部分,如查詢字符串參數,然後創建該helper方法也和保持解析出來的你的主要代碼。一些框架,如茉莉有測試間諜不僅可以嘲笑函數返回所需的值,也可以驗證它被稱爲:

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

在大多數,如果不是所有現代瀏覽器,自我已經默認情況下對窗口的引用。在實現Worker API的平臺中,Worker內部是對全局範圍的引用。在node.js中自身和窗都沒有定義,所以,如果你願意,你也可以這樣做:

self || window || global 

如果node.js中確實實現工人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"); 
    
3

下面是我用來模擬window.location.href和/或可能在全局對象上的其他任何東西的方法。

首先,不是直接訪問它,而是將它封裝在一個模塊中,其中的對象由getter和setter保存。以下是我的例子。我正在使用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

國際海事組織,這個解決方案是cburgmer的一個小改進,因爲它允許你用源代碼中的$ window.location.href替換window.location.href。當然,我使用的是Karma而不是Jasmine,但我相信這種方法也適用於任何一種。我已經添加了對sinon的依賴。

服務首先/單:

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

,並在嘲笑它:

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

現在我可以通過注入windowInjectable()爲$窗口這樣的設置location.href代碼單元測試它看起來像:

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

-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