Mam myService
to zastosowania myOtherService
, które wykonuje zdalne połączenie, zwracając obietnicę:
angular.module('app.myService', ['app.myOtherService'])
.factory('myService', [
myOtherService,
function(myOtherService) {
function makeRemoteCall() {
return myOtherService.makeRemoteCallReturningPromise();
}
return {
makeRemoteCall: makeRemoteCall
};
}
])
Aby wykonać test jednostkowy myService
, muszę mock myOtherService
, tak aby jego makeRemoteCallReturningPromise
metoda zwracała obietnicę. Oto jak to robię:
describe('Testing remote call returning promise', function() {
var myService;
var myOtherServiceMock = {};
beforeEach(module('app.myService'));
// I have to inject mock when calling module(),
// and module() should come before any inject()
beforeEach(module(function ($provide) {
$provide.value('myOtherService', myOtherServiceMock);
}));
// However, in order to properly construct my mock
// I need $q, which can give me a promise
beforeEach(inject(function(_myService_, $q){
myService = _myService_;
myOtherServiceMock = {
makeRemoteCallReturningPromise: function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
return deferred.promise;
}
};
}
// Here the value of myOtherServiceMock is not
// updated, and it is still {}
it('can do remote call', inject(function() {
myService.makeRemoteCall() // Error: makeRemoteCall() is not defined on {}
.then(function() {
console.log('Success');
});
}));
Jak widać z powyższego, definicja mojej makiety zależy od tego $q
, którą mam załadować za pomocą inject()
. Co więcej, wstrzyknięcie makiety powinno mieć miejsce module()
, co powinno nastąpić wcześniej inject()
. Jednak wartość makiety nie jest aktualizowana po jej zmianie.
Jaki jest właściwy sposób, aby to zrobić?
javascript
angularjs
unit-testing
mocking
jasmine
Georgii Oleinikov
źródło
źródło
myService.makeRemoteCall()
? Jeśli tak, problem polega namyService
tymmakeRemoteCall
, że nie masz nic wspólnego z wyśmiewanymmyOtherService
.Odpowiedzi:
Nie jestem pewien, dlaczego sposób, w jaki to zrobiłeś, nie działa, ale zwykle robię to z
spyOn
funkcją. Coś takiego:Pamiętaj również, że będziesz musiał wykonać
$digest
wywołaniethen
funkcji, która ma zostać wywołana. Zobacz sekcję Testowanie w dokumentacji $ q .------EDYTOWAĆ------
Po bliższym przyjrzeniu się temu, co robisz, wydaje mi się, że widzę problem w Twoim kodzie. W programie
beforeEach
ustawiaszmyOtherServiceMock
się na zupełnie nowy obiekt.$provide
Nigdy nie zobaczy tego odniesienia. Wystarczy zaktualizować istniejące odniesienie:źródło
andCallFake
możesz użyćandReturnValue(deferred.promise)
(luband.returnValue(deferred.promise)
w Jasmine 2.0+). Oczywiście musisz to określić,deferred
zanim zadzwoniszspyOn
.$digest
w tym przypadku, gdy nie masz dostępu do zakresu?$rootScope
i wywołujesz$digest
to.$q.when()
codelord.net/2015/09/24/$q-dot-defer-youre-doing-it-wrongMożemy również napisać wykonanie jaśminowej obietnicy zwrotu bezpośrednio przez szpiega.
Dla Jasmine 2:
(skopiowane z komentarzy dzięki ccnokes)
źródło
spyOn(myOtherService, "makeRemoteCallReturningPromise").and.returnValue($q.when({}));
właśnie zabiłem pół godziny, zastanawiając się nad tym.źródło
Możesz użyć biblioteki do krojenia, takiej jak sinon, aby kpić z twojej usługi. Następnie możesz zwrócić $ q.when () jako swoją obietnicę. Jeśli wartość obiektu zakresu pochodzi z wyniku obietnicy, będziesz musiał wywołać zakres. $ Root. $ Digest ().
źródło
$rootScope.$digest()
do rozwiązania obietnicyużywając
sinon
:Wiadomo, że
httpPromise
może to być:źródło
Szczerze ... postępujesz w ten sposób w niewłaściwy sposób, polegając na wstrzyknięciu do fałszywej usługi zamiast modułu. Ponadto wywołanie funkcji inject w beforeEach jest anty-wzorcem, ponieważ utrudnia kpienie na podstawie testu.
Oto jak bym to zrobił ...
Teraz, gdy wstrzykniesz swoją usługę, będzie ona miała odpowiednio wyszydzoną metodę użycia.
źródło
Znalazłem tę przydatną, przeszywającą funkcję usługi jako sinon.stub (). Zwraca ($ q.when ({})):
kontroler:
i przetestuj
źródło
Fragment kodu:
Można zapisać w bardziej zwięzłej formie:
źródło