假设我有一个查询某些数据的服务并将其设置在控制器中,有点类似于:
(Method on controller)
DogService.query(function(data)){
if(data.isSuccess){
$scope.IloveDogs = true;
$scope.dogLovers += 1;
}
})
它是高度简化的,但是我在控制器中如何测试在调用模拟dogService
时它是否设置了正确的数据?
如果为了简单起见,我们说该函数不是异步的并且处理promises,我会创建并向控制器注入一个mock。模拟看起来像:
var DogService = {
query: function(){
return true;
}
}
遗憾的是,这不会运行$scope.IloveDogs
设置为true的代码,而dogLovers
会增加1。
任何想法,因为我宁愿不必将控制器中的代码从服务复制到模拟服务?
答案 0 :(得分:0)
这就是我通常在单元测试中模拟服务的方式。 (你没有提到你使用的测试框架,所以我假设Jasmine是目前最受欢迎的测试框架。)
我只是创建一个愚蠢的对象来充当我的模拟,然后只是Jasmine的内置间谍功能来决定它返回的内容。请注意,这是Jasmine 2.0的语法。
我使用$q
创建一个承诺,并确保我能够从我的测试中引用它,以便我可以解决它。
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();
});
});
对于不使用promises的服务,我可能会这样做:
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
块中,并且看起来一目了然比如它在测试中的样板较少,你最终会得到更多。
这就是为什么我更喜欢我的服务返回promises的原因之一,即使它们不是异步的。