[javascript] 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()
. 또한 mock 주입은에서 module()
진행되어야합니다 inject()
. 그러나 일단 변경하면 모의 값이 업데이트되지 않습니다.
이를 수행하는 올바른 방법은 무엇입니까?
답변
왜 그렇게했는지 모르겠지만 일반적으로 spyOn
기능 으로 수행합니다 . 이 같은:
describe('Testing remote call returning promise', function() {
var myService;
beforeEach(module('app.myService'));
beforeEach(inject( function(_myService_, myOtherService, $q){
myService = _myService_;
spyOn(myOtherService, "makeRemoteCallReturningPromise").and.callFake(function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
return deferred.promise;
});
}
it('can do remote call', inject(function() {
myService.makeRemoteCall()
.then(function() {
console.log('Success');
});
}));
또한 당신이해야 할 것을 기억 $digest
에 대한 호출을 then
함수가 호출 될 수 있습니다. $ 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();
});
});
답변
sinon과 같은 스터 빙 라이브러리를 사용하여 서비스를 조롱 할 수 있습니다. 그런 다음 $ q.when ()을 약속으로 반환 할 수 있습니다. 범위 오브젝트의 값이 약속 결과에서 나온 경우 scope. $ root. $ digest ()를 호출해야합니다.
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 })
);
답변
솔직히 .. 모듈 대신 서비스를 모의하기 위해 인젝션에 의존하여 잘못된 방법으로 가고 있습니다. 또한 beforeEach에서 inject를 호출하는 것은 테스트 기준으로 조롱하기가 어렵 기 때문에 안티 패턴입니다.
여기 내가 어떻게 할 것입니다 …
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;
};
});
});
이제 서비스를 주입하면 적절하게 조롱하여 사용할 수 있습니다.
답변
유용하고 유용한 서비스 함수를 sinon.stub (). returns ($ 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' );