web-dev-qa-db-ja.com

qUnitテストのためにwindow.locationオブジェクトをモックすることは可能ですか?

簡単にするために(本物は複雑で無関係です)、現在のウィンドウのクエリ文字列を返すユーティリティ関数があるとしましょう。

var someUtilityFunction = () {
    return window.location.search.substring(1);
};

ここで、この関数をqUnitで単体テストしたいと思います(テストハーネスが関連しているかどうかはわかりません)。

test('#1 someUtilityFunction works', function () {
    // setup
    var oldQS = window.location.search;
    window.location.search = '?key1=value1&key2=value2&key3=value3';

    var expectedOutput = 'key1=value1&key2=value2&key3=value3';

    // test
    equals(someUtilityFunction(),
        expectedOutput,
        'someUtilityFunction works as expected.');

    // teardown
    window.location.search = oldQS;
});

ここでの問題は、window.location.searchを別のクエリ文字列に設定すると、ページが再読み込みされ、本質的に無限の要求ループに入るということです。 window.locationオブジェクトをモックアウトする方法はありますかなしsomeUtilityFunction関数に変更を加えますか?

18
jbabey

数日前に同じ問題が発生しました。主に2つのアプローチがあります:

コードを書き直します

これは(もしあれば)最善の解決策ではないかもしれませんが、モックを簡単にするためにwindowオブジェクトを関数に渡すことを検討してください。さらに良いことに、クロージャを使用してコードをカプセル化します。これには、さらにいくつかの利点があります。

  • グローバル変数をシャドウすることができます
  • プライベートローカル変数を使用できます
  • 名前の衝突を回避できます
  • シャドウイングにより、モックが非常に簡単になります。他のものを渡すだけです。

コードをラップする

ウィンドウオブジェクトをローカル変数にモックする関数内にすべてのコードをラップできます。基本的に2つの可能性もあります。

これがモックだとします。

var customWindow = {
    location: {
        search: "",
        hash: ""
    }
};

クロージャを使用する

var someUtilityFunction;

(function(window) {
    // window is now shadowed by your local variable
    someUtilityFunction = () {
        return window.location.search.substring(1);
    };
})(customWindow);

これにより、グローバルwindowがローカルwindowでシャドウされます。

withステートメントを使用します

私は通常強く反対しますが、それはここで多くの問題を本当に解決することができます。基本的にスコープを再マップするため、環境を非常に簡単にモックできます。

// first some more preparation for our mock
customWindow.window = customWindow;

with(customWindow) {

    // this still creates the var in the global scope
    var someUtilityFunction = () {
        // window is a property of customWindow
        return window.location.search.substring(1);
    };

    // since customWindow is our scope now
    // this will work also
    someUtilityFunction = () {
        // location is a property of customWindow too
        return location.search.substring(1);
    };

}

ちなみに、searchプロパティがhashプロパティと同じ症状に苦しんでいるかどうかはわかりません。つまり、疑問符が含まれている場合と含まれていない場合があります。しかし、あなたは使用を検討したいかもしれません

window.location.search.replace(/^\?/, "");

の代わりに

window.location.substr(1);
19
Torsten Walter

_window.history.pushState_を使用してある程度の成功を収めました。 このStackOverflowの回答 を参照してください。単体テストごとに、関数setQueryString('var=something')を呼び出し、次のように実装します。

_function setQueryString(queryString) {
  window.history.pushState({}, '', '?' + queryString);
}
_

QUnit.moduleのafterEachメソッドを使用してクエリ文字列をクリアする必要があります。そうしないと、クエリ文字列が最終テストの値に設定され、奇妙な結果が得られます。

3
Sam Schneider