44

Questo è un controller con una funzione di conferma:

$scope.submit = function(){ 

$http.post('/api/project', $scope.project) 
     .success(function(data, status){ 
     $modalInstance.dismiss(true); 
     }) 
     .error(function(data){ 
     console.log(data); 
     }) 
    } 
} 

Questa è la mia prova

it('should make a post to /api/project on submit and close the modal on success', function() { 
    scope.submit(); 

    $httpBackend.expectPOST('/api/project').respond(200, 'test'); 

    $httpBackend.flush(); 

    expect(modalInstance.dismiss).toHaveBeenCalledWith(true); 
    }); 

L'errore che ottengo è:

Error: Unexpected request: GET views/appBar.html 

viste/appBar.html è il mio TemplateURL:

.state('project', { 
    url: '/', 
    templateUrl:'views/appBar.html', 
    controller: 'ProjectsCtrl' 
    }) 

Quindi in qualche modo ui-router sta facendo il mio $ httpBackend punta a questo invece della mia funzione di invio. Ho lo stesso problema in tutti i miei test usando $ httpBackend.

C'è qualche soluzione a questo?

+2

Problema correlato all'interfaccia utente n. 212 con alcuni suggerimenti: https://github.com/angular-ui/ui-router/issues/212#issuecomment-69974072 –

risposta

48

prendere questa sostanza https://gist.github.com/wilsonwc/8358542

angular.module('stateMock',[]); 
angular.module('stateMock').service("$state", function($q){ 
    this.expectedTransitions = []; 
    this.transitionTo = function(stateName){ 
     if(this.expectedTransitions.length > 0){ 
      var expectedState = this.expectedTransitions.shift(); 
      if(expectedState !== stateName){ 
       throw Error("Expected transition to state: " + expectedState + " but transitioned to " + stateName); 
      } 
     }else{ 
      throw Error("No more transitions were expected! Tried to transition to "+ stateName); 
     } 
     console.log("Mock transition to: " + stateName); 
     var deferred = $q.defer(); 
     var promise = deferred.promise; 
     deferred.resolve(); 
     return promise; 
    } 
    this.go = this.transitionTo; 
    this.expectTransitionTo = function(stateName){ 
     this.expectedTransitions.push(stateName); 
    } 

    this.ensureAllTransitionsHappened = function(){ 
     if(this.expectedTransitions.length > 0){ 
      throw Error("Not all transitions happened!"); 
     } 
    } 
}); 

aggiungerlo a un file chiamato stateMock nella cartella test/finto, includere il file nella configurazione del karma, se non è già preso.

La configurazione prima che il test dovrebbe quindi essere simile a questa:

beforeEach(module('stateMock')); 

// Initialize the controller and a mock scope 
beforeEach(inject(function ($state //other vars as needed) { 
    state = $state; 
    //initialize other stuff 
} 

Poi, nel tuo test si dovrebbe aggiungere

state.expectTransitionTo('project'); 
+2

Impressionante! Stavo quasi saltando l'ui-router tutti insieme. Ottimo lavoro! Dovresti condividere questo lavoro con gli ui-router in modo che le persone possano trovarlo meglio. – Per

+1

@ user1572526 sì Fantastico! +1 rosswil solo un enorme mal di testa ^^ mi sembra strano non vedere solo un sacco di gente qui ... – Whisher

+1

Wow, questo deve essere condiviso con l'ui-router, ho passato ore a cercare di capire perché commentare il mio $ urlRouterProvider stava correggendo i miei test. Grazie rosswil e Whisher per avermi indicato qui !!! – evkline

38

Questo problema Github circa Unit Testing UI Router spiega più a fondo cosa sta succedendo.

Il problema è che $httpBackend.flush() attiva una trasmissione che attiva quindi il caso dello stateProvider.

Una soluzione semplice può essere la seguente configurazione, come indicato da @darinclark nel thread Github menzionato sopra. Questo è valido se non è necessario testare le transizioni di stato. Altrimenti dai uno sguardo a @rosswil's answer ispirato allo @Vratislav answer on Github.

beforeEach(module(function ($urlRouterProvider) { 
    $urlRouterProvider.otherwise(function(){return false;}); 
})); 

a cura

Grazie a Chris T per segnalare questa nei commenti, sembra dopo v0.2.14? il modo migliore per farlo è utilizzare

beforeEach(module(function($urlRouterProvider) { 
    $urlRouterProvider.deferIntercept(); 
})); 
+11

UI-Router ora ha una funzione '$ urlRouterProvider.deferIntercept()'. Vedere https://github.com/angular-ui/ui-router/issues/212#issuecomment-69974072 –

+1

Questa è la soluzione più rapida –

+0

@ChrisT, grazie! La tua soluzione mi ha aiutato. – smalone

1

Spostare i propri servizi sul proprio modulo che non hanno alcuna dipendenza da ui.router. la tua app principale dipende da questo modulo. Quando esegui il test, non testare l'app principale, verifica il modulo in cui sono presenti i tuoi servizi. Il provider di stato non tenterà di cambiare stato/percorso perché questo modulo non sa nulla di ui.router. Questo ha funzionato per me.

3

Se non si desidera aggiungere i file GIST, come si dice nella soluzione corretta è possibile aggiungere un "quando" condizione al tuo $ httpBackend di ignorare GET petizioni di punti di vista in questo modo:

$httpBackend.when("GET", function (url) { 
    // This condition works for my needs, but maybe you need to improve it 
    return url.indexOf(".tpl.html") !== -1; 
}).passThrough(); 
+1

Ho ottenuto l'errore '.passThrough non è una funzione ', non so perché. Questo ha funzionato per me: '$ httpBackend.whenGET (/ templates \ /.*/). Respond ('');' –

+0

Puoi vedere qui la documentazione, passThrough è una funzione valida: https: //docs.angularjs. org/api/ngMockE2E/service/$ httpBackend È una funzione di requestHandler che è la risposta della funzione "when". –

+0

Anche io ho ottenuto .passThrough non è un errore di funzione. ".respond" ha funzionato bene però. –

2

I hai lo stesso errore che hai commentato, dopo un servizio di chiamata mi chiedono dell'URL di altrimenti ui-route.

per risolvere il problema della chiamata altrimenti ui-percorso in fase di test è Non iniettare $ Stato in beforeach statment. Nel mio test $ state non ha senso usarlo.