La mia comprensione è che quando si carica il modulo nei test di unità angolari, viene chiamato il blocco run
.Come deve essere gestito il blocco di corsa nei test di unità angolari?
penserei che se si sta testando un componente, non si vorrebbe essere contemporaneamente testare il blocco run
perché unità test si suppone per testare un solo unità. È vero?
In tal caso, esiste un modo per impedire l'esecuzione del blocco run
? La mia ricerca mi porta a pensare che la risposta sia "no" e che il blocco run
venga sempre eseguito quando il modulo è caricato, ma forse c'è un modo per sovrascriverlo. In caso contrario, come potrei testare il blocco run
?
blocco Run:
function run(Auth, $cookies, $rootScope) {
$rootScope.user = {};
Auth.getCurrentUser();
}
Auth.getCurrentUser:
getCurrentUser: function() {
// user is logged in
if (Object.keys($rootScope.user).length > 0) {
return $q.when($rootScope.user);
}
// user is logged in, but page has been refreshed and $rootScope.user is lost
if ($cookies.get('userId')) {
return $http.get('/current-user')
.then(function(response) {
angular.copy(response.data, $rootScope.user);
return $rootScope.user;
})
;
}
// user isn't logged in
else {
return $q.when({});
}
}
auth.factory.spec.js
describe('Auth Factory', function() {
var Auth, $httpBackend, $rootScope, $cookies, $q;
var user = {
username: 'a',
password: 'password',
};
var response = {
_id: 1,
local: {
username: 'a',
role: 'user'
}
};
function isPromise(el) {
return !!el.$$state;
}
beforeEach(module('mean-starter', 'ngCookies', 'templates'));
beforeEach(inject(function(_Auth_, _$httpBackend_, _$rootScope_, _$cookies_, _$q_) {
Auth = _Auth_;
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
$cookies = _$cookies_;
$q = _$q_;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('#signup', function() {
$rootScope.user = {};
$httpBackend.expectPOST('/users', user).respond(response);
spyOn(angular, 'copy').and.callThrough();
spyOn($cookies, 'put').and.callThrough();
var retVal = Auth.signup(user);
$httpBackend.flush();
expect(angular.copy).toHaveBeenCalledWith(response, $rootScope.user);
expect($cookies.put).toHaveBeenCalledWith('userId', 1);
expect(isPromise(retVal)).toBe(true);
});
it('#login', function() {
$rootScope.user = {};
$httpBackend.expectPOST('/login', user).respond(response);
spyOn(angular, 'copy').and.callThrough();
spyOn($cookies, 'put').and.callThrough();
var retVal = Auth.login(user);
$httpBackend.flush();
expect(angular.copy).toHaveBeenCalledWith(response, $rootScope.user);
expect($cookies.put).toHaveBeenCalledWith('userId', 1);
expect(isPromise(retVal)).toBe(true);
});
it('#logout', function() {
$httpBackend.expectGET('/logout').respond();
spyOn(angular, 'copy').and.callThrough();
spyOn($cookies, 'remove');
Auth.logout();
$httpBackend.flush();
expect(angular.copy).toHaveBeenCalledWith({}, $rootScope.user);
expect($cookies.remove).toHaveBeenCalledWith('userId');
});
describe('#getCurrentUser', function() {
it('User is logged in', function() {
$rootScope.user = response;
spyOn($q, 'when').and.callThrough();
var retVal = Auth.getCurrentUser();
expect($q.when).toHaveBeenCalledWith($rootScope.user);
expect(isPromise(retVal)).toBe(true);
});
it('User is logged in but page has been refreshed', function() {
$cookies.put('userId', 1);
$httpBackend.expectGET('/current-user').respond(response);
spyOn(angular, 'copy').and.callThrough();
var retVal = Auth.getCurrentUser();
$httpBackend.flush();
expect(angular.copy).toHaveBeenCalledWith(response, $rootScope.user);
expect(isPromise(retVal)).toBe(true);
});
it("User isn't logged in", function() {
$rootScope.user = {};
$cookies.remove('userId');
spyOn($q, 'when').and.callThrough();
var retVal = Auth.getCurrentUser();
expect($q.when).toHaveBeenCalledWith({});
expect(isPromise(retVal)).toBe(true);
});
});
});
Tentativo 1:
beforeEach(module('mean-starter', 'ngCookies', 'templates'));
beforeEach(inject(function(_Auth_, _$httpBackend_, _$rootScope_, _$cookies_, _$q_) {
Auth = _Auth_;
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
$cookies = _$cookies_;
$q = _$q_;
}));
beforeEach(function() {
spyOn(Auth, 'getCurrentUser');
});
afterEach(function() {
expect(Auth.getCurrentUser).toHaveBeenCalled();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
Questo non funziona. Il blocco run
viene eseguito quando il modulo viene caricato, quindi Auth.getCurrentUser()
viene chiamato prima che la spia sia impostata.
Expected spy getCurrentUser to have been called.
Tentativo 2:
beforeEach(inject(function(_Auth_, _$httpBackend_, _$rootScope_, _$cookies_, _$q_) {
Auth = _Auth_;
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
$cookies = _$cookies_;
$q = _$q_;
}));
beforeEach(function() {
spyOn(Auth, 'getCurrentUser');
});
beforeEach(module('mean-starter', 'ngCookies', 'templates'));
afterEach(function() {
expect(Auth.getCurrentUser).toHaveBeenCalled();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
Questo non funziona perché Auth
non è disponibile per essere iniettato prima che il mio modulo di applicazione viene caricato.
Error: [$injector:unpr] Unknown provider: AuthProvider <- Auth
Tentativo 3:
Come potete vedere, c'è un problema di pollo uova qui. Ho bisogno di iniettare Auth e impostare la spia prima che il modulo sia caricato, ma non posso perché Auth non è disponibile per essere iniettato prima che il modulo sia caricato.
This post sul blog menziona il problema dell'uovo di gallina e offre un'interessante soluzione potenziale. L'autore propone di creare manualmente il mio servizio Auth
utilizzando $provide
prima del. Carico il mio modulo. Dato che sto creando il servizio, senza iniettarlo, potrei farlo prima che il modulo venga caricato, e potrei installare la spia. Quindi, quando il modulo viene caricato, usa questo servizio di simulazione creato.
ecco il suo codice di esempio:
describe('example', function() {
var loggingService;
beforeEach(function() {
module('example', function ($provide) {
$provide.value('loggingService', {
start: jasmine.createSpy()
});
});
inject(function (_loggingService_) {
loggingService = _loggingService_;
});
});
it('should start logging service', function() {
expect(loggingService.start).toHaveBeenCalled();
});
});
Il problema con questo, è che ho bisogno del mio servizio Auth
! Vorrei solo usare la simulazione per il blocco run
; Ho bisogno del mio vero servizio Auth
altrove per poterlo testare.
Immagino di poter creare il servizio Auth
effettivo utilizzando $provide
, ma ciò sembra sbagliato.
domanda finale - per qualsiasi codice finisco usando per affrontare questo problema run
blocco, c'è un modo per me per estrarre fuori in modo da non dover riscrivere per ogni mio file spec? L'unico modo in cui potrei pensare di farlo sarebbe usare una sorta di funzione globale.
auth.factory.js
angular
.module('mean-starter')
.factory('Auth', Auth)
;
function Auth($http, $state, $window, $cookies, $q, $rootScope) {
return {
signup: function(user) {
return $http
.post('/users', user)
.then(function(response) {
angular.copy(response.data, $rootScope.user);
$cookies.put('userId', response.data._id);
$state.go('home');
})
;
},
login: function(user) {
return $http
.post('/login', user)
.then(function(response) {
angular.copy(response.data, $rootScope.user);
$cookies.put('userId', response.data._id);
$state.go('home');
})
;
},
logout: function() {
$http
.get('/logout')
.then(function() {
angular.copy({}, $rootScope.user);
$cookies.remove('userId');
$state.go('home');
})
.catch(function() {
console.log('Problem logging out.');
})
;
},
getCurrentUser: function() {
// user is logged in
if (Object.keys($rootScope.user).length > 0) {
return $q.when($rootScope.user);
}
// user is logged in, but page has been refreshed and $rootScope.user is lost
if ($cookies.get('userId')) {
return $http.get('/current-user')
.then(function(response) {
angular.copy(response.data, $rootScope.user);
return $rootScope.user;
})
;
}
// user isn't logged in
else {
return $q.when({});
}
}
};
}
Edit - fallito tentativo + tentativo riuscito:
beforeEach(module('auth'));
beforeEach(inject(function(_Auth_) {
Auth = _Auth_;
spyOn(Auth, 'requestCurrentUser');
}));
beforeEach(module('mean-starter', 'ngCookies', 'templates'));
beforeEach(inject(function(_Auth_, _$httpBackend_, _$rootScope_, _$cookies_, _$q_) {
// Auth = _Auth_;
$httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
$cookies = _$cookies_;
$q = _$q_;
}));
// beforeEach(function() {
// spyOn(Auth, 'getCurrentUser');
// });
afterEach(function() {
expect(Auth.getCurrentUser).toHaveBeenCalled();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
Io non sono sicuro perché questo non avrebbe funzionato (indipendente dal problema con l'utilizzo di inject
due volte).
Stavo cercando di evitare di dover usare $provide
come inizialmente mi sentivo come un hacker/strano per me. Dopo averci pensato un po 'di più però, ora sento che $provide
va bene, e che seguire il tuo suggerimento di usare mock-auth
è fantastico !!! Entrambi hanno funzionato per me.
In auth.factory.spec.js
Ho appena caricato il modulo auth
(sto chiamando auth
, non mean-auth
) senza caricare mean-starter
. Questo non ha il problema del blocco run
perché quel modulo non ha il codice di blocco run
, ma mi consente di testare il mio factory Auth
. Altrove, questo funziona:
beforeEach(module('mean-starter', 'templates', function($provide) {
$provide.value('Auth', {
requestCurrentUser: jasmine.createSpy()
});
}));
così come il fantastico mock-auth
soluzione:
auth.factory.mock.js
angular
.module('mock-auth', [])
.factory('Auth', Auth)
;
function Auth() {
return {
requestCurrentUser: jasmine.createSpy()
};
}
user.service.spec.js
beforeEach(module('mean-starter', 'mock-auth', 'templates'));
off-topic alla tua domanda, ma dovresti sapere che '.run' non aspetta' $ http' per il completamento. Se qualcosa nell'app si basa sul risultato che c'è, hai una condizione di gara. In genere, si usa 'resolve' se si utilizza' ngRoute' o 'ui.router'. –