在为模拟依赖项创建提供程序时遇到了麻烦,因为它需要使用$q
,这是angular中的另一个服务,并且在设置提供程序时无法访问这些服务。
想象一下,我们有一个我们想要测试的工厂:
angular.module('myApp').factory('MyFactory', function (MyDependency, $q) {
return {
doSomething: function () {
var deferred = $q.defer();
MyDependency.doAction().then(function (response) {
deferred.resolve(response);
// other events
}, function (error) {
deferred.reject(error);
// other events
});
return deferred.promise;
}
}
});
以下单元测试:
describe('Service: MyFactory', function () {
var myDependency, myFactory;
beforeEach(module('myApp'));
// The problem is here, as $q cannot be instantiated
// when setting up providers, and our mock service we are
// creating as the dependency for MyFactory requires $q
beforeEach(module(function ($provide, $q) {
var promise = $q.defer().promise;
myDependency = jasmine.createSpyObj('MyDependency', ['open']);
myDependency.open.andReturn(promise);
$provide.value('MyDependency', {
doAction: myDependency.open
});
}));
beforeEach(inject(function (MyFactory) {
myFactory = MyFactory;
}));
describe('MyDependency.doAction should be called', function () {
myFactory.doSomething();
expect(myDependency.open).toHaveBeenCalled();
// expect other events
});
});
MyDependency
有一个函数open
,我们需要使用自定义承诺来监视和覆盖该方法,我们将控制正在解析和拒绝的数据。我们可以轻松创建将注入MyFactory
的模拟依赖项,但是如何在此阶段访问其他服务,例如$q
?
我提出的唯一合理的解决方案就是像这样设置提供程序,但与promise.reject()promise.resolve相比,它为我们提供了更少的控制权和更多的解决方案来处理成功与失败。 )
beforeEach(module(function ($provide) {
myDependency = jasmine.createSpyObj('MyDependency', ['doAction']);
myDependency.doAction.andCallFake(function (){
return {
then: function (success, err){
success.call(this);
}
};
});
$provide.value('MyDependency', {
open: myDependency.open
});
}));
答案 0 :(得分:2)
最奇怪的一行是$provide.value('MyDependency', {});
。这是概念证明,欢迎提出任何意见。
(function() {
angular.module('myApp', []).factory('MyFactory', function(MyDependency, $q) {
return {
doSomething: function() {
var deferred = $q.defer();
MyDependency.doAction().then(function(response) {
deferred.resolve(response);
}, function(error) {
deferred.reject(error);
});
return deferred.promise;
}
};
});
})();
describe('myApp', function() {
var MyFactory, MyDependency = {},
$q, scope;
beforeEach(function() {
module('myApp');
});
beforeEach(function() {
module(function($provide) {
$provide.value('MyDependency', {});
});
});
beforeEach(inject(function(_MyFactory_, _$q_, $rootScope) {
MyFactory = _MyFactory_;
$q = _$q_;
scope = $rootScope.$new();
}));
describe('MyDependency', function() {
var MyDependencyDefer;
beforeEach(inject(function(MyDependency, $q) {
MyDependencyDefer = $q.defer();
MyDependency.doAction = jasmine.createSpy('MyDependency.doAction').and.returnValue(MyDependencyDefer.promise);
}));
it('resolves doAction()', function() {
var stubSuccess = 'mock data';
var doSomethingDefer = MyFactory.doSomething();
MyDependencyDefer.resolve(stubSuccess);
doSomethingDefer.then(function(r) {
expect(r).toBe(stubSuccess);
});
scope.$digest();
});
it('rejects doAction()', function() {
var stubError = 'reason error';
var doSomethingDefer = MyFactory.doSomething();
MyDependencyDefer.reject(stubError);
doSomethingDefer.catch(function(r) {
expect(r).toBe(stubError);
});
scope.$digest();
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>
(() => {
angular.module('myApp', []).factory('MyFactory', ['MyDependency', MyDependency => {
return {
doSomething: () => MyDependency.doAction()
}
}])
})()
describe('MyFactory.doSomething() returns MyDependency.doAction()', () => {
'use strict';
const promiseResponse = {
succ: 'success',
err: 'error'
}
let MyFactory, MyDependency = {},
$q, scope
beforeEach(module('myApp'));
beforeEach(() => {
module(['$provide', $provide => {
$provide.value('MyDependency', {})
}]);
});
beforeEach(inject(['MyFactory', '$q', '$rootScope', (_MyFactory_, _$q_, $rootScope) => {
MyFactory = _MyFactory_
$q = _$q_
scope = $rootScope.$new()
}]));
describe('MyDependency.doAction() returns promise', () => {
let MyDependencyDefer, doSomethingDefer
beforeEach(inject(['MyDependency', '$q', (MyDependency, $q) => {
MyDependencyDefer = $q.defer()
MyDependency.doAction = jasmine.createSpy('MyDependency.doAction').and.returnValue(MyDependencyDefer.promise)
}]))
beforeEach(() => {
doSomethingDefer = MyFactory.doSomething()
})
it('that can be resolved', done => {
MyDependencyDefer.resolve(promiseResponse.succ)
doSomethingDefer.then(r => {
expect(r).toBe(promiseResponse.succ)
done()
});
scope.$digest()
});
it('that can be rejected', done => {
MyDependencyDefer.reject(promiseResponse.err)
doSomethingDefer.catch(r => {
expect(r).toBe(promiseResponse.err)
done()
})
scope.$digest()
})
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>