2011-01-25 18 views
53

mi hanno alcuni test di unità per una funzione che fa uso della window.location.href - non è l'ideale Preferirei di gran lunga hanno superato questo, ma la sua non è possibile per l'attuazione. Mi stavo solo chiedendo se è possibile prendere in giro questo valore senza in effetti far sì che la mia pagina di test runner vada effettivamente all'URL.beffardo window.location.href in Javascript

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

Utilizzo il gelsomino per il mio framework di test delle unità.

+1

io avendo lo stesso problema quando voglio chiamare una funzione all'interno del con. Come lo risolvi? – wizztjh

risposta

6

È necessario simulare il contesto locale e creare la propria versione di window e window.location oggetti

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

Se si utilizza with poi tutte le variabili (compresi window!) Sarà in primo luogo essere guardato dall'oggetto contesto e se non presente quindi dal contesto attuale.

+1

questa soluzione funzionerà in angular2 con dattiloscritto –

+5

'' con 'le istruzioni non sono consentite in modalità rigorosa.' – isherwood

+0

Non mi sembra che tu possa scrivere test leggibili con esso. Penso che la soluzione proposta da @Kurt Harriger sia decisamente migliore. -1 –

42

Il modo migliore per farlo è quello di creare una funzione di supporto da qualche parte e poi prendere in giro che:

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

Ora, invece di utilizzare window.location.href direttamente nel codice è sufficiente utilizzare questo invece. Poi si sostituisce questo metodo ogni volta che è necessario restituire un valore deriso:

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

Se si desidera una parte specifica della posizione della finestra, come un parametro di stringa di query quindi creare metodi di supporto anche per questo e mantenere il parsing di il tuo codice principale Alcuni quadri come il gelsomino hanno spie di test che possono non solo prendere in giro il funzione per restituire valori desiderati, ma può anche verificato è stato chiamato:

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

A volte si può avere una libreria che modifica window.location e si desidera consentire che funzioni normalmente ma anche essere testato. In tal caso, puoi utilizzare una chiusura per passare il riferimento desiderato alla tua biblioteca come questa.

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

Poi nel test, prima di includere la libreria, è possibile ridefinire sé a livello globale, e la biblioteca utilizzerà il sé finto come la vista.

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

Nella libreria, si può anche fare qualcosa di simile a quanto segue, quindi si può ridefinire sé in qualsiasi punto del test:

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

In maggior parte se non tutti i browser moderni, l'auto è già un riferimento alla finestra per impostazione predefinita. Nelle piattaforme che implementano l'API Worker, all'interno di un lavoratore autonomo è un riferimento all'ambito globale. In node.js sia di auto e la finestra non sono definiti, quindi se si vuole si può anche fare questo:

self || window || global 

la situazione potrebbe cambiare se node.js realmente implementare l'API Worker.

17

vorrei proporre due soluzioni che sono già state accennate nei post precedenti qui:

  • creare una funzione intorno l'accesso, l'uso che nel codice di produzione, e stub questo con Jasmine nei test:

    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"); 
    
  • Utilizzare un dependency injection e iniettare un falso nel 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

In Jasmine 2.x la sintassi utilizzata nel primo esempio sembra essere leggermente cambiata. '.andReturn()' dovrebbe essere sostituito da '.and.returnValue()'. –

3

Di seguito è l'approccio che ho preso per deridere window.location.href e/o qualsiasi altra cosa che potrebbe essere su un oggetto globale.

In primo luogo, piuttosto che accedervi direttamente, incapsularlo in un modulo in cui l'oggetto viene mantenuto con un getter e un setter. Di seguito è il mio esempio. Sto usando require, ma non è necessario qui.

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

    var win = window; 

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

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

}); 

Ora, dove si hanno normalmente fatto nel codice qualcosa come window.location.href, ora si dovrebbe fare qualcosa di simile:

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

Infine l'installazione è completa e si può testare il codice sostituendo la finestra oggetto con un oggetto falso che vuoi essere al suo posto invece.

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

Questo cambierebbe la variabile win nel modulo finestra. Inizialmente era impostato sull'oggetto globale window, ma non è impostato sull'oggetto della finta finestra che hai inserito. Quindi, dopo averlo sostituito, il codice otterrà il tuo finto oggetto della finestra e il suo falso href che l'hai messo.

0

IMO, questa soluzione è un piccolo miglioramento di cburgmer in quanto consente di sostituire window.location.href con $ window.location.href nel sorgente. Certo, sto usando Karma e non Jasmine, ma credo che questo approccio funzionerebbe con entrambi. E ho aggiunto una dipendenza dal sinon.

Prima un servizio/Singleton:

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

ora posso impostare location.href nel codice iniettando windowInjectable() da $ finestra come questa:

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

e beffardo fuori in un unit test sembra:

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

Il getter/setter sintassi risale a IE 9, ed è comunque ampiamente supportato secondo https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set

-1

È necessario simulare window.location.href mentre si è sulla stessa pagina. Nel mio caso, questo snipped ha funzionato perfettamente:

$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 
Problemi correlati