簡體   English   中英

mocking window.location.href 在 Javascript

[英]mocking window.location.href in Javascript

我對使用 window.location.href 的 function 進行了一些單元測試——這並不理想,我寧願將其傳入,但在實現中是不可能的。 我只是想知道是否有可能在不實際導致我的測試運行程序頁面實際從 go 到 URL 的情況下模擬這個值。

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

我正在使用 jasmine 作為我的單元測試框架。

最好的方法是在某處創建一個輔助函數,然后模擬:

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

如果您想要窗口位置的特定部分,例如查詢字符串參數,那么也為此創建輔助方法,並將解析工作排除在主代碼之外。 一些框架如 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");

您需要模擬本地上下文並創建您自己的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 !)將首先從上下文對象中查看,如果不存在則從實際上下文中查看。

有時您可能有一個修改 window.location 的庫,並且您希望允許它正常運行但也需要進行測試。 如果是這種情況,您可以使用閉包將所需的引用傳遞給您的庫,例如這樣。

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

然后在您的測試中,在包含您的庫之前,您可以全局重新定義 self,該庫將使用模擬 self 作為視圖。

var self = {
   location: { href: location.href }
};

在您的庫中,您還可以執行以下操作,以便您可以在測試中的任何時候重新定義 self:

/* in mylib.js */
var mylib = (function(href) {
    function go ( href ) {
       var view = self || window;
       view.location.href = href;
    }
    return {go: go}
}());

在大多數(如果不是所有)現代瀏覽器中,默認情況下 self 已經是對 window 的引用。 在實現 Worker API 的平台中,Worker self 是對全局范圍的引用。 在 node.js 中 self 和 window 都沒有定義,所以如果你願意,你也可以這樣做:

self || window || global

如果 node.js 真的實現了 Worker API,這可能會改變。

下面是我用來模擬 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;

最后設置完成,你可以通過用你想要的假對象替換 window 對象來測試你的代碼。

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

這將更改窗口模塊中的win變量。 它最初設置為全局window對象,但未設置為您放入的假窗口對象。所以現在替換它后,代碼將獲取您放置的假窗口對象和它的假 href。

這對我有用:

delete window.location;
window.location = Object.create(window);
window.location.href = 'my-url';

這與 cpimhoff 的建議類似,但它在 Angular 中使用了依賴注入。 我想我會添加這個以防其他人來這里尋找 Angular 解決方案。

在模塊中,app.module 可能會添加一個 window 提供程序,如下所示:

@NgModule({
...
  providers: [
    {
      provide: Window,
      useValue: window,
    },
  ],
...
})   

然后在使用 window 的組件中,在構造函數中注入 window。

constructor(private window: Window)

現在不是直接使用 window,而是在使用 window 時使用組件屬性。

this.window.location.href = url

有了它,您可以使用 TestBed 在 Jasmine 測試中設置提供者。

    beforeEach(async () => {
      await TestBed.configureTestingModule({
        providers: [
          {
            provide: Window,
            useValue: {location: {href: ''}},
          },
        ],
      }).compileComponents();
    });

IMO,此解決方案是 cburgmer 的一個小改進,因為它允許您在源代碼中用 $window.location.href 替換 window.location.href。 當然,我使用的是 Karma 而不是 Jasmine,但我相信這種方法對兩者都適用。 我已經添加了對 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; }

現在我可以通過像這樣注入 windowInjectable() 作為 $window 在代碼中設置 location.href :

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得到廣泛支持

這是我的通用解決方案,它需要在生產代碼中額外導入,但不需要依賴注入或編寫像getHref()這樣的單獨包裝函數。

基本上我們將窗口扔到一個單獨的文件中,然后我們的 prod 代碼從該文件間接導入窗口。

在生產中, windowProxy === window 在測試中,我們可以改變導出windowProxy的模塊並用一個新的臨時值模擬它。

// windowProxy.js

/*
 * This file exists solely as proxied reference to the window object
 * so you can mock the window object during unit tests.
 */
export default window;
// prod/someCode.js
import windowProxy from 'path/to/windowProxy.js';

export function changeUrl() {
  windowProxy.location.href = 'https://coolsite.com';
}
// tests/someCode.spec.js
import { changeUrl } from '../prod/someCode.js';
import * as windowProxy from '../prod/path/to/windowProxy.js';

describe('changeUrl', () => {
  let mockWindow;
  beforeEach(() => {
    mockWindow = {};
    windowProxy.default = myMockWindow;
  });

  afterEach(() => {
    windowProxy.default = window;
  });

  it('changes the url', () => {
    changeUrl();
    expect(mockWindow.location.href).toEqual('https://coolsite.com');
  });
});

您需要在同一頁面上偽造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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM