正确测试附加于$ scope的承诺

时间:2013-01-16 00:38:42

标签: tdd angularjs jasmine

根据angular.js来源:

  

$ q promises被模板化引擎识别为angular,这意味着在模板中,您可以将附加到范围的promise视为结果值。

所以,我有一个控制器,它从后端获取一个类别列表,

function myController($scope, $categoryService) {
...
  $scope.categoriesList = categoryService.search().then(function(response) {
    return response;
  }
...
}

在我的模板中,我有一个选择:

<select multiple ng-model="categories" ng-options="category.name for category in categoriesList"></select>

在浏览器中“有效”(选择显示填充的列表)

但是你是如何测试的?

我有以下规格:

it('populates the categoriesList from the categoryService', inject(function(categoryService, $q, $controller, $rootScope) {
  var $scope = $rootScope.$new();
  var catList = [{id:1, name:"Animal"},{id:2, name:"Vegetable"}];
  var deferred = $q.defer()
  spyOn(categoryService, 'search').andReturn(deferred.promise);

  $controller(myController, {'$scope': $scope});
  expect(categoryService.search).toHaveBeenCalled(); // PASSES

  deferred.resolve(catList);
  $scope.$digest();

  expect($scope.categoriesList).toEqual(catList); // FAILS, returns the promise instead
}));

如果我重写了我的初始化程序

...then(function(response) {
  $scope.categoriesList = response;
}

我的测试将通过,但后来我没有将范围分配给范围,模板引擎也没有解决对我的承诺。在我看来,第一个实现是框架的意图,但它是不可测试的。第二个实现是可测试的,但不是将数据附加到范围的预期方法。

2 个答案:

答案 0 :(得分:1)

当你说

$scope.categoriesList = categoryService.search().then(function(response) {
  return response;
}

$scope.categoriesList未分配response;相反,它会被分配一个新的承诺,解析response(如您的测试所示)。由于最初的承诺已经解决为response,您可以拥有:

$scope.categoriesList = categoryService.search()

文档的含义是,您可以将$scope.categoriesList分配给此类承诺,并且该视图将表达式categoriesList视为其解析为的值(在在这种情况下,response) - 它实际上并不接受该值并将其分配给范围。

[更新]

如果您正在测试控制器,而不是类别服务本身,我会完全保留承诺 - 可能是这样的:

it('populates the categoriesList from the categoryService', inject(function(categoryService, $controller, $rootScope) {
  var $scope = $rootScope.$new();
  var catList = [{id:1, name:"Animal"},{id:2, name:"Vegetable"}];
  spyOn(categoryService, 'search').andReturn(catList);

  $controller(myController, {'$scope': $scope});
  expect(categoryService.search).toHaveBeenCalled();

  $scope.$digest();

  expect($scope.categoriesList).toEqual(catList);
}));

答案 1 :(得分:1)

我遇到了同样的问题,最后在我的测试中这样做了:

it("leaves a 'countries' promise that resolves to countries in the scope", function() {
  var value = null;
  scope.countries.then(function(v) {
    value = v;
  });
  scope.$apply();
  expect(value).toEqual([{ Code: "SE", Name: "Sweden" }]);
});

国家而不是类别,但同样的技术应该适用于您的情况。

(我省略了创建范围变量的beforeEach并使用$ httpBackend设置了GET期望,但当然需要它们。)