web-dev-qa-db-ja.com

Javascriptでwindow.location.hrefをモックする

Window.location.hrefを使用する関数のユニットテストがいくつかあります。これは理想的ではありませんが、これを渡すことはできますが、実装では不可能です。実際にテストランナーページを実際にURLに移動させずにこの値をモックすることが可能かどうか疑問に思っています。

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

ユニットテストフレームワークにジャスミンを使用しています。

61
wmitchell

ローカルコンテキストをシミュレートし、独自のバージョンの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!を含む)は最初にコンテキストオブジェクトから検索され、存在しない場合は実際のコンテキストから検索されます。

8
Andris

これを行う最善の方法は、どこかにヘルパー関数を作成し、それをモックすることです:

 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");
49
Kurt Harriger

ここで以前の投稿で示唆された2つのソリューションを提案します。

  • アクセスに関する関数を作成し、実動コードで使用し、テストで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");
    
  • 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");
    
18
cburgmer

Window.locationを変更するライブラリがあり、正常に機能するだけでなく、テストされることを許可したい場合があります。この場合、クロージャを使用して、このようなライブラリに目的の参照を渡すことができます。

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

次に、ライブラリを含める前のテストで、selfをグローバルに再定義できます。ライブラリは、モック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}
}());

最新ではないにしても、ほとんどのブラウザでは、デフォルトでselfはすでにウィンドウへの参照になっています。 Worker APIを実装するプラットフォームでは、Worker self内はグローバルスコープへの参照です。 node.jsでは、selfとwindowの両方が定義されていないため、必要に応じてこれも実行できます。

self || window || global

Node.jsが実際にWorker APIを実装している場合、これは変わる可能性があります。

5
Jim Isaacs

以下は、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を取得しますそれを置く。

3
user566245

IMO、このソリューションは、ソースのwindow.location.hrefを$ window.location.hrefに置き換えることができるという点で、cburgmerの小さな改良です。ジャスミンではなくカルマを使用しているのは確かですが、このアプローチはどちらでも機能すると考えています。また、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

0
Tongfa