我有一个调用Service的控制器,它是Resource的包装器。像这样:
app.factory("Service", ["$resource", function ($resource) {
return $resource("/api/Service/get");
}]);
将服务的返回值分配给控制器内的变量。通常,变量的类型为Resource
,并且包含promise
。解析promise后,将使用从后端返回的所有值填充变量。
我在promise上跟踪然后以修改从后端收到的模型。像这样:
this.model = Service.get();
this.model.$promise.then(function(data) {
// do something with data
});
我需要在控制器中测试生成的模型变量的值。
我发现这样做的唯一方法是使用 $ httpBackend 来实现我的服务。但是,这很难看,因为在测试我的控制器时,我必须将请求路径"api/Service/get"
传递给 httpBackend.when(),以便它以某些值响应。
我的测试摘录:
// call Controller
$httpBackend.when('GET', '/api/Service/get').respond(someData);
$httpBackend.flush();
expect(scope.model.property).toBe(null);
这似乎和完全错误。使用单独的服务来处理资源的全部意义在于控制器不知道有关url和http方法名称的任何信息。那我该怎么办?
换句话说,我想测试的是then
被调用并做我需要它做的事。
我想我可能会创建一个单独的服务,可以在then
中调用并执行我需要对模型执行的操作但是如果我想要做的就是设置一个,那会感觉有点过分字段为null,具体取决于简单条件。
答案 0 :(得分:0)
你是对的,你不应该使用echo
,除非你在你正在测试的控制器中使用$httpBackend
。
正如您所写,控制器不需要了解$http
的实现。控制器知道的是Service
有一个Service
方法,它返回一个具有get
属性的对象,这是一个承诺。
您要做的是在测试中使用$promise
的假实现。根据您的使用案例以及您正在使用的测试框架,有多种方法可以通过模拟,间谍,存根等方式执行此操作。
一种方法是创建一个虚假的实现:
Service
您希望能够从测试中访问var Service = {
get: function() {
deferred = $q.defer();
return {
$promise: deferred.promise
};
}
};
,因此您可以根据要测试的内容deferred
或resolve
承诺。
完整设置:
reject
控制器实施:
var $rootScope,
scope,
createController,
$q,
deferred;
var Service = {
get: function() {
deferred = $q.defer();
return {
$promise: deferred.promise
};
}
};
beforeEach(function() {
module('App');
inject(function(_$rootScope_, $controller, _$q_) {
$rootScope = _$rootScope_;
scope = $rootScope.$new();
createController = function() {
$controller('MyController', {
$scope: scope,
Service: Service
});
};
$q = _$q_;
});
});
然后,您可以监视虚假实现,断言它被正确调用。
Jasmine示例:
app.controller('MyController', function($scope, Service) {
$scope.property = false;
$scope.model = Service.get();
$scope.model.$promise.then(function(data) {
if (data) {
$scope.property = true;
}
});
});
您需要spyOn(Service, 'get').and.callThrough();
,否则通话将被中断,您的虚假实施将不会被使用。
您现在可以通过手动创建控制器,解析承诺和触发摘要循环来完全控制,并可以测试不同的状态:
and.callThrough()