这就是我通常在单元测试中模拟服务的方式。
(你没有提到你使用的测试框架,所以我假设 Jasmine 是目前最流行的)。
我只是创建了一个哑对象作为我的模拟对象,然后只是 Jasmine 的内置间谍功能来指示它返回的内容。请注意,这是 Jasmine 2.0 的语法。
我使用$q 创建一个promise,并确保我能够从我的测试中引用它,以便解决它。
describe('Spec', function() {
var scope;
var catServiceMock;
var deferredCatCall;
beforeEach(module('myModule'));
beforeEach(inject(function($controller, $rootScope, $q) {
scope = $rootScope;
//Create a mock and spy on it to return a promise
deferredCatCall = $q.defer();
catServiceMock = {
query: function() {}
};
spyOn(catServiceMock, 'query').and.returnValue(deferredCatCall.promise);
//Inject the mock into the controller
$controller('MyCtrl', {
$scope: scope,
catService: catServiceMock
});
}));
it('proves that cats are better than dogs', function() {
//resolve the promise that was returned by the mock
deferredCatCall.resolve({
isSuccess: true
});
//Need to trigger a $digest loop so angular process the resolved promise
scope.$digest();
//Check that the controller callback did something
expect(scope.iLoveCats).toBeTruthy();
});
});
对于不使用承诺的服务,我可能会这样做:
describe('Spec', function() {
var scope;
var catServiceMock;
beforeEach(module('myModule'));
beforeEach(inject(function($controller, $rootScope, $q) {
scope = $rootScope;
//Create a mock and spy on it to return a value
catServiceMock = {
query: function() {}
};
spyOn(catServiceMock, 'query').and.returnValue({
isSuccess: true
});
//Inject the mock into the controller
$controller('MyCtrl', {
$scope: scope,
catService: catServiceMock
});
}));
it('proves that cats are better than dogs', function() {
//Check that the controller callback did something
expect(scope.iLoveCats).toBeTruthy();
});
});
这种方法的主要问题是您必须在实例化控制器之前规定服务将返回什么。这意味着,如果您想测试控制器对从服务接收到的不同数据的行为方式,您将不得不将多个 beforeEach 块嵌套在不同的 describe 块中,虽然它看起来不像样板文件在测试中你会得到更多。
这就是为什么我更喜欢我的服务返回承诺的原因之一,即使它们不是异步的。