Как я могу издеваться над сервисом, который возвращает обещание в модульном тесте Angularjs Jasmine?
у меня есть myService, который использует myOtherService, который делает удаленный вызов, возвращая обещание:
angular.module('app.myService', ['app.myOtherService'])
.factory('myService', [myOtherService,
function(myOtherService) {
function makeRemoteCall() {
return myOtherService.makeRemoteCallReturningPromise();
}
return {
makeRemoteCall: makeRemoteCall
};
}
])
чтобы сделать модульный тест для myService мне нужно, чтобы поглумиться myOtherService, такой, что его makeRemoteCallReturningPromise() метод возвращает обещание. Вот как я это делаю:
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');
});
}));
как вы можете видеть из вышеизложенного, определение моего макета зависит от $q, который я должен загрузить с помощью inject(). Кроме того, инъекция макета должна происходить в module(), который должен прийти до
inject(). Однако, значение макет не обновляется после того, как я изменить его.
Как правильно это сделать?
8 ответов:
Я не уверен, почему так, как вы сделали это не работает, но я обычно делаю это с на $q документация.
------редактировать------
присмотревшись к тому, что вы делаете, я думаю, что вижу проблему в вашем коде. В
beforeEach, выmyOtherServiceMockк совершенно новому объекту. Элемент$provideникогда не будет видеть эту ссылку. Вам просто нужно обновить существующую ссылку:beforeEach(inject( function(_myService_, $q){ myService = _myService_; myOtherServiceMock.makeRemoteCallReturningPromise = function() { var deferred = $q.defer(); deferred.resolve('Remote call result'); return deferred.promise; }; }
мы также можем написать реализацию Жасмин возвращения обещание непосредственно шпионом.
spyOn(myOtherService, "makeRemoteCallReturningPromise").andReturn($q.when({}));Жасмин 2:
spyOn(myOtherService, "makeRemoteCallReturningPromise").and.returnValue($q.when({}));(скопировано из комментариев, благодаря ccnokes)
describe('testing a method() on a service', function () { var mock, service function init(){ return angular.mock.inject(function ($injector,, _serviceUnderTest_) { mock = $injector.get('service_that_is_being_mocked');; service = __serviceUnderTest_; }); } beforeEach(module('yourApp')); beforeEach(init()); it('that has a then', function () { //arrange var spy= spyOn(mock, 'actionBeingCalled').and.callFake(function () { return { then: function (callback) { return callback({'foo' : "bar"}); } }; }); //act var result = service.actionUnderTest(); // does cleverness //assert expect(spy).toHaveBeenCalled(); }); });
вы можете использовать библиотеку stubbing, такую как sinon, чтобы издеваться над вашим сервисом. Затем вы можете вернуть $q. when () в качестве своего обещания. Если значение объекта scope исходит из результата promise, вам нужно будет вызвать scope.$корень.$дайджест.)(
var scope, controller, datacontextMock, customer; beforeEach(function () { module('app'); inject(function ($rootScope, $controller,common, datacontext) { scope = $rootScope.$new(); var $q = common.$q; datacontextMock = sinon.stub(datacontext); customer = {id:1}; datacontextMock.customer.returns($q.when(customer)); controller = $controller('Index', { $scope: scope }); }) }); it('customer id to be 1.', function () { scope.$root.$digest(); expect(controller.customer.id).toBe(1); });
используя
sinon:const mockAction = sinon.stub(MyService.prototype,'actionBeingCalled') .returns(httpPromise(200));известно, что
httpPromiseможно :const httpPromise = (code) => new Promise((resolve, reject) => (code >= 200 && code <= 299) ? resolve({ code }) : reject({ code, error:true }) );
честно.. вы идете по этому неправильному пути, полагаясь на inject, чтобы издеваться над сервисом вместо модуля. Кроме того, вызов inject в beforeEach является анти-шаблоном, поскольку он затрудняет насмешку на основе каждого теста.
вот как я бы это делал...
module(function ($provide) { // By using a decorator we can access $q and stub our method with a promise. $provide.decorator('myOtherService', function ($delegate, $q) { $delegate.makeRemoteCallReturningPromise = function () { var dfd = $q.defer(); dfd.resolve('some value'); return dfd.promise; }; }); });теперь, когда вы вводите свой сервис, у него будет правильно издевательский метод для использования.
Я нашел эту полезную, колющую функцию обслуживания как Синон.окурок.)(возвращает ($q. when ({})):
:this.myService = { myFunction: sinon.stub().returns( $q.when( {} ) ) }; this.scope = $rootScope.$new(); this.angularStubs = { myService: this.myService, $scope: this.scope }; this.ctrl = $controller( require( 'app/bla/bla.controller' ), this.angularStubs );this.someMethod = function(someObj) { myService.myFunction( someObj ).then( function() { someObj.loaded = 'bla-bla'; }, function() { // failure } ); };и тест
const obj = { field: 'value' }; this.ctrl.someMethod( obj ); this.scope.$digest(); expect( this.myService.myFunction ).toHaveBeenCalled(); expect( obj.loaded ).toEqual( 'bla-bla' );
фрагмент кода:
spyOn(myOtherService, "makeRemoteCallReturningPromise").and.callFake(function() { var deferred = $q.defer(); deferred.resolve('Remote call result'); return deferred.promise; });можно записать в более сжатой форме:
spyOn(myOtherService, "makeRemoteCallReturningPromise").and.returnValue(function() { return $q.resolve('Remote call result'); });
Comments