Как я могу издеваться над сервисом, который возвращает обещание в модульном тесте 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(). Однако, значение макет не обновляется после того, как я изменить его.



Как правильно это сделать?

615   8  

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

    Ничего не найдено.