79 votes

simuler window.location.href en Javascript

J'ai quelques tests unitaires pour une fonction qui utilise window.location.href -- ce n'est pas l'idéal, j'aurais préféré passer cette valeur mais ce n'est pas possible dans l'implémentation. Je me demande simplement s'il est possible de simuler cette valeur sans que la page de mon programme de test aille réellement vers l'URL.

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

J'utilise jasmine pour mon cadre de test unitaire.

1 votes

J'ai le même problème lorsque je veux appeler une fonction à l'intérieur de l'objet with. Comment le résoudre ?

58voto

Kurt Harriger Points 812

La meilleure façon de procéder est de créer une fonction d'aide quelque part, puis de la simuler :

 var mynamespace = mynamespace || {};
    mynamespace.util = (function() {
      function getWindowLocationHRef() {
          return window.location.href;
      }
      return { 
        getWindowLocationHRef: getWindowLocationHRef
      }
    })();

Maintenant, au lieu d'utiliser window.location.href directement dans votre code, utilisez simplement ceci à la place. Vous pourrez alors remplacer cette méthode chaque fois que vous aurez besoin de retourner une valeur simulée :

mynamespace.util.getWindowLocationHRef = function() {
  return "http://mockhost/mockingpath" 
};

Si vous voulez une partie spécifique de l'emplacement de la fenêtre, comme un paramètre de chaîne de requête, créez des méthodes d'aide pour cela aussi et gardez l'analyse syntaxique en dehors de votre code principal. Certains frameworks comme Jasmine ont des espions de test qui peuvent non seulement simuler la fonction pour qu'elle renvoie les valeurs souhaitées, mais aussi vérifier qu'elle a été appelée :

spyOn(mynamespace.util, 'getQueryStringParameterByName').andReturn("desc");
//...
expect(mynamespace.util.getQueryStringParameterByName).toHaveBeenCalledWith("sort");

23voto

cburgmer Points 727

Je propose deux solutions qui ont déjà été évoquées dans des messages précédents :

  • Créez une fonction autour de l'accès, utilisez-la dans votre code de production, et bloquez-la avec Jasmine dans vos tests :

    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");
  • Utilisez un injection de dépendances et injecter un faux dans votre test :

    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 votes

Dans Jasmine 2.x, la syntaxe utilisée dans le premier exemple semble avoir un peu changé. .andReturn() doit être remplacé par .and.returnValue() .

11voto

Andris Points 6932

Vous devez simuler le contexte local et créer votre propre version de window y window.location objets

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/...

Si vous utilisez with alors toutes les variables (y compris window !) sera d'abord recherché dans l'objet contexte et, s'il n'est pas présent, dans le contexte actuel.

30 votes

'with' statements are not allowed in strict mode.

0 votes

Il ne me semble pas que vous puissiez écrire des tests lisibles avec. Je pense que la solution proposée par @Kurt Harriger est bien meilleure. -1

2 votes

L'instruction with(obj) {} est dépréciée et, en tant que telle, n'est pas valide en mode strict.

6voto

Jim Isaacs Points 43

Il peut arriver que vous ayez une bibliothèque qui modifie window.location et que vous souhaitiez qu'elle puisse fonctionner normalement mais aussi être testée. Si c'est le cas, vous pouvez utiliser une fermeture pour passer la référence souhaitée à votre bibliothèque, comme ceci.

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

Ensuite, dans votre test, avant d'inclure votre bibliothèque, vous pouvez redéfinir self globalement, et la bibliothèque utilisera le mock self comme vue.

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

Dans votre bibliothèque, vous pouvez également faire quelque chose comme ce qui suit, afin de pouvoir redéfinir self à tout moment du test :

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

Dans la plupart, sinon la totalité, des navigateurs modernes, self est déjà une référence à window par défaut. Sur les plateformes qui implémentent l'API Worker, dans un Worker, self est une référence à la portée globale. Dans node.js, self et window ne sont pas définis, donc si vous le souhaitez, vous pouvez également procéder ainsi :

self || window || global

Cela peut changer si node.js implémente réellement l'API Worker.

4voto

user566245 Points 808

Voici l'approche que j'ai adoptée pour simuler window.location.href et/ou tout autre élément qui peut concerner un objet global.

Tout d'abord, plutôt que d'y accéder directement, encapsulez-le dans un module où l'objet est conservé avec un getter et un setter. Voici mon exemple. J'utilise require, mais ce n'est pas nécessaire ici.

define(["exports"], function(exports){

  var win = window;

  exports.getWindow = function(){
    return win;
  };

  exports.setWindow = function(x){
    win = x;
  }

});

Maintenant, là où vous avez normalement fait dans votre code quelque chose comme window.location.href maintenant vous feriez quelque chose comme :

var window = global_window.getWindow();
var hrefString = window.location.href;

Enfin, la configuration est terminée et vous pouvez tester votre code en remplaçant l'objet fenêtre par un faux objet que vous souhaitez voir à sa place.

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

Cela modifierait le win dans le module fenêtre. Elle était à l'origine définie sur la variable globale window mais il n'est pas défini sur l'objet de la fausse fenêtre que vous avez inséré. Donc maintenant, après que vous l'ayez remplacé, le code va récupérer votre objet fausse fenêtre et son faux href que vous aviez mis.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X